Skip to content

Artifact import / export (backend)

Core artifact types register async import and export handlers in the backend.artifactTypes registry. Extensions use the same registry and shared conversion context (puppeteer, headlessUniver).

Registry

Registry IDTargetPayload
backend.artifactTypesbackendArtifactTypeBackend

Keys must match app.artifactTypes / ai.artifactTypes (e.g. document, table, bpmn).

Contract

ts
import type { ArtifactTypeBackend } from '@artiroute/extension-sdk';

const myType: ArtifactTypeBackend = {
  name: 'notes-doc',
  formats: [
    {
      id: 'txt',
      mimeType: 'text/plain',
      extension: '.txt',
      direction: 'both',
    },
  ],
  async exportArtifact(format, content, ctx) {
    const text = (content as { text?: string })?.text ?? '';
    return {
      buffer: new TextEncoder().encode(text),
      fileName: 'notes.txt',
      mimeType: 'text/plain',
    };
  },
  async importArtifact(format, file, ctx, currentContent) {
    const text = new TextDecoder().decode(file.buffer);
    return { content: { ...(currentContent as object), text } };
  },
};

ArtifactIOContext

FieldDescription
keystoneKeystone DB context
templateTemplate id, artifactType, definition
artifactCurrent artifact id + content
puppeteerPDF rendering (renderPdf)
headlessUniverTable snapshot normalization (optional)
logLogger

Extension setup

ts
import { defineExtension, getExtensionRegistry } from '@artiroute/extension-sdk/extension';

export default defineExtension(() => {
  getExtensionRegistry('backend.artifactTypes').register('notes-doc', myType);
});

Manifest:

json
{
  "contributes": {
    "ext": {
      "extendsRegistries": [
        { "registryId": "backend.artifactTypes", "keys": ["notes-doc"] }
      ]
    }
  }
}

HTTP API (host)

MethodPathDescription
GET/api/artifact-types/:name/formatsSupported formats
POST/api/artifacts/:id/exportBody { format } → file download
POST/api/artifacts/:id/importMultipart file + format{ content, warnings? }

Import does not auto-save; the editor applies content and the user clicks Save.

Core formats

TypeImportExport
bpmnxml, bpmnxml
documentdocx, htmldocx, html, pdf
tablexlsx, xls, csv, tsvxlsx, csv, tsv

Table

  • Import uses @mertdeveci55/univer-import-export (LuckyExcel) for XLSX/XLS/CSV/TSV → Univer snapshot with formatting preservation. In Node, uploads are passed as Uint8Array with a name field (browser File is not used).
  • Export uses the same LuckyExcel package for cells and styles. Floating images from SHEET_DRAWING_PLUGIN are embedded separately via ExcelJS (LuckyExcel does not export drawings). URL-based images (editor uploads under /uploads/...) are read from disk on the server. CSV export normalizes LuckyExcel object/data-URI payloads.

Document

Document content is stored as { html, rawHtml?, page }. html is TipTap/Umo source markup; rawHtml is a rendered snapshot from getVanillaHTML() (ECharts as PNG, live DOM) saved on each editor save.

  • Export uses rawHtml when present, otherwise html. Page settings come from content.page. No client-side preparation is required — call POST /api/artifacts/:id/export with { format } only. For documents, save before export so rawHtml is up to date.
  • Media (image/video/audio) inserted in Umo are stored inline as data: URLs in html/rawHtml (not uploaded as project files).
  • Import DOCX uses mammoth with embedded images as base64. Native Word diagrams/SmartArt cannot be fully preserved; the API may return warnings.
  • Import HTML accepts text/html,.html,.htm in the file picker.

PDF (Puppeteer)

PDF export requires Puppeteer (PUPPETEER_DISABLED must not be 1).

Install Chrome for local dev:

bash
cd apps/backend
npx puppeteer browsers install chrome

apps/backend runs postinstall to install Chrome automatically unless PUPPETEER_SKIP_DOWNLOAD=1 or PUPPETEER_DISABLED=1.

For production/Docker, set PUPPETEER_EXECUTABLE_PATH to a system Chromium binary.

Exported PDFs use waitUntil: 'load' so inline data: images render without waiting for external network idle.

AI workspace для командных материалов и знаний