@bendyline/squisq-formats 1.4.1 → 2.0.0

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.
Files changed (139) hide show
  1. package/README.md +48 -2
  2. package/dist/{chunk-WWBIKRNP.js → chunk-2CSZJEAL.js} +13 -11
  3. package/dist/chunk-2CSZJEAL.js.map +1 -0
  4. package/dist/{chunk-RTS5XBZ7.js → chunk-2EFDWKC6.js} +262 -84
  5. package/dist/chunk-2EFDWKC6.js.map +1 -0
  6. package/dist/chunk-2PRVLAU3.js +740 -0
  7. package/dist/chunk-2PRVLAU3.js.map +1 -0
  8. package/dist/{chunk-SHLPSADL.js → chunk-4HJ6AK2Q.js} +54 -193
  9. package/dist/chunk-4HJ6AK2Q.js.map +1 -0
  10. package/dist/{chunk-5LSSWZPU.js → chunk-4VUWTSGM.js} +72 -25
  11. package/dist/chunk-4VUWTSGM.js.map +1 -0
  12. package/dist/{chunk-YESS7VY3.js → chunk-CVNVD7VH.js} +154 -39
  13. package/dist/chunk-CVNVD7VH.js.map +1 -0
  14. package/dist/chunk-CX3LH47J.js +99 -0
  15. package/dist/chunk-CX3LH47J.js.map +1 -0
  16. package/dist/{chunk-D7IFPWNZ.js → chunk-GVS2XXV6.js} +27 -26
  17. package/dist/chunk-GVS2XXV6.js.map +1 -0
  18. package/dist/chunk-H7T5DXUU.js +479 -0
  19. package/dist/chunk-H7T5DXUU.js.map +1 -0
  20. package/dist/{chunk-EHLGMSTE.js → chunk-HBSCRS5P.js} +10 -8
  21. package/dist/chunk-HBSCRS5P.js.map +1 -0
  22. package/dist/{chunk-46FMDOWZ.js → chunk-LXYLOOST.js} +49 -17
  23. package/dist/chunk-LXYLOOST.js.map +1 -0
  24. package/dist/{chunk-MJGRI6XR.js → chunk-MLX2BOJC.js} +12 -7
  25. package/dist/chunk-MLX2BOJC.js.map +1 -0
  26. package/dist/chunk-NKAJPJ4G.js +106 -0
  27. package/dist/chunk-NKAJPJ4G.js.map +1 -0
  28. package/dist/{chunk-DQAZR57U.js → chunk-O3GVVND4.js} +1 -1
  29. package/dist/chunk-ODL3SSPT.js +18 -0
  30. package/dist/chunk-ODL3SSPT.js.map +1 -0
  31. package/dist/chunk-X4USWOQ2.js +131 -0
  32. package/dist/chunk-X4USWOQ2.js.map +1 -0
  33. package/dist/chunk-XTLQXR4U.js +195 -0
  34. package/dist/chunk-XTLQXR4U.js.map +1 -0
  35. package/dist/chunk-YRT7GQ5Y.js +28 -0
  36. package/dist/chunk-YRT7GQ5Y.js.map +1 -0
  37. package/dist/chunk-ZZ7D4DN6.js +97 -0
  38. package/dist/chunk-ZZ7D4DN6.js.map +1 -0
  39. package/dist/container/index.d.ts +6 -2
  40. package/dist/container/index.js +15 -17
  41. package/dist/container/index.js.map +1 -1
  42. package/dist/docx/index.d.ts +6 -2
  43. package/dist/docx/index.js +5 -3
  44. package/dist/epub/index.d.ts +3 -1
  45. package/dist/epub/index.js +3 -3
  46. package/dist/export-Djev9BLU.d.ts +69 -0
  47. package/dist/extract-LNOA7DBK.js +15 -0
  48. package/dist/extract-LNOA7DBK.js.map +1 -0
  49. package/dist/html/index.d.ts +15 -85
  50. package/dist/html/index.js +3 -3
  51. package/dist/images-7FBWPKE3.js +7 -0
  52. package/dist/images-7FBWPKE3.js.map +1 -0
  53. package/dist/import-DTkDxHmZ.d.ts +84 -0
  54. package/dist/import-Yivc3kZM.d.ts +136 -0
  55. package/dist/index.d.ts +13 -4
  56. package/dist/index.js +30 -11
  57. package/dist/infer/index.d.ts +138 -0
  58. package/dist/infer/index.js +28 -0
  59. package/dist/infer/index.js.map +1 -0
  60. package/dist/layouts-7OUUVAGU.js +12 -0
  61. package/dist/layouts-7OUUVAGU.js.map +1 -0
  62. package/dist/layouts-CZ0bdsNL.d.ts +150 -0
  63. package/dist/mapTheme-IR27S6IV.js +11 -0
  64. package/dist/mapTheme-IR27S6IV.js.map +1 -0
  65. package/dist/ooxml/index.d.ts +21 -127
  66. package/dist/ooxml/index.js +31 -9
  67. package/dist/pdf/index.d.ts +3 -1
  68. package/dist/pdf/index.js +1 -1
  69. package/dist/pptx/index.d.ts +8 -108
  70. package/dist/pptx/index.js +13 -4
  71. package/dist/reader-Gof6OrJX.d.ts +140 -0
  72. package/dist/registry/index.d.ts +46 -8
  73. package/dist/registry/index.js +5 -2
  74. package/dist/themeReader-DPw4J5Ss.d.ts +56 -0
  75. package/dist/xlsx/index.d.ts +6 -66
  76. package/dist/xlsx/index.js +4 -2
  77. package/dist/zipLimits-bGYMS9cI.d.ts +29 -0
  78. package/package.json +8 -2
  79. package/src/__tests__/container.test.ts +41 -0
  80. package/src/__tests__/convert.test.ts +167 -0
  81. package/src/__tests__/epub.test.ts +102 -1
  82. package/src/__tests__/html.test.ts +62 -0
  83. package/src/__tests__/inferTheme.test.ts +135 -0
  84. package/src/__tests__/lossyWarnings.test.ts +41 -0
  85. package/src/__tests__/ooxml.test.ts +59 -2
  86. package/src/__tests__/ooxmlThemeReader.test.ts +92 -0
  87. package/src/__tests__/plainHtml.test.ts +15 -1
  88. package/src/__tests__/plainHtmlBundle.test.ts +18 -0
  89. package/src/__tests__/pptxImport.test.ts +30 -0
  90. package/src/__tests__/pptxInferFixtures.ts +314 -0
  91. package/src/__tests__/pptxLayoutInfer.test.ts +395 -0
  92. package/src/__tests__/zipSafety.test.ts +317 -0
  93. package/src/container/index.ts +20 -29
  94. package/src/docx/export.ts +4 -2
  95. package/src/docx/import.ts +4 -3
  96. package/src/epub/export.ts +94 -25
  97. package/src/html/docsHtmlBundle.ts +6 -2
  98. package/src/html/htmlTemplate.ts +31 -6
  99. package/src/html/imageUtils.ts +14 -5
  100. package/src/html/index.ts +12 -1
  101. package/src/html/plainHtml.ts +7 -3
  102. package/src/html/plainHtmlBundle.ts +11 -9
  103. package/src/index.ts +20 -0
  104. package/src/infer/extract.ts +127 -0
  105. package/src/infer/index.ts +168 -0
  106. package/src/infer/mapTheme.ts +176 -0
  107. package/src/infer/types.ts +27 -0
  108. package/src/ooxml/index.ts +14 -0
  109. package/src/ooxml/readUtils.ts +44 -0
  110. package/src/ooxml/reader.ts +87 -28
  111. package/src/ooxml/themeReader.ts +197 -0
  112. package/src/ooxml/types.ts +8 -9
  113. package/src/ooxml/writer.ts +28 -10
  114. package/src/pdf/export.ts +4 -2
  115. package/src/pdf/import.ts +40 -34
  116. package/src/pptx/export.ts +4 -2
  117. package/src/pptx/import.ts +214 -32
  118. package/src/pptx/index.ts +14 -0
  119. package/src/pptx/layouts.ts +1168 -0
  120. package/src/registry/convert.ts +54 -23
  121. package/src/registry/defaultFormats.ts +131 -44
  122. package/src/registry/index.ts +3 -0
  123. package/src/registry/types.ts +52 -6
  124. package/src/shared/boundedZipArchive.ts +342 -0
  125. package/src/shared/fidelity.ts +130 -0
  126. package/src/shared/zipEntryCount.ts +97 -0
  127. package/src/shared/zipLimits.ts +286 -0
  128. package/src/shared/zipSafety.ts +19 -0
  129. package/src/xlsx/import.ts +3 -2
  130. package/dist/chunk-46FMDOWZ.js.map +0 -1
  131. package/dist/chunk-5LSSWZPU.js.map +0 -1
  132. package/dist/chunk-D7IFPWNZ.js.map +0 -1
  133. package/dist/chunk-EHLGMSTE.js.map +0 -1
  134. package/dist/chunk-MJGRI6XR.js.map +0 -1
  135. package/dist/chunk-RTS5XBZ7.js.map +0 -1
  136. package/dist/chunk-SHLPSADL.js.map +0 -1
  137. package/dist/chunk-WWBIKRNP.js.map +0 -1
  138. package/dist/chunk-YESS7VY3.js.map +0 -1
  139. /package/dist/{chunk-DQAZR57U.js.map → chunk-O3GVVND4.js.map} +0 -0
@@ -1,3 +1,11 @@
1
+ import {
2
+ ConversionError
3
+ } from "./chunk-ODL3SSPT.js";
4
+ import {
5
+ ZipSafetyError,
6
+ openBoundedZipArchive
7
+ } from "./chunk-H7T5DXUU.js";
8
+
1
9
  // src/registry/types.ts
2
10
  var BUILTIN_FORMAT_IDS = [
3
11
  "md",
@@ -12,19 +20,97 @@ var BUILTIN_FORMAT_IDS = [
12
20
  "dbk"
13
21
  ];
14
22
 
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
- }
23
+ // src/shared/fidelity.ts
24
+ var CONTENT_NODE_TYPES = {
25
+ heading: true,
26
+ paragraph: true,
27
+ blockquote: true,
28
+ list: true,
29
+ code: true,
30
+ thematicBreak: true,
31
+ table: true,
32
+ htmlBlock: true,
33
+ math: true,
34
+ definition: true,
35
+ footnoteDefinition: true,
36
+ containerDirective: true,
37
+ leafDirective: true,
38
+ definitionList: true,
39
+ text: true,
40
+ emphasis: true,
41
+ strong: true,
42
+ delete: true,
43
+ inlineCode: true,
44
+ link: true,
45
+ image: true,
46
+ break: true,
47
+ htmlInline: true,
48
+ inlineMath: true,
49
+ footnoteReference: true,
50
+ linkReference: true,
51
+ imageReference: true,
52
+ textDirective: true,
53
+ mention: true,
54
+ inlineIcon: true
27
55
  };
56
+ var COMMON_SUPPORTED = [
57
+ "heading",
58
+ "paragraph",
59
+ "blockquote",
60
+ "list",
61
+ "code",
62
+ "thematicBreak",
63
+ "table",
64
+ "htmlBlock",
65
+ "math",
66
+ "text",
67
+ "emphasis",
68
+ "strong",
69
+ "delete",
70
+ "inlineCode",
71
+ "link",
72
+ "image",
73
+ "break",
74
+ "htmlInline",
75
+ "inlineMath"
76
+ ];
77
+ var PROFILE_SUPPORT = {
78
+ docx: /* @__PURE__ */ new Set([...COMMON_SUPPORTED, "footnoteDefinition", "footnoteReference"]),
79
+ pdf: /* @__PURE__ */ new Set([...COMMON_SUPPORTED, "footnoteDefinition", "footnoteReference"]),
80
+ pptx: new Set(COMMON_SUPPORTED),
81
+ epub: new Set(COMMON_SUPPORTED)
82
+ };
83
+ var PROFILE_LABELS = {
84
+ docx: "DOCX",
85
+ pdf: "PDF",
86
+ pptx: "PPTX",
87
+ epub: "EPUB"
88
+ };
89
+ function markdownFidelityWarnings(document2, profile) {
90
+ const supported = PROFILE_SUPPORT[profile];
91
+ const omitted = /* @__PURE__ */ new Map();
92
+ const visit = (candidate) => {
93
+ if (!candidate || typeof candidate !== "object") return;
94
+ const node = candidate;
95
+ const type = node.type;
96
+ if (type && Object.prototype.hasOwnProperty.call(CONTENT_NODE_TYPES, type)) {
97
+ if (!supported.has(type)) {
98
+ omitted.set(type, (omitted.get(type) ?? 0) + 1);
99
+ return;
100
+ }
101
+ }
102
+ if (Array.isArray(node.children)) {
103
+ for (const child of node.children) visit(child);
104
+ }
105
+ };
106
+ for (const child of document2.children) visit(child);
107
+ if (omitted.size === 0) return [];
108
+ const total = [...omitted.values()].reduce((sum, count) => sum + count, 0);
109
+ const details = [...omitted].map(([type, count]) => `${type} (${count})`).join(", ");
110
+ return [
111
+ `${PROFILE_LABELS[profile]} export omitted ${total} unsupported Markdown node(s): ${details}.`
112
+ ];
113
+ }
28
114
 
29
115
  // src/registry/defaultFormats.ts
30
116
  var MIME = {
@@ -55,6 +141,9 @@ async function toBytes(data) {
55
141
  function resolveThemeId(input, options) {
56
142
  return options.themeId ?? input.doc.themeId;
57
143
  }
144
+ function optionsFor(options, id) {
145
+ return options.formatOptions?.[id] ?? {};
146
+ }
58
147
  async function markdownOf(input) {
59
148
  if (input.markdownDoc) return input.markdownDoc;
60
149
  const { docToMarkdown } = await import("@bendyline/squisq/doc");
@@ -73,6 +162,29 @@ async function collectContainerImages(container) {
73
162
  }
74
163
  return images;
75
164
  }
165
+ async function collectContainerDocxImages(container) {
166
+ const images = await collectContainerImages(container);
167
+ const { extToMime } = await import("./images-7FBWPKE3.js");
168
+ return new Map(
169
+ [...images].map(([path, data]) => [
170
+ path,
171
+ { data, contentType: extToMime(path.split(".").pop() ?? "") }
172
+ ])
173
+ );
174
+ }
175
+ async function snapshotContainerWithDocument(input) {
176
+ const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
177
+ const snapshot = new MemoryContentContainer();
178
+ const entries = await input.container.listFiles();
179
+ for (const entry of entries) {
180
+ const data = await input.container.readFile(entry.path);
181
+ if (data) await snapshot.writeFile(entry.path, new Uint8Array(data), entry.mimeType);
182
+ }
183
+ const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
184
+ const documentPath = await input.container.getDocumentPath() ?? "index.md";
185
+ await snapshot.writeDocument(stringifyMarkdown(await markdownOf(input)), documentPath);
186
+ return snapshot;
187
+ }
76
188
  async function requirePlayerScript(options, format) {
77
189
  const script = await options.resolvePlayerScript?.();
78
190
  if (!script) {
@@ -86,19 +198,27 @@ async function requirePlayerScript(options, format) {
86
198
  function ok(bytes, mimeType, warnings = []) {
87
199
  return { bytes, mimeType, suggestedFilename: "", warnings };
88
200
  }
201
+ function pptxImportOptionsFrom(options) {
202
+ const raw = optionsFor(options, "pptx");
203
+ return {
204
+ ...raw,
205
+ inferTheme: raw?.inferTheme !== false,
206
+ inferLayouts: raw?.inferLayouts !== false
207
+ };
208
+ }
89
209
  function defaultFormats() {
90
210
  const md = {
91
211
  id: "md",
92
212
  label: "Markdown",
93
213
  mimeType: MIME.md,
94
214
  extensions: [".md", ".markdown"],
95
- async importDoc(data) {
215
+ async importDoc(data, options) {
96
216
  const { parseMarkdown } = await import("@bendyline/squisq/markdown");
97
- return parseMarkdown(new TextDecoder().decode(data));
217
+ return parseMarkdown(new TextDecoder().decode(data), optionsFor(options, "md").parse);
98
218
  },
99
- async exportDoc(input) {
219
+ async exportDoc(input, options) {
100
220
  const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
101
- const text = stringifyMarkdown(await markdownOf(input));
221
+ const text = stringifyMarkdown(await markdownOf(input), optionsFor(options, "md").stringify);
102
222
  return ok(new TextEncoder().encode(text), MIME.md);
103
223
  }
104
224
  };
@@ -107,20 +227,28 @@ function defaultFormats() {
107
227
  label: "Word (DOCX)",
108
228
  mimeType: MIME.docx,
109
229
  extensions: [".docx"],
110
- async importContainer(data) {
230
+ async importContainer(data, options) {
111
231
  const { docxToContainer } = await import("./docx/index.js");
112
- return docxToContainer(data);
232
+ return docxToContainer(data, optionsFor(options, "docx"));
113
233
  },
114
- async importDoc(data) {
234
+ async importDoc(data, options) {
115
235
  const { docxToMarkdownDoc } = await import("./docx/index.js");
116
- return docxToMarkdownDoc(data);
236
+ return docxToMarkdownDoc(data, optionsFor(options, "docx"));
117
237
  },
118
238
  async exportDoc(input, options) {
119
239
  const { markdownDocToDocx } = await import("./docx/index.js");
120
- const buf = await markdownDocToDocx(await markdownOf(input), {
121
- themeId: resolveThemeId(input, options)
240
+ const raw = optionsFor(options, "docx");
241
+ const markdownDoc = await markdownOf(input);
242
+ const containerImages = await collectContainerDocxImages(input.container);
243
+ const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
244
+ const buf = await markdownDocToDocx(markdownDoc, {
245
+ ...raw,
246
+ ...options.title !== void 0 ? { title: options.title } : {},
247
+ themeId: resolveThemeId(input, options),
248
+ themeRegistry: options.themeRegistry ?? raw.themeRegistry,
249
+ images
122
250
  });
123
- return ok(await toBytes(buf), MIME.docx);
251
+ return ok(await toBytes(buf), MIME.docx, markdownFidelityWarnings(markdownDoc, "docx"));
124
252
  }
125
253
  };
126
254
  const pdf = {
@@ -128,20 +256,25 @@ function defaultFormats() {
128
256
  label: "PDF",
129
257
  mimeType: MIME.pdf,
130
258
  extensions: [".pdf"],
131
- async importContainer(data) {
259
+ async importContainer(data, options) {
132
260
  const { pdfToContainer } = await import("./pdf/index.js");
133
- return pdfToContainer(data);
261
+ return pdfToContainer(data, optionsFor(options, "pdf"));
134
262
  },
135
- async importDoc(data) {
263
+ async importDoc(data, options) {
136
264
  const { pdfToMarkdownDoc } = await import("./pdf/index.js");
137
- return pdfToMarkdownDoc(data);
265
+ return pdfToMarkdownDoc(data, optionsFor(options, "pdf"));
138
266
  },
139
267
  async exportDoc(input, options) {
140
268
  const { markdownDocToPdf } = await import("./pdf/index.js");
141
- const buf = await markdownDocToPdf(await markdownOf(input), {
142
- themeId: resolveThemeId(input, options)
269
+ const raw = optionsFor(options, "pdf");
270
+ const markdownDoc = await markdownOf(input);
271
+ const buf = await markdownDocToPdf(markdownDoc, {
272
+ ...raw,
273
+ ...options.title !== void 0 ? { title: options.title } : {},
274
+ themeId: resolveThemeId(input, options),
275
+ themeRegistry: options.themeRegistry ?? raw.themeRegistry
143
276
  });
144
- return ok(await toBytes(buf), MIME.pdf);
277
+ return ok(await toBytes(buf), MIME.pdf, markdownFidelityWarnings(markdownDoc, "pdf"));
145
278
  }
146
279
  };
147
280
  const pptx = {
@@ -149,22 +282,28 @@ function defaultFormats() {
149
282
  label: "PowerPoint (PPTX)",
150
283
  mimeType: MIME.pptx,
151
284
  extensions: [".pptx"],
152
- async importContainer(data) {
285
+ async importContainer(data, options) {
153
286
  const { pptxToContainer } = await import("./pptx/index.js");
154
- return pptxToContainer(data);
287
+ return pptxToContainer(data, pptxImportOptionsFrom(options));
155
288
  },
156
- async importDoc(data) {
289
+ async importDoc(data, options) {
157
290
  const { pptxToMarkdownDoc } = await import("./pptx/index.js");
158
- return pptxToMarkdownDoc(data);
291
+ return pptxToMarkdownDoc(data, pptxImportOptionsFrom(options));
159
292
  },
160
293
  async exportDoc(input, options) {
161
294
  const { markdownDocToPptx } = await import("./pptx/index.js");
162
- const images = await collectContainerImages(input.container);
163
- const buf = await markdownDocToPptx(await markdownOf(input), {
295
+ const raw = optionsFor(options, "pptx");
296
+ const markdownDoc = await markdownOf(input);
297
+ const containerImages = await collectContainerImages(input.container);
298
+ const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
299
+ const buf = await markdownDocToPptx(markdownDoc, {
300
+ ...raw,
301
+ ...options.title !== void 0 ? { title: options.title } : {},
164
302
  themeId: resolveThemeId(input, options),
303
+ themeRegistry: options.themeRegistry ?? raw.themeRegistry,
165
304
  images
166
305
  });
167
- return ok(await toBytes(buf), MIME.pptx);
306
+ return ok(await toBytes(buf), MIME.pptx, markdownFidelityWarnings(markdownDoc, "pptx"));
168
307
  }
169
308
  };
170
309
  const xlsx = {
@@ -172,18 +311,21 @@ function defaultFormats() {
172
311
  label: "Excel (XLSX)",
173
312
  mimeType: MIME.xlsx,
174
313
  extensions: [".xlsx"],
175
- async importDoc(data) {
314
+ async importDoc(data, options) {
176
315
  const { xlsxToMarkdownDoc } = await import("./xlsx/index.js");
177
- return xlsxToMarkdownDoc(data);
316
+ return xlsxToMarkdownDoc(data, optionsFor(options, "xlsx"));
178
317
  },
179
- async exportDoc(input) {
318
+ async exportDoc(input, options) {
180
319
  const { markdownDocToXlsx } = await import("./xlsx/index.js");
181
320
  const markdownDoc = await markdownOf(input);
182
321
  const omitted = markdownDoc.children.filter(
183
322
  (n) => n.type !== "table" && n.type !== "heading"
184
323
  ).length;
185
324
  const warnings = omitted > 0 ? [`XLSX export is tables-only; ${omitted} non-table block(s) were omitted.`] : [];
186
- const blob = await markdownDocToXlsx(markdownDoc);
325
+ const blob = await markdownDocToXlsx(markdownDoc, {
326
+ ...optionsFor(options, "xlsx"),
327
+ ...options.title !== void 0 ? { title: options.title } : {}
328
+ });
187
329
  return ok(await toBytes(blob), MIME.xlsx, warnings);
188
330
  }
189
331
  };
@@ -192,21 +334,22 @@ function defaultFormats() {
192
334
  label: "CSV",
193
335
  mimeType: MIME.csv,
194
336
  extensions: [".csv"],
195
- async importDoc(data) {
337
+ async importDoc(data, options) {
196
338
  const { csvToMarkdownDoc } = await import("./csv/index.js");
197
- return csvToMarkdownDoc(data);
339
+ return csvToMarkdownDoc(data, optionsFor(options, "csv"));
198
340
  },
199
- async exportDoc(input) {
341
+ async exportDoc(input, options) {
200
342
  const { markdownDocToCsv } = await import("./csv/index.js");
201
343
  const markdownDoc = await markdownOf(input);
344
+ const raw = optionsFor(options, "csv");
202
345
  const tableCount = markdownDoc.children.filter((n) => n.type === "table").length;
203
346
  const warnings = [];
204
- if (tableCount > 1) {
347
+ if (tableCount > 1 && raw.tableIndex === void 0) {
205
348
  warnings.push(
206
349
  `Document has ${tableCount} tables; CSV export emitted only the first. Use the csv converter's tableIndex option to select another.`
207
350
  );
208
351
  }
209
- const text = markdownDocToCsv(markdownDoc);
352
+ const text = markdownDocToCsv(markdownDoc, raw);
210
353
  return ok(new TextEncoder().encode(text), MIME.csv, warnings);
211
354
  }
212
355
  };
@@ -215,22 +358,26 @@ function defaultFormats() {
215
358
  label: "HTML (single file)",
216
359
  mimeType: MIME.html,
217
360
  extensions: [".html", ".htm"],
218
- async importDoc(data) {
361
+ async importDoc(data, options) {
219
362
  const { htmlToMarkdownDoc } = await import("./html/index.js");
220
- return htmlToMarkdownDoc(data);
363
+ return htmlToMarkdownDoc(data, optionsFor(options, "html"));
221
364
  },
222
365
  // importContainer omitted: HTML import already inlines images as data URIs,
223
366
  // so there is nothing to extract into a container this wave.
224
367
  async exportDoc(input, options) {
225
368
  const playerScript = await requirePlayerScript(options, "html");
226
369
  const { docToHtml } = await import("./html/index.js");
227
- const images = await collectContainerImages(input.container);
370
+ const raw = optionsFor(options, "html");
371
+ const containerImages = await collectContainerImages(input.container);
372
+ const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
228
373
  const htmlText = docToHtml(input.doc, {
374
+ ...raw,
229
375
  playerScript,
230
376
  images,
231
377
  title: options.title ?? input.baseName,
232
- mode: "static",
233
- themeId: resolveThemeId(input, options)
378
+ mode: raw.mode ?? "static",
379
+ themeId: resolveThemeId(input, options),
380
+ themeRegistry: options.themeRegistry ?? raw.themeRegistry
234
381
  });
235
382
  return ok(new TextEncoder().encode(htmlText), MIME.html);
236
383
  }
@@ -243,13 +390,17 @@ function defaultFormats() {
243
390
  async exportDoc(input, options) {
244
391
  const playerScript = await requirePlayerScript(options, "htmlzip");
245
392
  const { docToHtmlZip } = await import("./html/index.js");
246
- const images = await collectContainerImages(input.container);
393
+ const raw = optionsFor(options, "htmlzip");
394
+ const containerImages = await collectContainerImages(input.container);
395
+ const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
247
396
  const blob = await docToHtmlZip(input.doc, {
397
+ ...raw,
248
398
  playerScript,
249
399
  images,
250
400
  title: options.title ?? input.baseName,
251
- mode: "static",
252
- themeId: resolveThemeId(input, options)
401
+ mode: raw.mode ?? "static",
402
+ themeId: resolveThemeId(input, options),
403
+ themeRegistry: options.themeRegistry ?? raw.themeRegistry
253
404
  });
254
405
  return ok(await toBytes(blob), MIME.zip);
255
406
  }
@@ -261,13 +412,18 @@ function defaultFormats() {
261
412
  extensions: [".epub"],
262
413
  async exportDoc(input, options) {
263
414
  const { markdownDocToEpub } = await import("./epub/index.js");
264
- const images = await collectContainerImages(input.container);
265
- const buf = await markdownDocToEpub(await markdownOf(input), {
415
+ const raw = optionsFor(options, "epub");
416
+ const markdownDoc = await markdownOf(input);
417
+ const containerImages = await collectContainerImages(input.container);
418
+ const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
419
+ const buf = await markdownDocToEpub(markdownDoc, {
420
+ ...raw,
266
421
  title: options.title ?? input.baseName,
267
422
  themeId: resolveThemeId(input, options),
423
+ themeRegistry: options.themeRegistry ?? raw.themeRegistry,
268
424
  images
269
425
  });
270
- return ok(await toBytes(buf), MIME.epub);
426
+ return ok(await toBytes(buf), MIME.epub, markdownFidelityWarnings(markdownDoc, "epub"));
271
427
  }
272
428
  };
273
429
  const dbk = {
@@ -275,13 +431,13 @@ function defaultFormats() {
275
431
  label: "Squisq container (DBK)",
276
432
  mimeType: MIME.zip,
277
433
  extensions: [".dbk", ".zip"],
278
- async importContainer(data) {
434
+ async importContainer(data, options) {
279
435
  const { zipToContainer } = await import("./container/index.js");
280
- return zipToContainer(data);
436
+ return zipToContainer(data, optionsFor(options, "dbk"));
281
437
  },
282
438
  async exportDoc(input) {
283
439
  const { containerToZip } = await import("./container/index.js");
284
- const blob = await containerToZip(input.container);
440
+ const blob = await containerToZip(await snapshotContainerWithDocument(input));
285
441
  return ok(await toBytes(blob), MIME.zip);
286
442
  }
287
443
  };
@@ -323,6 +479,9 @@ function defaultRegistry() {
323
479
  }
324
480
 
325
481
  // src/registry/convert.ts
482
+ function markdownFormatOptions(options) {
483
+ return options.formatOptions?.md ?? {};
484
+ }
326
485
  function hasPrefix(bytes, prefix) {
327
486
  if (bytes.length < prefix.length) return false;
328
487
  for (let i = 0; i < prefix.length; i++) {
@@ -333,13 +492,19 @@ function hasPrefix(bytes, prefix) {
333
492
  var ZIP_MAGIC = [80, 75, 3, 4];
334
493
  var PDF_MAGIC = [37, 80, 68, 70];
335
494
  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.");
495
+ const archive = await openBoundedZipArchive(bytes).catch((cause) => {
496
+ if (cause instanceof ZipSafetyError && cause.code !== "invalid-archive") throw cause;
497
+ throw new ConversionError("invalid-input", "Input is not a readable ZIP archive.", { cause });
339
498
  });
340
- const contentTypes = zip.file("[Content_Types].xml");
499
+ const contentTypes = archive.entries.find((entry) => entry.path === "[Content_Types].xml");
341
500
  if (!contentTypes) return "dbk";
342
- const xml = await contentTypes.async("string");
501
+ const maxContentTypesBytes = 1024 * 1024;
502
+ if ((contentTypes.declaredSize ?? 0) > maxContentTypesBytes) {
503
+ throw new ConversionError("invalid-input", "OOXML content-types metadata is too large.");
504
+ }
505
+ const xmlBytes = await archive.read(contentTypes.path, maxContentTypesBytes);
506
+ if (!xmlBytes) return "dbk";
507
+ const xml = new TextDecoder().decode(xmlBytes);
343
508
  if (xml.includes("wordprocessingml")) return "docx";
344
509
  if (xml.includes("presentationml")) return "pptx";
345
510
  if (xml.includes("spreadsheetml")) return "xlsx";
@@ -357,13 +522,15 @@ async function detectByteFormat(bytes, filename, options, registry) {
357
522
  return "md";
358
523
  }
359
524
  function extractExt(filename) {
360
- const base = filename.split("/").pop() ?? filename;
525
+ const normalized = filename.replace(/\\/g, "/");
526
+ const base = normalized.split("/").pop() ?? normalized;
361
527
  const dot = base.lastIndexOf(".");
362
528
  if (dot <= 0) return "";
363
529
  return base.slice(dot + 1).toLowerCase();
364
530
  }
365
531
  function baseNameOf(filename) {
366
- const base = filename.split("/").pop() ?? filename;
532
+ const normalized = filename.replace(/\\/g, "/");
533
+ const base = normalized.split("/").pop() ?? normalized;
367
534
  const dot = base.lastIndexOf(".");
368
535
  return dot > 0 ? base.slice(0, dot) : base;
369
536
  }
@@ -403,7 +570,7 @@ async function normalizeBytes(source, options, registry) {
403
570
  );
404
571
  }
405
572
  const text = await container.readDocument();
406
- markdownDoc = text ? parseMarkdown(text) : { type: "document", children: [] };
573
+ markdownDoc = text ? parseMarkdown(text, markdownFormatOptions(options).parse) : { type: "document", children: [] };
407
574
  } else {
408
575
  markdownDoc = await fromDef.importDoc(buffer, options);
409
576
  const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
@@ -422,7 +589,7 @@ async function normalizeMarkdown(source, options) {
422
589
  const { markdownToDoc } = await import("@bendyline/squisq/doc");
423
590
  const { parseMarkdown } = await import("@bendyline/squisq/markdown");
424
591
  const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
425
- const markdownDoc = typeof source.markdown === "string" ? parseMarkdown(source.markdown) : source.markdown;
592
+ const markdownDoc = typeof source.markdown === "string" ? parseMarkdown(source.markdown, markdownFormatOptions(options).parse) : source.markdown;
426
593
  let container = source.container;
427
594
  if (!container) {
428
595
  const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
@@ -457,11 +624,11 @@ async function normalize(source, options, registry) {
457
624
  return normalizeDoc(source);
458
625
  }
459
626
  }
460
- async function applyTransformStyle(input, transformStyle, themeId) {
627
+ async function applyTransformStyle(input, transformStyle, themeId, registry) {
461
628
  const { applyTransform, extractDocImages } = await import("@bendyline/squisq/transform");
462
629
  const { docToMarkdown, markdownToDoc } = await import("@bendyline/squisq/doc");
463
630
  const images = extractDocImages(input.doc.blocks);
464
- const result = applyTransform(input.doc, transformStyle, { themeId, images });
631
+ const result = applyTransform(input.doc, transformStyle, { themeId, images, registry });
465
632
  input.doc = result.doc;
466
633
  const markdownDoc = docToMarkdown(result.doc);
467
634
  input.markdownDoc = markdownDoc;
@@ -486,21 +653,33 @@ async function convert(source, to, options = {}) {
486
653
  { format: to, hint: "This format is import-only." }
487
654
  );
488
655
  }
489
- const { input, warnings } = await normalize(source, options, registry);
490
- if (options.themeId && !input.doc.themeId) {
491
- input.doc.themeId = options.themeId;
656
+ let normalized;
657
+ try {
658
+ normalized = await normalize(source, options, registry);
659
+ } catch (err) {
660
+ if (err instanceof ConversionError) throw err;
661
+ const message = err instanceof Error ? err.message : String(err);
662
+ throw new ConversionError("invalid-input", message, {
663
+ format: options.from,
664
+ cause: err
665
+ });
492
666
  }
493
- if (options.transformStyle) {
494
- warnings.push(
495
- ...await applyTransformStyle(
496
- input,
497
- options.transformStyle,
498
- options.themeId ?? input.doc.themeId
499
- )
500
- );
667
+ const { input, warnings } = normalized;
668
+ if (options.themeId && !input.doc.themeId) {
669
+ input.doc = { ...input.doc, themeId: options.themeId };
501
670
  }
502
671
  let result;
503
672
  try {
673
+ if (options.transformStyle) {
674
+ warnings.push(
675
+ ...await applyTransformStyle(
676
+ input,
677
+ options.transformStyle,
678
+ options.themeId ?? input.doc.themeId,
679
+ options.transformRegistry
680
+ )
681
+ );
682
+ }
504
683
  result = await target.exportDoc(input, options);
505
684
  } catch (err) {
506
685
  if (err instanceof ConversionError) throw err;
@@ -517,10 +696,9 @@ async function convert(source, to, options = {}) {
517
696
 
518
697
  export {
519
698
  BUILTIN_FORMAT_IDS,
520
- ConversionError,
521
699
  defaultFormats,
522
700
  createRegistry,
523
701
  defaultRegistry,
524
702
  convert
525
703
  };
526
- //# sourceMappingURL=chunk-RTS5XBZ7.js.map
704
+ //# sourceMappingURL=chunk-2EFDWKC6.js.map