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 ID | Target | Payload |
|---|---|---|
backend.artifactTypes | backend | ArtifactTypeBackend |
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
| Field | Description |
|---|---|
keystone | Keystone DB context |
template | Template id, artifactType, definition |
artifact | Current artifact id + content |
puppeteer | PDF rendering (renderPdf) |
headlessUniver | Table snapshot normalization (optional) |
log | Logger |
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)
| Method | Path | Description |
|---|---|---|
GET | /api/artifact-types/:name/formats | Supported formats |
POST | /api/artifacts/:id/export | Body { format } → file download |
POST | /api/artifacts/:id/import | Multipart file + format → { content, warnings? } |
Import does not auto-save; the editor applies content and the user clicks Save.
Core formats
| Type | Import | Export |
|---|---|---|
bpmn | xml, bpmn | xml |
document | docx, html | docx, html, pdf |
table | xlsx, xls, csv, tsv | xlsx, 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 asUint8Arraywith anamefield (browserFileis not used). - Export uses the same LuckyExcel package for cells and styles. Floating images from
SHEET_DRAWING_PLUGINare 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
rawHtmlwhen present, otherwisehtml. Page settings come fromcontent.page. No client-side preparation is required — callPOST /api/artifacts/:id/exportwith{ format }only. For documents, save before export sorawHtmlis up to date. - Media (image/video/audio) inserted in Umo are stored inline as
data:URLs inhtml/rawHtml(not uploaded as project files). - Import DOCX uses
mammothwith embedded images as base64. Native Word diagrams/SmartArt cannot be fully preserved; the API may returnwarnings. - Import HTML accepts
text/html,.html,.htmin 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 chromeapps/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.