@bendyline/squisq-formats 1.3.1 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -43
- package/dist/{chunk-33YRFXZZ.js → chunk-46FMDOWZ.js} +260 -14
- package/dist/chunk-46FMDOWZ.js.map +1 -0
- package/dist/{chunk-ERZ627GR.js → chunk-5LSSWZPU.js} +12 -27
- package/dist/chunk-5LSSWZPU.js.map +1 -0
- package/dist/chunk-6M7Z25LA.js +46 -0
- package/dist/chunk-6M7Z25LA.js.map +1 -0
- package/dist/{chunk-S3Y7H2BK.js → chunk-D7IFPWNZ.js} +95 -50
- package/dist/chunk-D7IFPWNZ.js.map +1 -0
- package/dist/chunk-DQAZR57U.js +33 -0
- package/dist/chunk-DQAZR57U.js.map +1 -0
- package/dist/chunk-EHLGMSTE.js +273 -0
- package/dist/chunk-EHLGMSTE.js.map +1 -0
- package/dist/{chunk-A3FHLTY5.js → chunk-MJGRI6XR.js} +6 -19
- package/dist/chunk-MJGRI6XR.js.map +1 -0
- package/dist/chunk-PN52A5AA.js +32 -0
- package/dist/chunk-PN52A5AA.js.map +1 -0
- package/dist/chunk-RFAPOKHJ.js +123 -0
- package/dist/chunk-RFAPOKHJ.js.map +1 -0
- package/dist/chunk-RTS5XBZ7.js +526 -0
- package/dist/chunk-RTS5XBZ7.js.map +1 -0
- package/dist/{chunk-7DEUGIOO.js → chunk-SHLPSADL.js} +120 -2
- package/dist/chunk-SHLPSADL.js.map +1 -0
- package/dist/chunk-SSUPBUF5.js +15 -0
- package/dist/chunk-SSUPBUF5.js.map +1 -0
- package/dist/{chunk-UDS45KUJ.js → chunk-WWBIKRNP.js} +40 -80
- package/dist/chunk-WWBIKRNP.js.map +1 -0
- package/dist/{chunk-VN2KEOYB.js → chunk-YESS7VY3.js} +379 -87
- package/dist/chunk-YESS7VY3.js.map +1 -0
- package/dist/csv/index.d.ts +50 -0
- package/dist/csv/index.js +13 -0
- package/dist/csv/index.js.map +1 -0
- package/dist/docx/index.js +6 -3
- package/dist/epub/index.js +4 -2
- package/dist/html/index.d.ts +42 -2
- package/dist/html/index.js +9 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +47 -14
- package/dist/ooxml/index.d.ts +5 -1
- package/dist/ooxml/index.js +13 -9
- package/dist/pdf/index.js +2 -1
- package/dist/pptx/index.d.ts +36 -19
- package/dist/pptx/index.js +9 -2
- package/dist/registry/index.d.ts +167 -0
- package/dist/registry/index.js +17 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/xlsx/index.d.ts +60 -29
- package/dist/xlsx/index.js +4 -1
- package/package.json +12 -2
- package/src/__tests__/convert.test.ts +186 -0
- package/src/__tests__/csvImport.test.ts +84 -0
- package/src/__tests__/exportThemeReconciliation.test.ts +87 -0
- package/src/__tests__/formatRegistry.test.ts +174 -0
- package/src/__tests__/htmlImport.test.ts +57 -0
- package/src/__tests__/lossyWarnings.test.ts +105 -0
- package/src/__tests__/pdfImport.test.ts +95 -1
- package/src/__tests__/plainHtml.test.ts +31 -0
- package/src/__tests__/pptxExport.test.ts +138 -0
- package/src/__tests__/pptxImport.test.ts +115 -0
- package/src/__tests__/roundTripAssets.test.ts +50 -0
- package/src/__tests__/roundTripMatrix.fixtures.ts +86 -0
- package/src/__tests__/roundTripMatrix.helpers.ts +154 -0
- package/src/__tests__/roundTripMatrix.test.ts +142 -0
- package/src/__tests__/sharedContainer.test.ts +41 -0
- package/src/__tests__/sharedImages.test.ts +61 -0
- package/src/__tests__/xlsxExport.test.ts +164 -0
- package/src/__tests__/xlsxImport.test.ts +80 -0
- package/src/csv/index.ts +188 -0
- package/src/docx/export.ts +27 -44
- package/src/docx/import.ts +7 -39
- package/src/epub/export.ts +10 -27
- package/src/html/imageUtils.ts +5 -19
- package/src/html/import.ts +297 -0
- package/src/html/index.ts +4 -0
- package/src/html/plainHtml.ts +66 -16
- package/src/index.ts +36 -5
- package/src/ooxml/index.ts +3 -0
- package/src/ooxml/namespaces.ts +13 -0
- package/src/pdf/export.ts +10 -5
- package/src/pdf/import.ts +146 -51
- package/src/pptx/export.ts +225 -85
- package/src/pptx/import.ts +273 -0
- package/src/pptx/index.ts +10 -28
- package/src/registry/convert.ts +318 -0
- package/src/registry/defaultFormats.ts +323 -0
- package/src/registry/errors.ts +46 -0
- package/src/registry/index.ts +38 -0
- package/src/registry/registry.ts +48 -0
- package/src/registry/types.ts +107 -0
- package/src/shared/container.ts +28 -0
- package/src/shared/images.ts +44 -0
- package/src/shared/inlineRuns.ts +99 -0
- package/src/shared/text.ts +41 -0
- package/src/xlsx/export.ts +253 -0
- package/src/xlsx/import.ts +159 -0
- package/src/xlsx/index.ts +18 -61
- package/dist/chunk-33YRFXZZ.js.map +0 -1
- package/dist/chunk-67KIJHV2.js +0 -21
- package/dist/chunk-67KIJHV2.js.map +0 -1
- package/dist/chunk-7DEUGIOO.js.map +0 -1
- package/dist/chunk-A3FHLTY5.js.map +0 -1
- package/dist/chunk-ERZ627GR.js.map +0 -1
- package/dist/chunk-S3Y7H2BK.js.map +0 -1
- package/dist/chunk-UDS45KUJ.js.map +0 -1
- package/dist/chunk-VN2KEOYB.js.map +0 -1
- package/dist/chunk-YN5HFCEW.js +0 -120
- package/dist/chunk-YN5HFCEW.js.map +0 -1
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
// src/registry/types.ts
|
|
2
|
+
var BUILTIN_FORMAT_IDS = [
|
|
3
|
+
"md",
|
|
4
|
+
"docx",
|
|
5
|
+
"pdf",
|
|
6
|
+
"pptx",
|
|
7
|
+
"xlsx",
|
|
8
|
+
"csv",
|
|
9
|
+
"html",
|
|
10
|
+
"htmlzip",
|
|
11
|
+
"epub",
|
|
12
|
+
"dbk"
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// src/registry/errors.ts
|
|
16
|
+
var ConversionError = class extends Error {
|
|
17
|
+
constructor(code, message, opts = {}) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "ConversionError";
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.format = opts.format;
|
|
22
|
+
this.hint = opts.hint;
|
|
23
|
+
if (opts.cause !== void 0) {
|
|
24
|
+
this.cause = opts.cause;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/registry/defaultFormats.ts
|
|
30
|
+
var MIME = {
|
|
31
|
+
md: "text/markdown",
|
|
32
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
33
|
+
pdf: "application/pdf",
|
|
34
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
35
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
36
|
+
csv: "text/csv",
|
|
37
|
+
html: "text/html",
|
|
38
|
+
zip: "application/zip",
|
|
39
|
+
epub: "application/epub+zip"
|
|
40
|
+
};
|
|
41
|
+
async function toBytes(data) {
|
|
42
|
+
if (data instanceof Uint8Array) return data;
|
|
43
|
+
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
44
|
+
if (typeof data.arrayBuffer === "function") {
|
|
45
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
46
|
+
}
|
|
47
|
+
const buffer = await new Promise((resolve, reject) => {
|
|
48
|
+
const reader = new FileReader();
|
|
49
|
+
reader.onload = () => resolve(reader.result);
|
|
50
|
+
reader.onerror = () => reject(reader.error);
|
|
51
|
+
reader.readAsArrayBuffer(data);
|
|
52
|
+
});
|
|
53
|
+
return new Uint8Array(buffer);
|
|
54
|
+
}
|
|
55
|
+
function resolveThemeId(input, options) {
|
|
56
|
+
return options.themeId ?? input.doc.themeId;
|
|
57
|
+
}
|
|
58
|
+
async function markdownOf(input) {
|
|
59
|
+
if (input.markdownDoc) return input.markdownDoc;
|
|
60
|
+
const { docToMarkdown } = await import("@bendyline/squisq/doc");
|
|
61
|
+
return docToMarkdown(input.doc);
|
|
62
|
+
}
|
|
63
|
+
async function collectContainerImages(container) {
|
|
64
|
+
const images = /* @__PURE__ */ new Map();
|
|
65
|
+
const files = await container.listFiles();
|
|
66
|
+
for (const file of files) {
|
|
67
|
+
if (!/\.(jpg|jpeg|png|gif|webp|svg|bmp|avif)$/i.test(file.path)) continue;
|
|
68
|
+
const data = await container.readFile(file.path);
|
|
69
|
+
if (!data) continue;
|
|
70
|
+
images.set(file.path, data);
|
|
71
|
+
const slash = file.path.lastIndexOf("/");
|
|
72
|
+
if (slash !== -1) images.set(file.path.slice(slash + 1), data);
|
|
73
|
+
}
|
|
74
|
+
return images;
|
|
75
|
+
}
|
|
76
|
+
async function requirePlayerScript(options, format) {
|
|
77
|
+
const script = await options.resolvePlayerScript?.();
|
|
78
|
+
if (!script) {
|
|
79
|
+
throw new ConversionError("missing-dependency", "HTML export needs a player script.", {
|
|
80
|
+
format,
|
|
81
|
+
hint: "pass resolvePlayerScript, e.g. () => import('@bendyline/squisq-react/standalone-source').then(m => m.PLAYER_BUNDLE)"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return script;
|
|
85
|
+
}
|
|
86
|
+
function ok(bytes, mimeType, warnings = []) {
|
|
87
|
+
return { bytes, mimeType, suggestedFilename: "", warnings };
|
|
88
|
+
}
|
|
89
|
+
function defaultFormats() {
|
|
90
|
+
const md = {
|
|
91
|
+
id: "md",
|
|
92
|
+
label: "Markdown",
|
|
93
|
+
mimeType: MIME.md,
|
|
94
|
+
extensions: [".md", ".markdown"],
|
|
95
|
+
async importDoc(data) {
|
|
96
|
+
const { parseMarkdown } = await import("@bendyline/squisq/markdown");
|
|
97
|
+
return parseMarkdown(new TextDecoder().decode(data));
|
|
98
|
+
},
|
|
99
|
+
async exportDoc(input) {
|
|
100
|
+
const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
101
|
+
const text = stringifyMarkdown(await markdownOf(input));
|
|
102
|
+
return ok(new TextEncoder().encode(text), MIME.md);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const docx = {
|
|
106
|
+
id: "docx",
|
|
107
|
+
label: "Word (DOCX)",
|
|
108
|
+
mimeType: MIME.docx,
|
|
109
|
+
extensions: [".docx"],
|
|
110
|
+
async importContainer(data) {
|
|
111
|
+
const { docxToContainer } = await import("./docx/index.js");
|
|
112
|
+
return docxToContainer(data);
|
|
113
|
+
},
|
|
114
|
+
async importDoc(data) {
|
|
115
|
+
const { docxToMarkdownDoc } = await import("./docx/index.js");
|
|
116
|
+
return docxToMarkdownDoc(data);
|
|
117
|
+
},
|
|
118
|
+
async exportDoc(input, options) {
|
|
119
|
+
const { markdownDocToDocx } = await import("./docx/index.js");
|
|
120
|
+
const buf = await markdownDocToDocx(await markdownOf(input), {
|
|
121
|
+
themeId: resolveThemeId(input, options)
|
|
122
|
+
});
|
|
123
|
+
return ok(await toBytes(buf), MIME.docx);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const pdf = {
|
|
127
|
+
id: "pdf",
|
|
128
|
+
label: "PDF",
|
|
129
|
+
mimeType: MIME.pdf,
|
|
130
|
+
extensions: [".pdf"],
|
|
131
|
+
async importContainer(data) {
|
|
132
|
+
const { pdfToContainer } = await import("./pdf/index.js");
|
|
133
|
+
return pdfToContainer(data);
|
|
134
|
+
},
|
|
135
|
+
async importDoc(data) {
|
|
136
|
+
const { pdfToMarkdownDoc } = await import("./pdf/index.js");
|
|
137
|
+
return pdfToMarkdownDoc(data);
|
|
138
|
+
},
|
|
139
|
+
async exportDoc(input, options) {
|
|
140
|
+
const { markdownDocToPdf } = await import("./pdf/index.js");
|
|
141
|
+
const buf = await markdownDocToPdf(await markdownOf(input), {
|
|
142
|
+
themeId: resolveThemeId(input, options)
|
|
143
|
+
});
|
|
144
|
+
return ok(await toBytes(buf), MIME.pdf);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const pptx = {
|
|
148
|
+
id: "pptx",
|
|
149
|
+
label: "PowerPoint (PPTX)",
|
|
150
|
+
mimeType: MIME.pptx,
|
|
151
|
+
extensions: [".pptx"],
|
|
152
|
+
async importContainer(data) {
|
|
153
|
+
const { pptxToContainer } = await import("./pptx/index.js");
|
|
154
|
+
return pptxToContainer(data);
|
|
155
|
+
},
|
|
156
|
+
async importDoc(data) {
|
|
157
|
+
const { pptxToMarkdownDoc } = await import("./pptx/index.js");
|
|
158
|
+
return pptxToMarkdownDoc(data);
|
|
159
|
+
},
|
|
160
|
+
async exportDoc(input, options) {
|
|
161
|
+
const { markdownDocToPptx } = await import("./pptx/index.js");
|
|
162
|
+
const images = await collectContainerImages(input.container);
|
|
163
|
+
const buf = await markdownDocToPptx(await markdownOf(input), {
|
|
164
|
+
themeId: resolveThemeId(input, options),
|
|
165
|
+
images
|
|
166
|
+
});
|
|
167
|
+
return ok(await toBytes(buf), MIME.pptx);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const xlsx = {
|
|
171
|
+
id: "xlsx",
|
|
172
|
+
label: "Excel (XLSX)",
|
|
173
|
+
mimeType: MIME.xlsx,
|
|
174
|
+
extensions: [".xlsx"],
|
|
175
|
+
async importDoc(data) {
|
|
176
|
+
const { xlsxToMarkdownDoc } = await import("./xlsx/index.js");
|
|
177
|
+
return xlsxToMarkdownDoc(data);
|
|
178
|
+
},
|
|
179
|
+
async exportDoc(input) {
|
|
180
|
+
const { markdownDocToXlsx } = await import("./xlsx/index.js");
|
|
181
|
+
const markdownDoc = await markdownOf(input);
|
|
182
|
+
const omitted = markdownDoc.children.filter(
|
|
183
|
+
(n) => n.type !== "table" && n.type !== "heading"
|
|
184
|
+
).length;
|
|
185
|
+
const warnings = omitted > 0 ? [`XLSX export is tables-only; ${omitted} non-table block(s) were omitted.`] : [];
|
|
186
|
+
const blob = await markdownDocToXlsx(markdownDoc);
|
|
187
|
+
return ok(await toBytes(blob), MIME.xlsx, warnings);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const csv = {
|
|
191
|
+
id: "csv",
|
|
192
|
+
label: "CSV",
|
|
193
|
+
mimeType: MIME.csv,
|
|
194
|
+
extensions: [".csv"],
|
|
195
|
+
async importDoc(data) {
|
|
196
|
+
const { csvToMarkdownDoc } = await import("./csv/index.js");
|
|
197
|
+
return csvToMarkdownDoc(data);
|
|
198
|
+
},
|
|
199
|
+
async exportDoc(input) {
|
|
200
|
+
const { markdownDocToCsv } = await import("./csv/index.js");
|
|
201
|
+
const markdownDoc = await markdownOf(input);
|
|
202
|
+
const tableCount = markdownDoc.children.filter((n) => n.type === "table").length;
|
|
203
|
+
const warnings = [];
|
|
204
|
+
if (tableCount > 1) {
|
|
205
|
+
warnings.push(
|
|
206
|
+
`Document has ${tableCount} tables; CSV export emitted only the first. Use the csv converter's tableIndex option to select another.`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
const text = markdownDocToCsv(markdownDoc);
|
|
210
|
+
return ok(new TextEncoder().encode(text), MIME.csv, warnings);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
const html = {
|
|
214
|
+
id: "html",
|
|
215
|
+
label: "HTML (single file)",
|
|
216
|
+
mimeType: MIME.html,
|
|
217
|
+
extensions: [".html", ".htm"],
|
|
218
|
+
async importDoc(data) {
|
|
219
|
+
const { htmlToMarkdownDoc } = await import("./html/index.js");
|
|
220
|
+
return htmlToMarkdownDoc(data);
|
|
221
|
+
},
|
|
222
|
+
// importContainer omitted: HTML import already inlines images as data URIs,
|
|
223
|
+
// so there is nothing to extract into a container this wave.
|
|
224
|
+
async exportDoc(input, options) {
|
|
225
|
+
const playerScript = await requirePlayerScript(options, "html");
|
|
226
|
+
const { docToHtml } = await import("./html/index.js");
|
|
227
|
+
const images = await collectContainerImages(input.container);
|
|
228
|
+
const htmlText = docToHtml(input.doc, {
|
|
229
|
+
playerScript,
|
|
230
|
+
images,
|
|
231
|
+
title: options.title ?? input.baseName,
|
|
232
|
+
mode: "static",
|
|
233
|
+
themeId: resolveThemeId(input, options)
|
|
234
|
+
});
|
|
235
|
+
return ok(new TextEncoder().encode(htmlText), MIME.html);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const htmlzip = {
|
|
239
|
+
id: "htmlzip",
|
|
240
|
+
label: "HTML (ZIP archive)",
|
|
241
|
+
mimeType: MIME.zip,
|
|
242
|
+
extensions: [".html.zip"],
|
|
243
|
+
async exportDoc(input, options) {
|
|
244
|
+
const playerScript = await requirePlayerScript(options, "htmlzip");
|
|
245
|
+
const { docToHtmlZip } = await import("./html/index.js");
|
|
246
|
+
const images = await collectContainerImages(input.container);
|
|
247
|
+
const blob = await docToHtmlZip(input.doc, {
|
|
248
|
+
playerScript,
|
|
249
|
+
images,
|
|
250
|
+
title: options.title ?? input.baseName,
|
|
251
|
+
mode: "static",
|
|
252
|
+
themeId: resolveThemeId(input, options)
|
|
253
|
+
});
|
|
254
|
+
return ok(await toBytes(blob), MIME.zip);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const epub = {
|
|
258
|
+
id: "epub",
|
|
259
|
+
label: "EPUB",
|
|
260
|
+
mimeType: MIME.epub,
|
|
261
|
+
extensions: [".epub"],
|
|
262
|
+
async exportDoc(input, options) {
|
|
263
|
+
const { markdownDocToEpub } = await import("./epub/index.js");
|
|
264
|
+
const images = await collectContainerImages(input.container);
|
|
265
|
+
const buf = await markdownDocToEpub(await markdownOf(input), {
|
|
266
|
+
title: options.title ?? input.baseName,
|
|
267
|
+
themeId: resolveThemeId(input, options),
|
|
268
|
+
images
|
|
269
|
+
});
|
|
270
|
+
return ok(await toBytes(buf), MIME.epub);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const dbk = {
|
|
274
|
+
id: "dbk",
|
|
275
|
+
label: "Squisq container (DBK)",
|
|
276
|
+
mimeType: MIME.zip,
|
|
277
|
+
extensions: [".dbk", ".zip"],
|
|
278
|
+
async importContainer(data) {
|
|
279
|
+
const { zipToContainer } = await import("./container/index.js");
|
|
280
|
+
return zipToContainer(data);
|
|
281
|
+
},
|
|
282
|
+
async exportDoc(input) {
|
|
283
|
+
const { containerToZip } = await import("./container/index.js");
|
|
284
|
+
const blob = await containerToZip(input.container);
|
|
285
|
+
return ok(await toBytes(blob), MIME.zip);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
return [md, docx, pdf, pptx, xlsx, csv, html, htmlzip, epub, dbk];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/registry/registry.ts
|
|
292
|
+
function normalizeExt(ext) {
|
|
293
|
+
return ext.replace(/^\.+/, "").toLowerCase();
|
|
294
|
+
}
|
|
295
|
+
function createRegistry() {
|
|
296
|
+
const byId = /* @__PURE__ */ new Map();
|
|
297
|
+
return {
|
|
298
|
+
register(def) {
|
|
299
|
+
byId.set(def.id, def);
|
|
300
|
+
},
|
|
301
|
+
get(id) {
|
|
302
|
+
return byId.get(id);
|
|
303
|
+
},
|
|
304
|
+
byExtension(ext) {
|
|
305
|
+
const target = normalizeExt(ext);
|
|
306
|
+
if (!target) return void 0;
|
|
307
|
+
for (const def of byId.values()) {
|
|
308
|
+
if (def.extensions.some((e) => normalizeExt(e) === target)) return def;
|
|
309
|
+
}
|
|
310
|
+
return void 0;
|
|
311
|
+
},
|
|
312
|
+
list() {
|
|
313
|
+
return [...byId.values()];
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function defaultRegistry() {
|
|
318
|
+
const registry = createRegistry();
|
|
319
|
+
for (const def of defaultFormats()) {
|
|
320
|
+
registry.register(def);
|
|
321
|
+
}
|
|
322
|
+
return registry;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/registry/convert.ts
|
|
326
|
+
function hasPrefix(bytes, prefix) {
|
|
327
|
+
if (bytes.length < prefix.length) return false;
|
|
328
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
329
|
+
if (bytes[i] !== prefix[i]) return false;
|
|
330
|
+
}
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
var ZIP_MAGIC = [80, 75, 3, 4];
|
|
334
|
+
var PDF_MAGIC = [37, 80, 68, 70];
|
|
335
|
+
async function sniffZip(bytes) {
|
|
336
|
+
const JSZip = (await import("jszip")).default;
|
|
337
|
+
const zip = await JSZip.loadAsync(bytes).catch(() => {
|
|
338
|
+
throw new ConversionError("invalid-input", "Input is not a readable ZIP archive.");
|
|
339
|
+
});
|
|
340
|
+
const contentTypes = zip.file("[Content_Types].xml");
|
|
341
|
+
if (!contentTypes) return "dbk";
|
|
342
|
+
const xml = await contentTypes.async("string");
|
|
343
|
+
if (xml.includes("wordprocessingml")) return "docx";
|
|
344
|
+
if (xml.includes("presentationml")) return "pptx";
|
|
345
|
+
if (xml.includes("spreadsheetml")) return "xlsx";
|
|
346
|
+
return "dbk";
|
|
347
|
+
}
|
|
348
|
+
async function detectByteFormat(bytes, filename, options, registry) {
|
|
349
|
+
if (options.from) return options.from;
|
|
350
|
+
const ext = filename ? extractExt(filename) : void 0;
|
|
351
|
+
if (ext) {
|
|
352
|
+
const byExt = registry.byExtension(ext);
|
|
353
|
+
if (byExt) return byExt.id;
|
|
354
|
+
}
|
|
355
|
+
if (hasPrefix(bytes, PDF_MAGIC)) return "pdf";
|
|
356
|
+
if (hasPrefix(bytes, ZIP_MAGIC)) return sniffZip(bytes);
|
|
357
|
+
return "md";
|
|
358
|
+
}
|
|
359
|
+
function extractExt(filename) {
|
|
360
|
+
const base = filename.split("/").pop() ?? filename;
|
|
361
|
+
const dot = base.lastIndexOf(".");
|
|
362
|
+
if (dot <= 0) return "";
|
|
363
|
+
return base.slice(dot + 1).toLowerCase();
|
|
364
|
+
}
|
|
365
|
+
function baseNameOf(filename) {
|
|
366
|
+
const base = filename.split("/").pop() ?? filename;
|
|
367
|
+
const dot = base.lastIndexOf(".");
|
|
368
|
+
return dot > 0 ? base.slice(0, dot) : base;
|
|
369
|
+
}
|
|
370
|
+
async function normalizeBytes(source, options, registry) {
|
|
371
|
+
const bytes = source.data instanceof Uint8Array ? source.data : new Uint8Array(source.data);
|
|
372
|
+
const buffer = bytes.buffer.slice(
|
|
373
|
+
bytes.byteOffset,
|
|
374
|
+
bytes.byteOffset + bytes.byteLength
|
|
375
|
+
);
|
|
376
|
+
const fromId = await detectByteFormat(bytes, source.filename, options, registry);
|
|
377
|
+
const fromDef = registry.get(fromId);
|
|
378
|
+
if (!fromDef) {
|
|
379
|
+
throw new ConversionError("unknown-format", `Unknown source format "${fromId}".`, {
|
|
380
|
+
format: fromId
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
if (!fromDef.importContainer && !fromDef.importDoc) {
|
|
384
|
+
throw new ConversionError(
|
|
385
|
+
"unsupported-input",
|
|
386
|
+
`Format "${fromDef.label}" cannot be used as a conversion source.`,
|
|
387
|
+
{
|
|
388
|
+
format: fromId,
|
|
389
|
+
hint: "This format is export-only."
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
const { markdownToDoc } = await import("@bendyline/squisq/doc");
|
|
394
|
+
const { parseMarkdown, stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
395
|
+
const warnings = [];
|
|
396
|
+
let markdownDoc;
|
|
397
|
+
let container;
|
|
398
|
+
if (fromDef.importContainer) {
|
|
399
|
+
container = await fromDef.importContainer(buffer, options);
|
|
400
|
+
if (fromDef.id === "pdf" && typeof document === "undefined") {
|
|
401
|
+
warnings.push(
|
|
402
|
+
"PDF embedded images were skipped \u2014 image decoding requires a browser canvas (running under Node)."
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const text = await container.readDocument();
|
|
406
|
+
markdownDoc = text ? parseMarkdown(text) : { type: "document", children: [] };
|
|
407
|
+
} else {
|
|
408
|
+
markdownDoc = await fromDef.importDoc(buffer, options);
|
|
409
|
+
const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
|
|
410
|
+
const mem = new MemoryContentContainer();
|
|
411
|
+
await mem.writeDocument(stringifyMarkdown(markdownDoc));
|
|
412
|
+
container = mem;
|
|
413
|
+
}
|
|
414
|
+
const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });
|
|
415
|
+
const baseName = source.filename ? baseNameOf(source.filename) : "document";
|
|
416
|
+
return {
|
|
417
|
+
input: { doc, markdownDoc, container, baseName },
|
|
418
|
+
warnings
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
async function normalizeMarkdown(source, options) {
|
|
422
|
+
const { markdownToDoc } = await import("@bendyline/squisq/doc");
|
|
423
|
+
const { parseMarkdown } = await import("@bendyline/squisq/markdown");
|
|
424
|
+
const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
425
|
+
const markdownDoc = typeof source.markdown === "string" ? parseMarkdown(source.markdown) : source.markdown;
|
|
426
|
+
let container = source.container;
|
|
427
|
+
if (!container) {
|
|
428
|
+
const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
|
|
429
|
+
const mem = new MemoryContentContainer();
|
|
430
|
+
await mem.writeDocument(stringifyMarkdown(markdownDoc));
|
|
431
|
+
container = mem;
|
|
432
|
+
}
|
|
433
|
+
const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });
|
|
434
|
+
const baseName = source.baseName ?? "document";
|
|
435
|
+
return { input: { doc, markdownDoc, container, baseName }, warnings: [] };
|
|
436
|
+
}
|
|
437
|
+
async function normalizeDoc(source) {
|
|
438
|
+
let container = source.container;
|
|
439
|
+
if (!container) {
|
|
440
|
+
const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
|
|
441
|
+
const { docToMarkdown } = await import("@bendyline/squisq/doc");
|
|
442
|
+
const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
443
|
+
const mem = new MemoryContentContainer();
|
|
444
|
+
await mem.writeDocument(stringifyMarkdown(docToMarkdown(source.doc)));
|
|
445
|
+
container = mem;
|
|
446
|
+
}
|
|
447
|
+
const baseName = source.baseName ?? "document";
|
|
448
|
+
return { input: { doc: source.doc, container, baseName }, warnings: [] };
|
|
449
|
+
}
|
|
450
|
+
async function normalize(source, options, registry) {
|
|
451
|
+
switch (source.kind) {
|
|
452
|
+
case "bytes":
|
|
453
|
+
return normalizeBytes(source, options, registry);
|
|
454
|
+
case "markdown":
|
|
455
|
+
return normalizeMarkdown(source, options);
|
|
456
|
+
case "doc":
|
|
457
|
+
return normalizeDoc(source);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async function applyTransformStyle(input, transformStyle, themeId) {
|
|
461
|
+
const { applyTransform, extractDocImages } = await import("@bendyline/squisq/transform");
|
|
462
|
+
const { docToMarkdown, markdownToDoc } = await import("@bendyline/squisq/doc");
|
|
463
|
+
const images = extractDocImages(input.doc.blocks);
|
|
464
|
+
const result = applyTransform(input.doc, transformStyle, { themeId, images });
|
|
465
|
+
input.doc = result.doc;
|
|
466
|
+
const markdownDoc = docToMarkdown(result.doc);
|
|
467
|
+
input.markdownDoc = markdownDoc;
|
|
468
|
+
const roundTripped = markdownToDoc(markdownDoc, { autoTemplates: false });
|
|
469
|
+
if (roundTripped.blocks.length < result.doc.blocks.length) {
|
|
470
|
+
return [
|
|
471
|
+
"Transform produced blocks that don\u2019t round-trip to markdown; export may be incomplete."
|
|
472
|
+
];
|
|
473
|
+
}
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
async function convert(source, to, options = {}) {
|
|
477
|
+
const registry = options.registry ?? defaultRegistry();
|
|
478
|
+
const target = registry.get(to);
|
|
479
|
+
if (!target) {
|
|
480
|
+
throw new ConversionError("unknown-format", `Unknown target format "${to}".`, { format: to });
|
|
481
|
+
}
|
|
482
|
+
if (!target.exportDoc) {
|
|
483
|
+
throw new ConversionError(
|
|
484
|
+
"unsupported-output",
|
|
485
|
+
`Format "${target.label}" does not support export.`,
|
|
486
|
+
{ format: to, hint: "This format is import-only." }
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
const { input, warnings } = await normalize(source, options, registry);
|
|
490
|
+
if (options.themeId && !input.doc.themeId) {
|
|
491
|
+
input.doc.themeId = options.themeId;
|
|
492
|
+
}
|
|
493
|
+
if (options.transformStyle) {
|
|
494
|
+
warnings.push(
|
|
495
|
+
...await applyTransformStyle(
|
|
496
|
+
input,
|
|
497
|
+
options.transformStyle,
|
|
498
|
+
options.themeId ?? input.doc.themeId
|
|
499
|
+
)
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
let result;
|
|
503
|
+
try {
|
|
504
|
+
result = await target.exportDoc(input, options);
|
|
505
|
+
} catch (err) {
|
|
506
|
+
if (err instanceof ConversionError) throw err;
|
|
507
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
508
|
+
throw new ConversionError("conversion-failed", message, { format: to, cause: err });
|
|
509
|
+
}
|
|
510
|
+
const ext = target.extensions[0]?.replace(/^\.+/, "") ?? to;
|
|
511
|
+
return {
|
|
512
|
+
...result,
|
|
513
|
+
suggestedFilename: `${input.baseName}.${ext}`,
|
|
514
|
+
warnings: [...warnings, ...result.warnings]
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export {
|
|
519
|
+
BUILTIN_FORMAT_IDS,
|
|
520
|
+
ConversionError,
|
|
521
|
+
defaultFormats,
|
|
522
|
+
createRegistry,
|
|
523
|
+
defaultRegistry,
|
|
524
|
+
convert
|
|
525
|
+
};
|
|
526
|
+
//# sourceMappingURL=chunk-RTS5XBZ7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry/types.ts","../src/registry/errors.ts","../src/registry/defaultFormats.ts","../src/registry/registry.ts","../src/registry/convert.ts"],"sourcesContent":["/**\n * Format registry — shared types.\n *\n * The registry is the programmatic heart of `convert()`: it maps a small set of\n * format ids (`md`, `docx`, `pdf`, …) to `FormatDefinition`s, each of which\n * knows how to import to / export from squisq's markdown + Doc model. Every\n * converter module is loaded lazily via `import()` inside the definition\n * methods, so pulling in the registry never eagerly bundles heavy converters.\n */\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport type { MarkdownDocument } from '@bendyline/squisq/markdown';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\n\n/** A format identifier (e.g. `'docx'`). Strings so hosts can register their own. */\nexport type FormatId = string;\n\n/** The built-in formats the default registry ships with. */\nexport const BUILTIN_FORMAT_IDS = [\n 'md',\n 'docx',\n 'pdf',\n 'pptx',\n 'xlsx',\n 'csv',\n 'html',\n 'htmlzip',\n 'epub',\n 'dbk',\n] as const;\n\n/** The bytes + metadata produced by a successful export. */\nexport interface ConversionResult {\n /** Encoded output bytes. */\n bytes: Uint8Array;\n /** MIME type of the output. */\n mimeType: string;\n /** A sensible download filename (`<baseName>.<ext>`). */\n suggestedFilename: string;\n /** Non-fatal notes accumulated during conversion (may be empty). */\n warnings: string[];\n}\n\n/**\n * A source normalized into every shape an exporter might need. `doc` is always\n * present; `markdownDoc` is present when the source was markdown-shaped (an\n * exporter that wants markdown but finds none derives it from `doc`).\n */\nexport interface NormalizedInput {\n doc: Doc;\n markdownDoc?: MarkdownDocument;\n container: ContentContainer;\n baseName: string;\n}\n\n/** Options threaded through `convert()` and into every format method. */\nexport interface ConvertOptions {\n /** Registry to resolve formats against. Defaults to `defaultRegistry()`. */\n registry?: FormatRegistry;\n /** Explicit source format id (skips extension/byte sniffing). */\n from?: FormatId;\n /** Theme id to apply to the exported document. */\n themeId?: string;\n /** Transform style id to apply before export. */\n transformStyle?: string;\n /** Content-aware auto-templating when deriving a Doc from markdown. */\n autoTemplates?: boolean;\n /** Title hint for exporters that support one (epub, html). */\n title?: string;\n /** Lazily resolve the standalone player IIFE bundle (required for HTML export). */\n resolvePlayerScript?: () => Promise<string>;\n /** Per-format escape hatch for extra options. */\n formatOptions?: Record<FormatId, Record<string, unknown>>;\n}\n\n/** Describes how a single format imports to / exports from the squisq model. */\nexport interface FormatDefinition {\n id: FormatId;\n label: string;\n mimeType: string;\n extensions: readonly string[];\n /** Import raw bytes to a MarkdownDocument. */\n importDoc?(data: ArrayBuffer, options: ConvertOptions): Promise<MarkdownDocument>;\n /** Import raw bytes to a ContentContainer (markdown + extracted media). */\n importContainer?(data: ArrayBuffer, options: ConvertOptions): Promise<ContentContainer>;\n /** Export a normalized input to bytes. */\n exportDoc?(input: NormalizedInput, options: ConvertOptions): Promise<ConversionResult>;\n}\n\n/** A mutable collection of format definitions keyed by id. */\nexport interface FormatRegistry {\n register(def: FormatDefinition): void;\n get(id: FormatId): FormatDefinition | undefined;\n byExtension(ext: string): FormatDefinition | undefined;\n list(): FormatDefinition[];\n}\n\n/** The three shapes `convert()` accepts as a source. */\nexport type ConvertSource =\n | { kind: 'bytes'; data: ArrayBuffer | Uint8Array; filename?: string }\n | {\n kind: 'markdown';\n markdown: string | MarkdownDocument;\n container?: ContentContainer;\n baseName?: string;\n }\n | { kind: 'doc'; doc: Doc; container?: ContentContainer; baseName?: string };\n","/**\n * Structured error for the format registry / `convert()` pipeline.\n *\n * Every failure path in the registry throws a `ConversionError` with a stable\n * machine-readable `code`, so callers can branch on the failure kind rather\n * than string-matching messages.\n */\n\nimport type { FormatId } from './types.js';\n\n/** Machine-readable failure kinds surfaced by the registry / `convert()`. */\nexport type ConversionErrorCode =\n | 'unknown-format'\n | 'unsupported-input'\n | 'unsupported-output'\n | 'invalid-input'\n | 'missing-dependency'\n | 'conversion-failed';\n\n/** Options for constructing a {@link ConversionError}. */\nexport interface ConversionErrorOptions {\n /** The format id the error relates to, if any. */\n format?: FormatId;\n /** A human-oriented remediation hint. */\n hint?: string;\n /** The underlying error, preserved on `error.cause`. */\n cause?: unknown;\n}\n\n/** An error raised by the format registry or `convert()`. */\nexport class ConversionError extends Error {\n readonly code: ConversionErrorCode;\n readonly format?: FormatId;\n readonly hint?: string;\n\n constructor(code: ConversionErrorCode, message: string, opts: ConversionErrorOptions = {}) {\n super(message);\n this.name = 'ConversionError';\n this.code = code;\n this.format = opts.format;\n this.hint = opts.hint;\n if (opts.cause !== undefined) {\n (this as Error & { cause?: unknown }).cause = opts.cause;\n }\n }\n}\n","/**\n * Built-in format definitions.\n *\n * Each definition loads its converter module lazily via `import()` inside the\n * method body, so importing the registry never eagerly bundles a heavy\n * converter (pdf-lib, jszip, the OOXML writers, …). The capability of each\n * format — which of import/export it supports — is expressed purely by which\n * methods are present.\n */\n\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport type { MarkdownDocument } from '@bendyline/squisq/markdown';\nimport { ConversionError } from './errors.js';\nimport type {\n ConversionResult,\n ConvertOptions,\n FormatDefinition,\n NormalizedInput,\n} from './types.js';\n\n// ── MIME constants ──────────────────────────────────────────────────\n\nconst MIME = {\n md: 'text/markdown',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n pdf: 'application/pdf',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n csv: 'text/csv',\n html: 'text/html',\n zip: 'application/zip',\n epub: 'application/epub+zip',\n} as const;\n\n// ── Small shared helpers ────────────────────────────────────────────\n\n/** Normalize any binary export return shape to Uint8Array. */\nasync function toBytes(data: ArrayBuffer | Uint8Array | Blob): Promise<Uint8Array> {\n if (data instanceof Uint8Array) return data;\n if (data instanceof ArrayBuffer) return new Uint8Array(data);\n // Blob. Prefer the modern arrayBuffer() method; fall back to FileReader for\n // environments (e.g. some jsdom builds) whose Blob lacks it.\n if (typeof data.arrayBuffer === 'function') {\n return new Uint8Array(await data.arrayBuffer());\n }\n const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(data);\n });\n return new Uint8Array(buffer);\n}\n\n/** The theme to apply: explicit option wins, then the doc's own theme. */\nfunction resolveThemeId(input: NormalizedInput, options: ConvertOptions): string | undefined {\n return options.themeId ?? input.doc.themeId;\n}\n\n/** The markdown shape an exporter needs — reuse the source's, else derive it. */\nasync function markdownOf(input: NormalizedInput): Promise<MarkdownDocument> {\n if (input.markdownDoc) return input.markdownDoc;\n const { docToMarkdown } = await import('@bendyline/squisq/doc');\n return docToMarkdown(input.doc);\n}\n\n/**\n * Collect image files from a container, keyed by both their container path and\n * their bare filename. Exporters that resolve by url (PPTX) and the standalone\n * player (HTML) both match against one of those two keys.\n */\nasync function collectContainerImages(\n container: ContentContainer,\n): Promise<Map<string, ArrayBuffer>> {\n const images = new Map<string, ArrayBuffer>();\n const files = await container.listFiles();\n for (const file of files) {\n if (!/\\.(jpg|jpeg|png|gif|webp|svg|bmp|avif)$/i.test(file.path)) continue;\n const data = await container.readFile(file.path);\n if (!data) continue;\n images.set(file.path, data);\n const slash = file.path.lastIndexOf('/');\n if (slash !== -1) images.set(file.path.slice(slash + 1), data);\n }\n return images;\n}\n\n/** Resolve the player IIFE bundle or throw a helpful missing-dependency error. */\nasync function requirePlayerScript(options: ConvertOptions, format: string): Promise<string> {\n const script = await options.resolvePlayerScript?.();\n if (!script) {\n throw new ConversionError('missing-dependency', 'HTML export needs a player script.', {\n format,\n hint: \"pass resolvePlayerScript, e.g. () => import('@bendyline/squisq-react/standalone-source').then(m => m.PLAYER_BUNDLE)\",\n });\n }\n return script;\n}\n\nfunction ok(bytes: Uint8Array, mimeType: string, warnings: string[] = []): ConversionResult {\n // suggestedFilename is filled in by convert() once the baseName is known.\n return { bytes, mimeType, suggestedFilename: '', warnings };\n}\n\n// ── Definitions ─────────────────────────────────────────────────────\n\nexport function defaultFormats(): FormatDefinition[] {\n const md: FormatDefinition = {\n id: 'md',\n label: 'Markdown',\n mimeType: MIME.md,\n extensions: ['.md', '.markdown'],\n async importDoc(data): Promise<MarkdownDocument> {\n const { parseMarkdown } = await import('@bendyline/squisq/markdown');\n return parseMarkdown(new TextDecoder().decode(data));\n },\n async exportDoc(input): Promise<ConversionResult> {\n const { stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n const text = stringifyMarkdown(await markdownOf(input));\n return ok(new TextEncoder().encode(text), MIME.md);\n },\n };\n\n const docx: FormatDefinition = {\n id: 'docx',\n label: 'Word (DOCX)',\n mimeType: MIME.docx,\n extensions: ['.docx'],\n async importContainer(data): Promise<ContentContainer> {\n const { docxToContainer } = await import('../docx/index.js');\n return docxToContainer(data);\n },\n async importDoc(data): Promise<MarkdownDocument> {\n const { docxToMarkdownDoc } = await import('../docx/index.js');\n return docxToMarkdownDoc(data);\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToDocx } = await import('../docx/index.js');\n const buf = await markdownDocToDocx(await markdownOf(input), {\n themeId: resolveThemeId(input, options),\n });\n return ok(await toBytes(buf), MIME.docx);\n },\n };\n\n const pdf: FormatDefinition = {\n id: 'pdf',\n label: 'PDF',\n mimeType: MIME.pdf,\n extensions: ['.pdf'],\n async importContainer(data): Promise<ContentContainer> {\n const { pdfToContainer } = await import('../pdf/index.js');\n return pdfToContainer(data);\n },\n async importDoc(data): Promise<MarkdownDocument> {\n const { pdfToMarkdownDoc } = await import('../pdf/index.js');\n return pdfToMarkdownDoc(data);\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToPdf } = await import('../pdf/index.js');\n const buf = await markdownDocToPdf(await markdownOf(input), {\n themeId: resolveThemeId(input, options),\n });\n return ok(await toBytes(buf), MIME.pdf);\n },\n };\n\n const pptx: FormatDefinition = {\n id: 'pptx',\n label: 'PowerPoint (PPTX)',\n mimeType: MIME.pptx,\n extensions: ['.pptx'],\n async importContainer(data): Promise<ContentContainer> {\n const { pptxToContainer } = await import('../pptx/index.js');\n return pptxToContainer(data);\n },\n async importDoc(data): Promise<MarkdownDocument> {\n const { pptxToMarkdownDoc } = await import('../pptx/index.js');\n return pptxToMarkdownDoc(data);\n },\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToPptx } = await import('../pptx/index.js');\n const images = await collectContainerImages(input.container);\n const buf = await markdownDocToPptx(await markdownOf(input), {\n themeId: resolveThemeId(input, options),\n images,\n });\n return ok(await toBytes(buf), MIME.pptx);\n },\n };\n\n const xlsx: FormatDefinition = {\n id: 'xlsx',\n label: 'Excel (XLSX)',\n mimeType: MIME.xlsx,\n extensions: ['.xlsx'],\n async importDoc(data): Promise<MarkdownDocument> {\n const { xlsxToMarkdownDoc } = await import('../xlsx/index.js');\n return xlsxToMarkdownDoc(data);\n },\n async exportDoc(input): Promise<ConversionResult> {\n const { markdownDocToXlsx } = await import('../xlsx/index.js');\n const markdownDoc = await markdownOf(input);\n // XLSX fidelity is tables-only: `table` nodes become worksheets and\n // headings name them, but every other top-level block (prose, lists,\n // images, code, …) is dropped. Count those and warn honestly.\n const omitted = markdownDoc.children.filter(\n (n) => n.type !== 'table' && n.type !== 'heading',\n ).length;\n const warnings =\n omitted > 0\n ? [`XLSX export is tables-only; ${omitted} non-table block(s) were omitted.`]\n : [];\n const blob = await markdownDocToXlsx(markdownDoc);\n return ok(await toBytes(blob), MIME.xlsx, warnings);\n },\n };\n\n const csv: FormatDefinition = {\n id: 'csv',\n label: 'CSV',\n mimeType: MIME.csv,\n extensions: ['.csv'],\n async importDoc(data): Promise<MarkdownDocument> {\n const { csvToMarkdownDoc } = await import('../csv/index.js');\n return csvToMarkdownDoc(data);\n },\n async exportDoc(input): Promise<ConversionResult> {\n const { markdownDocToCsv } = await import('../csv/index.js');\n const markdownDoc = await markdownOf(input);\n const tableCount = markdownDoc.children.filter((n) => n.type === 'table').length;\n const warnings: string[] = [];\n if (tableCount > 1) {\n warnings.push(\n `Document has ${tableCount} tables; CSV export emitted only the first. Use the csv converter's tableIndex option to select another.`,\n );\n }\n const text = markdownDocToCsv(markdownDoc);\n return ok(new TextEncoder().encode(text), MIME.csv, warnings);\n },\n };\n\n const html: FormatDefinition = {\n id: 'html',\n label: 'HTML (single file)',\n mimeType: MIME.html,\n extensions: ['.html', '.htm'],\n async importDoc(data): Promise<MarkdownDocument> {\n const { htmlToMarkdownDoc } = await import('../html/index.js');\n return htmlToMarkdownDoc(data);\n },\n // importContainer omitted: HTML import already inlines images as data URIs,\n // so there is nothing to extract into a container this wave.\n async exportDoc(input, options): Promise<ConversionResult> {\n const playerScript = await requirePlayerScript(options, 'html');\n const { docToHtml } = await import('../html/index.js');\n const images = await collectContainerImages(input.container);\n const htmlText = docToHtml(input.doc, {\n playerScript,\n images,\n title: options.title ?? input.baseName,\n mode: 'static',\n themeId: resolveThemeId(input, options),\n });\n return ok(new TextEncoder().encode(htmlText), MIME.html);\n },\n };\n\n const htmlzip: FormatDefinition = {\n id: 'htmlzip',\n label: 'HTML (ZIP archive)',\n mimeType: MIME.zip,\n extensions: ['.html.zip'],\n async exportDoc(input, options): Promise<ConversionResult> {\n const playerScript = await requirePlayerScript(options, 'htmlzip');\n const { docToHtmlZip } = await import('../html/index.js');\n const images = await collectContainerImages(input.container);\n const blob = await docToHtmlZip(input.doc, {\n playerScript,\n images,\n title: options.title ?? input.baseName,\n mode: 'static',\n themeId: resolveThemeId(input, options),\n });\n return ok(await toBytes(blob), MIME.zip);\n },\n };\n\n const epub: FormatDefinition = {\n id: 'epub',\n label: 'EPUB',\n mimeType: MIME.epub,\n extensions: ['.epub'],\n async exportDoc(input, options): Promise<ConversionResult> {\n const { markdownDocToEpub } = await import('../epub/index.js');\n const images = await collectContainerImages(input.container);\n const buf = await markdownDocToEpub(await markdownOf(input), {\n title: options.title ?? input.baseName,\n themeId: resolveThemeId(input, options),\n images,\n });\n return ok(await toBytes(buf), MIME.epub);\n },\n };\n\n const dbk: FormatDefinition = {\n id: 'dbk',\n label: 'Squisq container (DBK)',\n mimeType: MIME.zip,\n extensions: ['.dbk', '.zip'],\n async importContainer(data): Promise<ContentContainer> {\n const { zipToContainer } = await import('../container/index.js');\n return zipToContainer(data);\n },\n async exportDoc(input): Promise<ConversionResult> {\n const { containerToZip } = await import('../container/index.js');\n const blob = await containerToZip(input.container);\n return ok(await toBytes(blob), MIME.zip);\n },\n };\n\n return [md, docx, pdf, pptx, xlsx, csv, html, htmlzip, epub, dbk];\n}\n","/**\n * Format registry implementation.\n *\n * `createRegistry()` returns an empty, mutable registry (last-write-wins by id);\n * `defaultRegistry()` returns one preloaded with every built-in format.\n */\n\nimport type { FormatDefinition, FormatId, FormatRegistry } from './types.js';\nimport { defaultFormats } from './defaultFormats.js';\n\n/** Normalize an extension to lowercase, without a leading dot. */\nfunction normalizeExt(ext: string): string {\n return ext.replace(/^\\.+/, '').toLowerCase();\n}\n\n/** Create an empty registry. Registering an existing id overwrites it. */\nexport function createRegistry(): FormatRegistry {\n const byId = new Map<FormatId, FormatDefinition>();\n\n return {\n register(def: FormatDefinition): void {\n byId.set(def.id, def);\n },\n get(id: FormatId): FormatDefinition | undefined {\n return byId.get(id);\n },\n byExtension(ext: string): FormatDefinition | undefined {\n const target = normalizeExt(ext);\n if (!target) return undefined;\n for (const def of byId.values()) {\n if (def.extensions.some((e) => normalizeExt(e) === target)) return def;\n }\n return undefined;\n },\n list(): FormatDefinition[] {\n return [...byId.values()];\n },\n };\n}\n\n/** Create a registry preloaded with all built-in formats. */\nexport function defaultRegistry(): FormatRegistry {\n const registry = createRegistry();\n for (const def of defaultFormats()) {\n registry.register(def);\n }\n return registry;\n}\n","/**\n * Programmatic `convert()` — the format-registry front door.\n *\n * Normalizes any {@link ConvertSource} into a {@link NormalizedInput} (always\n * yielding a `Doc`, keeping the `MarkdownDocument` when the source was\n * markdown-shaped, and always carrying a `ContentContainer` for media), applies\n * an optional transform, then hands off to the target format's exporter.\n */\n\nimport type { MarkdownDocument } from '@bendyline/squisq/markdown';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport { ConversionError } from './errors.js';\nimport { defaultRegistry } from './registry.js';\nimport type {\n ConversionResult,\n ConvertOptions,\n ConvertSource,\n FormatDefinition,\n FormatId,\n FormatRegistry,\n NormalizedInput,\n} from './types.js';\n\n// ── Byte sniffing ───────────────────────────────────────────────────\n\nfunction hasPrefix(bytes: Uint8Array, prefix: number[]): boolean {\n if (bytes.length < prefix.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (bytes[i] !== prefix[i]) return false;\n }\n return true;\n}\n\nconst ZIP_MAGIC = [0x50, 0x4b, 0x03, 0x04]; // \"PK\\x03\\x04\"\nconst PDF_MAGIC = [0x25, 0x50, 0x44, 0x46]; // \"%PDF\"\n\n/**\n * Disambiguate a ZIP-based file. OOXML formats (docx/pptx/xlsx) all carry a\n * `[Content_Types].xml` whose content-type strings name the flavor; a squisq\n * container (.dbk) is a plain ZIP with no such part, so absence of the OOXML\n * marker means `dbk`.\n */\nasync function sniffZip(bytes: Uint8Array): Promise<FormatId> {\n const JSZip = (await import('jszip')).default;\n const zip = await JSZip.loadAsync(bytes).catch(() => {\n throw new ConversionError('invalid-input', 'Input is not a readable ZIP archive.');\n });\n const contentTypes = zip.file('[Content_Types].xml');\n if (!contentTypes) return 'dbk';\n const xml = await contentTypes.async('string');\n if (xml.includes('wordprocessingml')) return 'docx';\n if (xml.includes('presentationml')) return 'pptx';\n if (xml.includes('spreadsheetml')) return 'xlsx';\n return 'dbk';\n}\n\n/** Determine the source format for raw bytes. */\nasync function detectByteFormat(\n bytes: Uint8Array,\n filename: string | undefined,\n options: ConvertOptions,\n registry: FormatRegistry,\n): Promise<FormatId> {\n if (options.from) return options.from;\n\n const ext = filename ? extractExt(filename) : undefined;\n if (ext) {\n const byExt = registry.byExtension(ext);\n if (byExt) return byExt.id;\n }\n\n if (hasPrefix(bytes, PDF_MAGIC)) return 'pdf';\n if (hasPrefix(bytes, ZIP_MAGIC)) return sniffZip(bytes);\n\n // No magic and no recognized extension → assume UTF-8 markdown.\n return 'md';\n}\n\n// ── Filename helpers ────────────────────────────────────────────────\n\n/** Lowercased extension without the leading dot (`report.DocX` → `docx`). */\nfunction extractExt(filename: string): string {\n const base = filename.split('/').pop() ?? filename;\n const dot = base.lastIndexOf('.');\n if (dot <= 0) return '';\n return base.slice(dot + 1).toLowerCase();\n}\n\n/** Basename without its final extension (`a/b/report.docx` → `report`). */\nfunction baseNameOf(filename: string): string {\n const base = filename.split('/').pop() ?? filename;\n const dot = base.lastIndexOf('.');\n return dot > 0 ? base.slice(0, dot) : base;\n}\n\n// ── Normalization ───────────────────────────────────────────────────\n\ninterface Normalized {\n input: NormalizedInput;\n warnings: string[];\n}\n\nasync function normalizeBytes(\n source: Extract<ConvertSource, { kind: 'bytes' }>,\n options: ConvertOptions,\n registry: FormatRegistry,\n): Promise<Normalized> {\n const bytes = source.data instanceof Uint8Array ? source.data : new Uint8Array(source.data);\n const buffer = bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n\n const fromId = await detectByteFormat(bytes, source.filename, options, registry);\n const fromDef = registry.get(fromId);\n if (!fromDef) {\n throw new ConversionError('unknown-format', `Unknown source format \"${fromId}\".`, {\n format: fromId,\n });\n }\n if (!fromDef.importContainer && !fromDef.importDoc) {\n throw new ConversionError(\n 'unsupported-input',\n `Format \"${fromDef.label}\" cannot be used as a conversion source.`,\n {\n format: fromId,\n hint: 'This format is export-only.',\n },\n );\n }\n\n const { markdownToDoc } = await import('@bendyline/squisq/doc');\n const { parseMarkdown, stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n\n const warnings: string[] = [];\n let markdownDoc: MarkdownDocument;\n let container: ContentContainer;\n\n if (fromDef.importContainer) {\n container = await fromDef.importContainer(buffer, options);\n // PDF embedded-image extraction needs a browser canvas; under Node it's\n // skipped inside pdfToContainer. importContainer has no warnings channel of\n // its own, so surface that degraded path here where warnings are collected.\n if (fromDef.id === 'pdf' && typeof document === 'undefined') {\n warnings.push(\n 'PDF embedded images were skipped — image decoding requires a browser canvas (running under Node).',\n );\n }\n const text = await container.readDocument();\n markdownDoc = text ? parseMarkdown(text) : { type: 'document', children: [] };\n } else {\n // importDoc is guaranteed present by the guard above.\n markdownDoc = await fromDef.importDoc!(buffer, options);\n const { MemoryContentContainer } = await import('@bendyline/squisq/storage');\n const mem = new MemoryContentContainer();\n await mem.writeDocument(stringifyMarkdown(markdownDoc));\n container = mem;\n }\n\n const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });\n const baseName = source.filename ? baseNameOf(source.filename) : 'document';\n\n return {\n input: { doc, markdownDoc, container, baseName },\n warnings,\n };\n}\n\nasync function normalizeMarkdown(\n source: Extract<ConvertSource, { kind: 'markdown' }>,\n options: ConvertOptions,\n): Promise<Normalized> {\n const { markdownToDoc } = await import('@bendyline/squisq/doc');\n const { parseMarkdown } = await import('@bendyline/squisq/markdown');\n\n const { stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n\n const markdownDoc =\n typeof source.markdown === 'string' ? parseMarkdown(source.markdown) : source.markdown;\n\n let container = source.container;\n if (!container) {\n // Seed a fresh container with the document so container-backed exports\n // (dbk) and media resolution have the markdown to serialize.\n const { MemoryContentContainer } = await import('@bendyline/squisq/storage');\n const mem = new MemoryContentContainer();\n await mem.writeDocument(stringifyMarkdown(markdownDoc));\n container = mem;\n }\n\n const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });\n const baseName = source.baseName ?? 'document';\n\n return { input: { doc, markdownDoc, container, baseName }, warnings: [] };\n}\n\nasync function normalizeDoc(source: Extract<ConvertSource, { kind: 'doc' }>): Promise<Normalized> {\n let container = source.container;\n if (!container) {\n const { MemoryContentContainer } = await import('@bendyline/squisq/storage');\n const { docToMarkdown } = await import('@bendyline/squisq/doc');\n const { stringifyMarkdown } = await import('@bendyline/squisq/markdown');\n const mem = new MemoryContentContainer();\n await mem.writeDocument(stringifyMarkdown(docToMarkdown(source.doc)));\n container = mem;\n }\n const baseName = source.baseName ?? 'document';\n // markdownDoc is intentionally omitted — exporters derive it lazily via docToMarkdown.\n return { input: { doc: source.doc, container, baseName }, warnings: [] };\n}\n\nasync function normalize(\n source: ConvertSource,\n options: ConvertOptions,\n registry: FormatRegistry,\n): Promise<Normalized> {\n switch (source.kind) {\n case 'bytes':\n return normalizeBytes(source, options, registry);\n case 'markdown':\n return normalizeMarkdown(source, options);\n case 'doc':\n return normalizeDoc(source);\n }\n}\n\n// ── Transform ───────────────────────────────────────────────────────\n\nasync function applyTransformStyle(\n input: NormalizedInput,\n transformStyle: string,\n themeId?: string,\n): Promise<string[]> {\n const { applyTransform, extractDocImages } = await import('@bendyline/squisq/transform');\n const { docToMarkdown, markdownToDoc } = await import('@bendyline/squisq/doc');\n const images = extractDocImages(input.doc.blocks);\n const result = applyTransform(input.doc, transformStyle, { themeId, images });\n input.doc = result.doc;\n const markdownDoc = docToMarkdown(result.doc);\n input.markdownDoc = markdownDoc;\n\n // Lossy-path detection: transforms can emit template blocks whose data lives\n // in template inputs rather than in round-trippable markdown `contents`, so\n // `docToMarkdown` silently drops them. Re-parse with auto-templating off (so\n // the comparison stays ~1:1 and isn't confused by content-aware promotion)\n // and compare block counts. Best-effort — most blocks carry `contents` and\n // round-trip fine, so this only fires on a genuine collapse.\n const roundTripped = markdownToDoc(markdownDoc, { autoTemplates: false });\n if (roundTripped.blocks.length < result.doc.blocks.length) {\n return [\n 'Transform produced blocks that don’t round-trip to markdown; export may be incomplete.',\n ];\n }\n return [];\n}\n\n// ── convert() ───────────────────────────────────────────────────────\n\n/**\n * Convert a source document to a target format.\n *\n * @param source - A bytes / markdown / doc source.\n * @param to - The target format id (must be registered and support export).\n * @param options - Conversion options (registry, theme, transform, …).\n * @throws {@link ConversionError} on any failure, with a stable `code`.\n */\nexport async function convert(\n source: ConvertSource,\n to: FormatId,\n options: ConvertOptions = {},\n): Promise<ConversionResult> {\n const registry = options.registry ?? defaultRegistry();\n\n const target: FormatDefinition | undefined = registry.get(to);\n if (!target) {\n throw new ConversionError('unknown-format', `Unknown target format \"${to}\".`, { format: to });\n }\n if (!target.exportDoc) {\n throw new ConversionError(\n 'unsupported-output',\n `Format \"${target.label}\" does not support export.`,\n { format: to, hint: 'This format is import-only.' },\n );\n }\n\n const { input, warnings } = await normalize(source, options, registry);\n\n // Respect an existing doc theme; only fill it in from options when absent.\n if (options.themeId && !input.doc.themeId) {\n input.doc.themeId = options.themeId;\n }\n\n if (options.transformStyle) {\n warnings.push(\n ...(await applyTransformStyle(\n input,\n options.transformStyle,\n options.themeId ?? input.doc.themeId,\n )),\n );\n }\n\n let result: ConversionResult;\n try {\n result = await target.exportDoc(input, options);\n } catch (err: unknown) {\n if (err instanceof ConversionError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new ConversionError('conversion-failed', message, { format: to, cause: err });\n }\n\n const ext = target.extensions[0]?.replace(/^\\.+/, '') ?? to;\n return {\n ...result,\n suggestedFilename: `${input.baseName}.${ext}`,\n warnings: [...warnings, ...result.warnings],\n };\n}\n"],"mappings":"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","names":[]}
|