@bendyline/squisq-formats 2.1.0 → 2.3.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.
- package/LICENSE +21 -0
- package/NOTICE.md +19 -0
- package/README.md +2 -2
- package/dist/{chunk-NNHKUXKA.js → chunk-26ISNJ7Y.js} +85 -65
- package/dist/{chunk-NKAJPJ4G.js → chunk-2JJ5RFDZ.js} +0 -1
- package/dist/{chunk-KURGXM4I.js → chunk-4V3KCHAP.js} +3 -4
- package/dist/{chunk-MLX2BOJC.js → chunk-6RQOV3B3.js} +1 -2
- package/dist/{chunk-EW54IRRS.js → chunk-6S6GU3ZG.js} +5 -6
- package/dist/{chunk-FE6OJV6O.js → chunk-7AWFHP5U.js} +1 -1
- package/dist/{chunk-RFAPOKHJ.js → chunk-AD2WT564.js} +59 -9
- package/dist/{chunk-O3GVVND4.js → chunk-AONELFLA.js} +0 -1
- package/dist/{chunk-SC67HYQJ.js → chunk-EJTNGKEA.js} +5 -8
- package/dist/{chunk-WQSHGBLN.js → chunk-G6J326JW.js} +193 -42
- package/dist/chunk-GX7RAUME.js +121 -0
- package/dist/{chunk-GVS2XXV6.js → chunk-GYIVES2E.js} +485 -59
- package/dist/{chunk-SSUPBUF5.js → chunk-IIQYS2YH.js} +0 -1
- package/dist/{chunk-RLU7UFYU.js → chunk-IPN56VLW.js} +83 -58
- package/dist/{chunk-DTDF6QDP.js → chunk-JE6LSIHE.js} +81 -20
- package/dist/{chunk-U4MRIFKL.js → chunk-JU2RHXUB.js} +0 -1
- package/dist/{chunk-4VUWTSGM.js → chunk-K6XRMVPW.js} +64 -31
- package/dist/{chunk-ODL3SSPT.js → chunk-KXOZMWBS.js} +0 -1
- package/dist/chunk-OGS5VCGJ.js +446 -0
- package/dist/{chunk-74GO3FVS.js → chunk-PU7REGWV.js} +5 -8
- package/dist/{chunk-PN52A5AA.js → chunk-SBUW7NHR.js} +0 -1
- package/dist/{chunk-QFLDYKCR.js → chunk-TAAENIRB.js} +5 -8
- package/dist/{chunk-7ARKUCQT.js → chunk-X2DEAXNK.js} +54 -2
- package/dist/container/index.js +1 -2
- package/dist/csv/index.d.ts +27 -2
- package/dist/csv/index.js +1 -2
- package/dist/docx/index.d.ts +5 -1
- package/dist/docx/index.js +9 -11
- package/dist/epub/index.d.ts +2 -0
- package/dist/epub/index.js +5 -6
- package/dist/{export-D2NkylDT.d.ts → export-D9msROJS.d.ts} +18 -6
- package/dist/extract-MN7LA3NL.js +13 -0
- package/dist/html/index.d.ts +11 -4
- package/dist/html/index.js +3 -4
- package/dist/images-ESPQKVTW.js +6 -0
- package/dist/{import-K8mfc0fz.d.ts → import-C3htUTss.d.ts} +5 -1
- package/dist/{import-DTkDxHmZ.d.ts → import-C8whCC7_.d.ts} +6 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +28 -26
- package/dist/infer/index.d.ts +3 -3
- package/dist/infer/index.js +7 -9
- package/dist/{layouts-BHrgZ5FS.d.ts → layouts-CTdPlB-u.d.ts} +1 -1
- package/dist/layouts-DRWZGSPD.js +10 -0
- package/dist/{mapTheme-IR27S6IV.js → mapTheme-4TWH25FT.js} +1 -2
- package/dist/ooxml/index.d.ts +3 -3
- package/dist/ooxml/index.js +14 -13
- package/dist/pdf/index.d.ts +18 -0
- package/dist/pdf/index.js +2 -3
- package/dist/pptx/index.d.ts +4 -4
- package/dist/pptx/index.js +11 -13
- package/dist/{reader-B9L8Ucbj.d.ts → reader-B_m1aKZC.d.ts} +30 -1
- package/dist/registry/index.d.ts +21 -5
- package/dist/registry/index.js +9 -6
- package/dist/{themeReader-DJKErl_j.d.ts → themeReader-DCtwC83Q.d.ts} +1 -1
- package/dist/xlsx/index.d.ts +3 -3
- package/dist/xlsx/index.js +6 -7
- package/package.json +10 -4
- package/dist/chunk-4VUWTSGM.js.map +0 -1
- package/dist/chunk-6M7Z25LA.js +0 -46
- package/dist/chunk-6M7Z25LA.js.map +0 -1
- package/dist/chunk-74GO3FVS.js.map +0 -1
- package/dist/chunk-7ARKUCQT.js.map +0 -1
- package/dist/chunk-DTDF6QDP.js.map +0 -1
- package/dist/chunk-EW54IRRS.js.map +0 -1
- package/dist/chunk-FE6OJV6O.js.map +0 -1
- package/dist/chunk-GVS2XXV6.js.map +0 -1
- package/dist/chunk-KURGXM4I.js.map +0 -1
- package/dist/chunk-MLX2BOJC.js.map +0 -1
- package/dist/chunk-NKAJPJ4G.js.map +0 -1
- package/dist/chunk-NNHKUXKA.js.map +0 -1
- package/dist/chunk-O3GVVND4.js.map +0 -1
- package/dist/chunk-ODL3SSPT.js.map +0 -1
- package/dist/chunk-PN52A5AA.js.map +0 -1
- package/dist/chunk-QFLDYKCR.js.map +0 -1
- package/dist/chunk-RFAPOKHJ.js.map +0 -1
- package/dist/chunk-RLU7UFYU.js.map +0 -1
- package/dist/chunk-SC67HYQJ.js.map +0 -1
- package/dist/chunk-SSUPBUF5.js.map +0 -1
- package/dist/chunk-U4MRIFKL.js.map +0 -1
- package/dist/chunk-UGYF5AZE.js +0 -275
- package/dist/chunk-UGYF5AZE.js.map +0 -1
- package/dist/chunk-WQSHGBLN.js.map +0 -1
- package/dist/chunk-YRT7GQ5Y.js +0 -28
- package/dist/chunk-YRT7GQ5Y.js.map +0 -1
- package/dist/container/index.js.map +0 -1
- package/dist/csv/index.js.map +0 -1
- package/dist/docx/index.js.map +0 -1
- package/dist/epub/index.js.map +0 -1
- package/dist/extract-OJ7ZQV6P.js +0 -15
- package/dist/extract-OJ7ZQV6P.js.map +0 -1
- package/dist/html/index.js.map +0 -1
- package/dist/images-7FBWPKE3.js +0 -7
- package/dist/images-7FBWPKE3.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/infer/index.js.map +0 -1
- package/dist/layouts-5VDIRPIJ.js +0 -12
- package/dist/layouts-5VDIRPIJ.js.map +0 -1
- package/dist/mapTheme-IR27S6IV.js.map +0 -1
- package/dist/ooxml/index.js.map +0 -1
- package/dist/pdf/index.js.map +0 -1
- package/dist/pptx/index.js.map +0 -1
- package/dist/registry/index.js.map +0 -1
- package/dist/xlsx/index.js.map +0 -1
- package/src/__tests__/container.test.ts +0 -230
- package/src/__tests__/convert.test.ts +0 -495
- package/src/__tests__/csvImport.test.ts +0 -84
- package/src/__tests__/docxExport.test.ts +0 -491
- package/src/__tests__/docxImport.test.ts +0 -531
- package/src/__tests__/epub.test.ts +0 -649
- package/src/__tests__/exportThemeReconciliation.test.ts +0 -87
- package/src/__tests__/formatRegistry.test.ts +0 -174
- package/src/__tests__/html.test.ts +0 -439
- package/src/__tests__/htmlImport.test.ts +0 -57
- package/src/__tests__/inferTheme.test.ts +0 -135
- package/src/__tests__/lossyWarnings.test.ts +0 -146
- package/src/__tests__/ooxml.test.ts +0 -271
- package/src/__tests__/ooxmlCancellation.test.ts +0 -113
- package/src/__tests__/ooxmlThemeReader.test.ts +0 -92
- package/src/__tests__/pdfExport.test.ts +0 -322
- package/src/__tests__/pdfImport.test.ts +0 -384
- package/src/__tests__/plainHtml.test.ts +0 -417
- package/src/__tests__/plainHtmlBundle.test.ts +0 -253
- package/src/__tests__/pptxExport.test.ts +0 -138
- package/src/__tests__/pptxImport.test.ts +0 -145
- package/src/__tests__/pptxInferFixtures.ts +0 -314
- package/src/__tests__/pptxLayoutInfer.test.ts +0 -395
- package/src/__tests__/roundTrip.test.ts +0 -201
- package/src/__tests__/roundTripAssets.test.ts +0 -50
- package/src/__tests__/roundTripMatrix.fixtures.ts +0 -86
- package/src/__tests__/roundTripMatrix.helpers.ts +0 -154
- package/src/__tests__/roundTripMatrix.test.ts +0 -142
- package/src/__tests__/sharedContainer.test.ts +0 -41
- package/src/__tests__/sharedImages.test.ts +0 -61
- package/src/__tests__/xlsxExport.test.ts +0 -164
- package/src/__tests__/xlsxImport.test.ts +0 -80
- package/src/__tests__/zipSafety.test.ts +0 -317
- package/src/container/index.ts +0 -94
- package/src/csv/index.ts +0 -188
- package/src/docx/export.ts +0 -1375
- package/src/docx/import.ts +0 -1250
- package/src/docx/index.ts +0 -26
- package/src/docx/styles.ts +0 -145
- package/src/epub/export.ts +0 -968
- package/src/epub/index.ts +0 -20
- package/src/html/docsHtmlBundle.ts +0 -373
- package/src/html/htmlTemplate.ts +0 -385
- package/src/html/imageUtils.ts +0 -61
- package/src/html/import.ts +0 -297
- package/src/html/index.ts +0 -212
- package/src/html/plainHtml.ts +0 -790
- package/src/html/plainHtmlBundle.ts +0 -421
- package/src/index.ts +0 -109
- package/src/infer/extract.ts +0 -127
- package/src/infer/index.ts +0 -199
- package/src/infer/mapTheme.ts +0 -176
- package/src/infer/types.ts +0 -27
- package/src/ooxml/index.ts +0 -111
- package/src/ooxml/namespaces.ts +0 -217
- package/src/ooxml/readUtils.ts +0 -44
- package/src/ooxml/reader.ts +0 -318
- package/src/ooxml/themeReader.ts +0 -197
- package/src/ooxml/types.ts +0 -103
- package/src/ooxml/writer.ts +0 -339
- package/src/ooxml/xmlUtils.ts +0 -123
- package/src/pdf/export.ts +0 -1084
- package/src/pdf/import.ts +0 -1164
- package/src/pdf/index.ts +0 -29
- package/src/pdf/styles.ts +0 -180
- package/src/pptx/export.ts +0 -1184
- package/src/pptx/import.ts +0 -455
- package/src/pptx/index.ts +0 -52
- package/src/pptx/layouts.ts +0 -1222
- package/src/pptx/styles.ts +0 -96
- package/src/pptx/templates.ts +0 -187
- package/src/registry/convert.ts +0 -433
- package/src/registry/defaultFormats.ts +0 -413
- package/src/registry/errors.ts +0 -46
- package/src/registry/index.ts +0 -43
- package/src/registry/registry.ts +0 -48
- package/src/registry/types.ts +0 -170
- package/src/shared/boundedZipArchive.ts +0 -383
- package/src/shared/container.ts +0 -28
- package/src/shared/fidelity.ts +0 -130
- package/src/shared/images.ts +0 -44
- package/src/shared/inlineRuns.ts +0 -99
- package/src/shared/text.ts +0 -41
- package/src/shared/zipEntryCount.ts +0 -151
- package/src/shared/zipLimits.ts +0 -296
- package/src/shared/zipSafety.ts +0 -19
- package/src/xlsx/export.ts +0 -253
- package/src/xlsx/import.ts +0 -160
- package/src/xlsx/index.ts +0 -35
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ConversionError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KXOZMWBS.js";
|
|
4
4
|
import {
|
|
5
5
|
ZipSafetyError,
|
|
6
|
-
openBoundedZipArchive
|
|
7
|
-
|
|
6
|
+
openBoundedZipArchive,
|
|
7
|
+
resolveZipSafetyLimits
|
|
8
|
+
} from "./chunk-7AWFHP5U.js";
|
|
8
9
|
|
|
9
10
|
// src/registry/types.ts
|
|
10
11
|
var BUILTIN_FORMAT_IDS = [
|
|
@@ -20,6 +21,84 @@ var BUILTIN_FORMAT_IDS = [
|
|
|
20
21
|
"dbk"
|
|
21
22
|
];
|
|
22
23
|
|
|
24
|
+
// src/registry/limits.ts
|
|
25
|
+
var DEFAULT_CONVERSION_LIMITS = Object.freeze({
|
|
26
|
+
maxInputBytes: 256 * 1024 * 1024,
|
|
27
|
+
maxMarkdownBytes: 16 * 1024 * 1024,
|
|
28
|
+
maxMarkdownNodes: 25e4,
|
|
29
|
+
maxMarkdownDepth: 128,
|
|
30
|
+
maxTableCells: 1e5,
|
|
31
|
+
maxBlocks: 5e4,
|
|
32
|
+
maxBlockDepth: 128,
|
|
33
|
+
maxDurationSeconds: 24 * 60 * 60,
|
|
34
|
+
maxAudioSegments: 1e4
|
|
35
|
+
});
|
|
36
|
+
function resolveConversionLimits(limits) {
|
|
37
|
+
const resolved = { ...DEFAULT_CONVERSION_LIMITS, ...limits };
|
|
38
|
+
for (const [name, value] of Object.entries(resolved)) {
|
|
39
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
40
|
+
throw new RangeError(`${name} must be a non-negative safe integer`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
|
45
|
+
function markdownLimitsFromConversion(limits) {
|
|
46
|
+
if (limits === false) return false;
|
|
47
|
+
const resolved = resolveConversionLimits(limits);
|
|
48
|
+
return {
|
|
49
|
+
maxSourceBytes: resolved.maxMarkdownBytes,
|
|
50
|
+
maxNodes: resolved.maxMarkdownNodes,
|
|
51
|
+
maxDepth: resolved.maxMarkdownDepth,
|
|
52
|
+
maxTableCells: resolved.maxTableCells
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function assertDocWithinConversionLimits(doc, limits, signal) {
|
|
56
|
+
if (limits === false) return;
|
|
57
|
+
const resolved = resolveConversionLimits(limits);
|
|
58
|
+
if (!Number.isFinite(doc.duration) || doc.duration < 0 || doc.duration > resolved.maxDurationSeconds) {
|
|
59
|
+
throw new RangeError(
|
|
60
|
+
`Document duration must be between 0 and ${resolved.maxDurationSeconds} seconds`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (doc.audio.segments.length > resolved.maxAudioSegments) {
|
|
64
|
+
throw new RangeError(
|
|
65
|
+
`Document exceeds the ${resolved.maxAudioSegments}-audio-segment safety limit`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
let audioDuration = 0;
|
|
69
|
+
for (const segment of doc.audio.segments) {
|
|
70
|
+
if (!Number.isFinite(segment.duration) || segment.duration < 0) {
|
|
71
|
+
throw new RangeError("Audio segment durations must be non-negative finite numbers");
|
|
72
|
+
}
|
|
73
|
+
audioDuration += segment.duration;
|
|
74
|
+
if (audioDuration > resolved.maxDurationSeconds) {
|
|
75
|
+
throw new RangeError(
|
|
76
|
+
`Narration exceeds the ${resolved.maxDurationSeconds}-second safety limit`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const stack = [];
|
|
81
|
+
for (let index = doc.blocks.length - 1; index >= 0; index--) {
|
|
82
|
+
stack.push({ block: doc.blocks[index], depth: 0 });
|
|
83
|
+
}
|
|
84
|
+
let count = 0;
|
|
85
|
+
while (stack.length > 0) {
|
|
86
|
+
if ((count & 1023) === 0) signal?.throwIfAborted();
|
|
87
|
+
const { block, depth } = stack.pop();
|
|
88
|
+
if (++count > resolved.maxBlocks) {
|
|
89
|
+
throw new RangeError(`Document exceeds the ${resolved.maxBlocks}-block safety limit`);
|
|
90
|
+
}
|
|
91
|
+
if (depth > resolved.maxBlockDepth) {
|
|
92
|
+
throw new RangeError(
|
|
93
|
+
`Document exceeds the ${resolved.maxBlockDepth}-level block nesting safety limit`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
for (let index = (block.children?.length ?? 0) - 1; index >= 0; index--) {
|
|
97
|
+
stack.push({ block: block.children[index], depth: depth + 1 });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
23
102
|
// src/shared/fidelity.ts
|
|
24
103
|
var CONTENT_NODE_TYPES = {
|
|
25
104
|
heading: true,
|
|
@@ -86,7 +165,7 @@ var PROFILE_LABELS = {
|
|
|
86
165
|
pptx: "PPTX",
|
|
87
166
|
epub: "EPUB"
|
|
88
167
|
};
|
|
89
|
-
function markdownFidelityWarnings(
|
|
168
|
+
function markdownFidelityWarnings(document, profile) {
|
|
90
169
|
const supported = PROFILE_SUPPORT[profile];
|
|
91
170
|
const omitted = /* @__PURE__ */ new Map();
|
|
92
171
|
const visit = (candidate) => {
|
|
@@ -103,7 +182,7 @@ function markdownFidelityWarnings(document2, profile) {
|
|
|
103
182
|
for (const child of node.children) visit(child);
|
|
104
183
|
}
|
|
105
184
|
};
|
|
106
|
-
for (const child of
|
|
185
|
+
for (const child of document.children) visit(child);
|
|
107
186
|
if (omitted.size === 0) return [];
|
|
108
187
|
const total = [...omitted.values()].reduce((sum, count) => sum + count, 0);
|
|
109
188
|
const details = [...omitted].map(([type, count]) => `${type} (${count})`).join(", ");
|
|
@@ -142,17 +221,22 @@ function resolveThemeId(input, options) {
|
|
|
142
221
|
return options.themeId ?? input.doc.themeId;
|
|
143
222
|
}
|
|
144
223
|
function optionsFor(options, id) {
|
|
145
|
-
return
|
|
224
|
+
return {
|
|
225
|
+
...options.formatOptions?.[id] ?? {},
|
|
226
|
+
...options.signal ? { signal: options.signal } : {}
|
|
227
|
+
};
|
|
146
228
|
}
|
|
147
229
|
async function markdownOf(input) {
|
|
148
230
|
if (input.markdownDoc) return input.markdownDoc;
|
|
149
231
|
const { docToMarkdown } = await import("@bendyline/squisq/doc");
|
|
150
232
|
return docToMarkdown(input.doc);
|
|
151
233
|
}
|
|
152
|
-
async function collectContainerImages(container) {
|
|
234
|
+
async function collectContainerImages(container, signal) {
|
|
153
235
|
const images = /* @__PURE__ */ new Map();
|
|
154
236
|
const files = await container.listFiles();
|
|
155
|
-
for (
|
|
237
|
+
for (let index = 0; index < files.length; index++) {
|
|
238
|
+
if ((index & 63) === 0) signal?.throwIfAborted();
|
|
239
|
+
const file = files[index];
|
|
156
240
|
if (!/\.(jpg|jpeg|png|gif|webp|svg|bmp|avif)$/i.test(file.path)) continue;
|
|
157
241
|
const data = await container.readFile(file.path);
|
|
158
242
|
if (!data) continue;
|
|
@@ -162,9 +246,9 @@ async function collectContainerImages(container) {
|
|
|
162
246
|
}
|
|
163
247
|
return images;
|
|
164
248
|
}
|
|
165
|
-
async function collectContainerDocxImages(container) {
|
|
166
|
-
const images = await collectContainerImages(container);
|
|
167
|
-
const { extToMime } = await import("./images-
|
|
249
|
+
async function collectContainerDocxImages(container, signal) {
|
|
250
|
+
const images = await collectContainerImages(container, signal);
|
|
251
|
+
const { extToMime } = await import("./images-ESPQKVTW.js");
|
|
168
252
|
return new Map(
|
|
169
253
|
[...images].map(([path, data]) => [
|
|
170
254
|
path,
|
|
@@ -172,11 +256,13 @@ async function collectContainerDocxImages(container) {
|
|
|
172
256
|
])
|
|
173
257
|
);
|
|
174
258
|
}
|
|
175
|
-
async function snapshotContainerWithDocument(input) {
|
|
259
|
+
async function snapshotContainerWithDocument(input, signal) {
|
|
176
260
|
const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
|
|
177
261
|
const snapshot = new MemoryContentContainer();
|
|
178
262
|
const entries = await input.container.listFiles();
|
|
179
|
-
for (
|
|
263
|
+
for (let index = 0; index < entries.length; index++) {
|
|
264
|
+
if ((index & 63) === 0) signal?.throwIfAborted();
|
|
265
|
+
const entry = entries[index];
|
|
180
266
|
const data = await input.container.readFile(entry.path);
|
|
181
267
|
if (data) await snapshot.writeFile(entry.path, new Uint8Array(data), entry.mimeType);
|
|
182
268
|
}
|
|
@@ -214,11 +300,21 @@ function defaultFormats() {
|
|
|
214
300
|
extensions: [".md", ".markdown"],
|
|
215
301
|
async importDoc(data, options) {
|
|
216
302
|
const { parseMarkdown } = await import("@bendyline/squisq/markdown");
|
|
217
|
-
|
|
303
|
+
const raw = optionsFor(options, "md").parse ?? {};
|
|
304
|
+
return parseMarkdown(new TextDecoder().decode(data), {
|
|
305
|
+
...raw,
|
|
306
|
+
signal: options.signal,
|
|
307
|
+
limits: raw.limits ?? markdownLimitsFromConversion(options.limits)
|
|
308
|
+
});
|
|
218
309
|
},
|
|
219
310
|
async exportDoc(input, options) {
|
|
220
311
|
const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
221
|
-
const
|
|
312
|
+
const raw = optionsFor(options, "md").stringify ?? {};
|
|
313
|
+
const text = stringifyMarkdown(await markdownOf(input), {
|
|
314
|
+
...raw,
|
|
315
|
+
signal: options.signal,
|
|
316
|
+
limits: raw.limits ?? markdownLimitsFromConversion(options.limits)
|
|
317
|
+
});
|
|
222
318
|
return ok(new TextEncoder().encode(text), MIME.md);
|
|
223
319
|
}
|
|
224
320
|
};
|
|
@@ -239,7 +335,7 @@ function defaultFormats() {
|
|
|
239
335
|
const { markdownDocToDocx } = await import("./docx/index.js");
|
|
240
336
|
const raw = optionsFor(options, "docx");
|
|
241
337
|
const markdownDoc = await markdownOf(input);
|
|
242
|
-
const containerImages = await collectContainerDocxImages(input.container);
|
|
338
|
+
const containerImages = await collectContainerDocxImages(input.container, options.signal);
|
|
243
339
|
const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
|
|
244
340
|
const buf = await markdownDocToDocx(markdownDoc, {
|
|
245
341
|
...raw,
|
|
@@ -268,13 +364,18 @@ function defaultFormats() {
|
|
|
268
364
|
const { markdownDocToPdf } = await import("./pdf/index.js");
|
|
269
365
|
const raw = optionsFor(options, "pdf");
|
|
270
366
|
const markdownDoc = await markdownOf(input);
|
|
367
|
+
const pdfWarnings = [];
|
|
271
368
|
const buf = await markdownDocToPdf(markdownDoc, {
|
|
272
369
|
...raw,
|
|
273
370
|
...options.title !== void 0 ? { title: options.title } : {},
|
|
274
371
|
themeId: resolveThemeId(input, options),
|
|
275
|
-
themeRegistry: options.themeRegistry ?? raw.themeRegistry
|
|
372
|
+
themeRegistry: options.themeRegistry ?? raw.themeRegistry,
|
|
373
|
+
onWarning: (message) => pdfWarnings.push(message)
|
|
276
374
|
});
|
|
277
|
-
return ok(await toBytes(buf), MIME.pdf,
|
|
375
|
+
return ok(await toBytes(buf), MIME.pdf, [
|
|
376
|
+
...markdownFidelityWarnings(markdownDoc, "pdf"),
|
|
377
|
+
...pdfWarnings
|
|
378
|
+
]);
|
|
278
379
|
}
|
|
279
380
|
};
|
|
280
381
|
const pptx = {
|
|
@@ -294,7 +395,7 @@ function defaultFormats() {
|
|
|
294
395
|
const { markdownDocToPptx } = await import("./pptx/index.js");
|
|
295
396
|
const raw = optionsFor(options, "pptx");
|
|
296
397
|
const markdownDoc = await markdownOf(input);
|
|
297
|
-
const containerImages = await collectContainerImages(input.container);
|
|
398
|
+
const containerImages = await collectContainerImages(input.container, options.signal);
|
|
298
399
|
const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
|
|
299
400
|
const buf = await markdownDocToPptx(markdownDoc, {
|
|
300
401
|
...raw,
|
|
@@ -368,7 +469,7 @@ function defaultFormats() {
|
|
|
368
469
|
const playerScript = await requirePlayerScript(options, "html");
|
|
369
470
|
const { docToHtml } = await import("./html/index.js");
|
|
370
471
|
const raw = optionsFor(options, "html");
|
|
371
|
-
const containerImages = await collectContainerImages(input.container);
|
|
472
|
+
const containerImages = await collectContainerImages(input.container, options.signal);
|
|
372
473
|
const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
|
|
373
474
|
const htmlText = docToHtml(input.doc, {
|
|
374
475
|
...raw,
|
|
@@ -391,7 +492,7 @@ function defaultFormats() {
|
|
|
391
492
|
const playerScript = await requirePlayerScript(options, "htmlzip");
|
|
392
493
|
const { docToHtmlZip } = await import("./html/index.js");
|
|
393
494
|
const raw = optionsFor(options, "htmlzip");
|
|
394
|
-
const containerImages = await collectContainerImages(input.container);
|
|
495
|
+
const containerImages = await collectContainerImages(input.container, options.signal);
|
|
395
496
|
const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
|
|
396
497
|
const blob = await docToHtmlZip(input.doc, {
|
|
397
498
|
...raw,
|
|
@@ -414,7 +515,7 @@ function defaultFormats() {
|
|
|
414
515
|
const { markdownDocToEpub } = await import("./epub/index.js");
|
|
415
516
|
const raw = optionsFor(options, "epub");
|
|
416
517
|
const markdownDoc = await markdownOf(input);
|
|
417
|
-
const containerImages = await collectContainerImages(input.container);
|
|
518
|
+
const containerImages = await collectContainerImages(input.container, options.signal);
|
|
418
519
|
const images = new Map([...containerImages, ...raw.images ?? /* @__PURE__ */ new Map()]);
|
|
419
520
|
const buf = await markdownDocToEpub(markdownDoc, {
|
|
420
521
|
...raw,
|
|
@@ -438,7 +539,7 @@ function defaultFormats() {
|
|
|
438
539
|
async exportDoc(input, options) {
|
|
439
540
|
const { containerToZip } = await import("./container/index.js");
|
|
440
541
|
const blob = await containerToZip(
|
|
441
|
-
await snapshotContainerWithDocument(input),
|
|
542
|
+
await snapshotContainerWithDocument(input, options.signal),
|
|
442
543
|
optionsFor(options, "dbk")
|
|
443
544
|
);
|
|
444
545
|
return ok(await toBytes(blob), MIME.zip);
|
|
@@ -485,6 +586,22 @@ function defaultRegistry() {
|
|
|
485
586
|
function markdownFormatOptions(options) {
|
|
486
587
|
return options.formatOptions?.md ?? {};
|
|
487
588
|
}
|
|
589
|
+
function parseOptions(options) {
|
|
590
|
+
const raw = markdownFormatOptions(options).parse ?? {};
|
|
591
|
+
return {
|
|
592
|
+
...raw,
|
|
593
|
+
signal: options.signal,
|
|
594
|
+
limits: raw.limits ?? markdownLimitsFromConversion(options.limits)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function stringifyOptions(options) {
|
|
598
|
+
const raw = markdownFormatOptions(options).stringify ?? {};
|
|
599
|
+
return {
|
|
600
|
+
...raw,
|
|
601
|
+
signal: options.signal,
|
|
602
|
+
limits: raw.limits ?? markdownLimitsFromConversion(options.limits)
|
|
603
|
+
};
|
|
604
|
+
}
|
|
488
605
|
function throwIfAborted(signal) {
|
|
489
606
|
if (!signal?.aborted) return;
|
|
490
607
|
if (signal.reason !== void 0) throw signal.reason;
|
|
@@ -501,11 +618,26 @@ function hasPrefix(bytes, prefix) {
|
|
|
501
618
|
}
|
|
502
619
|
var ZIP_MAGIC = [80, 75, 3, 4];
|
|
503
620
|
var PDF_MAGIC = [37, 80, 68, 70];
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
621
|
+
var ZIP_SNIFF_CANDIDATES = ["docx", "pptx", "xlsx", "dbk"];
|
|
622
|
+
function sniffZipLimits(options) {
|
|
623
|
+
const effective = ZIP_SNIFF_CANDIDATES.map(
|
|
624
|
+
(id) => resolveZipSafetyLimits(options.formatOptions?.[id] ?? {})
|
|
625
|
+
);
|
|
626
|
+
return {
|
|
627
|
+
signal: options.signal,
|
|
628
|
+
maxEntries: Math.max(...effective.map((l) => l.maxEntries)),
|
|
629
|
+
maxUncompressedBytes: Math.max(...effective.map((l) => l.maxUncompressedBytes)),
|
|
630
|
+
maxEntryUncompressedBytes: Math.max(...effective.map((l) => l.maxEntryUncompressedBytes)),
|
|
631
|
+
maxCompressionRatio: Math.max(...effective.map((l) => l.maxCompressionRatio))
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
async function sniffZip(bytes, options) {
|
|
635
|
+
const archive = await openBoundedZipArchive(bytes, sniffZipLimits(options)).catch(
|
|
636
|
+
(cause) => {
|
|
637
|
+
if (cause instanceof ZipSafetyError && cause.code !== "invalid-archive") throw cause;
|
|
638
|
+
throw new ConversionError("invalid-input", "Input is not a readable ZIP archive.", { cause });
|
|
639
|
+
}
|
|
640
|
+
);
|
|
509
641
|
const contentTypes = archive.entries.find((entry) => entry.path === "[Content_Types].xml");
|
|
510
642
|
if (!contentTypes) return "dbk";
|
|
511
643
|
const maxContentTypesBytes = 1024 * 1024;
|
|
@@ -528,7 +660,7 @@ async function detectByteFormat(bytes, filename, options, registry) {
|
|
|
528
660
|
if (byExt) return byExt.id;
|
|
529
661
|
}
|
|
530
662
|
if (hasPrefix(bytes, PDF_MAGIC)) return "pdf";
|
|
531
|
-
if (hasPrefix(bytes, ZIP_MAGIC)) return sniffZip(bytes);
|
|
663
|
+
if (hasPrefix(bytes, ZIP_MAGIC)) return sniffZip(bytes, options);
|
|
532
664
|
return "md";
|
|
533
665
|
}
|
|
534
666
|
function extractExt(filename) {
|
|
@@ -546,6 +678,12 @@ function baseNameOf(filename) {
|
|
|
546
678
|
}
|
|
547
679
|
async function normalizeBytes(source, options, registry) {
|
|
548
680
|
const bytes = source.data instanceof Uint8Array ? source.data : new Uint8Array(source.data);
|
|
681
|
+
if (options.limits !== false && bytes.byteLength > resolveConversionLimits(options.limits).maxInputBytes) {
|
|
682
|
+
throw new ConversionError(
|
|
683
|
+
"invalid-input",
|
|
684
|
+
`Input exceeds the ${resolveConversionLimits(options.limits).maxInputBytes}-byte safety limit.`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
549
687
|
const buffer = bytes.buffer.slice(
|
|
550
688
|
bytes.byteOffset,
|
|
551
689
|
bytes.byteOffset + bytes.byteLength
|
|
@@ -574,21 +712,23 @@ async function normalizeBytes(source, options, registry) {
|
|
|
574
712
|
let container;
|
|
575
713
|
if (fromDef.importContainer) {
|
|
576
714
|
container = await fromDef.importContainer(buffer, options);
|
|
577
|
-
if (fromDef.id === "pdf" && typeof document === "undefined") {
|
|
578
|
-
warnings.push(
|
|
579
|
-
"PDF embedded images were skipped \u2014 image decoding requires a browser canvas (running under Node)."
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
715
|
const text = await container.readDocument();
|
|
583
|
-
markdownDoc = text ? parseMarkdown(text,
|
|
716
|
+
markdownDoc = text ? parseMarkdown(text, parseOptions(options)) : { type: "document", children: [] };
|
|
584
717
|
} else {
|
|
585
718
|
markdownDoc = await fromDef.importDoc(buffer, options);
|
|
586
719
|
const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
|
|
587
720
|
const mem = new MemoryContentContainer();
|
|
588
|
-
await mem.writeDocument(stringifyMarkdown(markdownDoc));
|
|
721
|
+
await mem.writeDocument(stringifyMarkdown(markdownDoc, stringifyOptions(options)));
|
|
589
722
|
container = mem;
|
|
590
723
|
}
|
|
724
|
+
const { assertMarkdownDocumentWithinLimits } = await import("@bendyline/squisq/markdown");
|
|
725
|
+
assertMarkdownDocumentWithinLimits(
|
|
726
|
+
markdownDoc,
|
|
727
|
+
markdownLimitsFromConversion(options.limits),
|
|
728
|
+
options.signal
|
|
729
|
+
);
|
|
591
730
|
const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });
|
|
731
|
+
assertDocWithinConversionLimits(doc, options.limits, options.signal);
|
|
592
732
|
const baseName = source.filename ? baseNameOf(source.filename) : "document";
|
|
593
733
|
return {
|
|
594
734
|
input: { doc, markdownDoc, container, baseName },
|
|
@@ -599,26 +739,36 @@ async function normalizeMarkdown(source, options) {
|
|
|
599
739
|
const { markdownToDoc } = await import("@bendyline/squisq/doc");
|
|
600
740
|
const { parseMarkdown } = await import("@bendyline/squisq/markdown");
|
|
601
741
|
const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
602
|
-
const markdownDoc = typeof source.markdown === "string" ? parseMarkdown(source.markdown,
|
|
742
|
+
const markdownDoc = typeof source.markdown === "string" ? parseMarkdown(source.markdown, parseOptions(options)) : source.markdown;
|
|
743
|
+
const { assertMarkdownDocumentWithinLimits } = await import("@bendyline/squisq/markdown");
|
|
744
|
+
assertMarkdownDocumentWithinLimits(
|
|
745
|
+
markdownDoc,
|
|
746
|
+
markdownLimitsFromConversion(options.limits),
|
|
747
|
+
options.signal
|
|
748
|
+
);
|
|
603
749
|
let container = source.container;
|
|
604
750
|
if (!container) {
|
|
605
751
|
const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
|
|
606
752
|
const mem = new MemoryContentContainer();
|
|
607
|
-
await mem.writeDocument(stringifyMarkdown(markdownDoc));
|
|
753
|
+
await mem.writeDocument(stringifyMarkdown(markdownDoc, stringifyOptions(options)));
|
|
608
754
|
container = mem;
|
|
609
755
|
}
|
|
610
756
|
const doc = markdownToDoc(markdownDoc, { autoTemplates: options.autoTemplates });
|
|
757
|
+
assertDocWithinConversionLimits(doc, options.limits, options.signal);
|
|
611
758
|
const baseName = source.baseName ?? "document";
|
|
612
759
|
return { input: { doc, markdownDoc, container, baseName }, warnings: [] };
|
|
613
760
|
}
|
|
614
|
-
async function normalizeDoc(source) {
|
|
761
|
+
async function normalizeDoc(source, options) {
|
|
762
|
+
assertDocWithinConversionLimits(source.doc, options.limits, options.signal);
|
|
615
763
|
let container = source.container;
|
|
616
764
|
if (!container) {
|
|
617
765
|
const { MemoryContentContainer } = await import("@bendyline/squisq/storage");
|
|
618
766
|
const { docToMarkdown } = await import("@bendyline/squisq/doc");
|
|
619
767
|
const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
620
768
|
const mem = new MemoryContentContainer();
|
|
621
|
-
await mem.writeDocument(
|
|
769
|
+
await mem.writeDocument(
|
|
770
|
+
stringifyMarkdown(docToMarkdown(source.doc), stringifyOptions(options))
|
|
771
|
+
);
|
|
622
772
|
container = mem;
|
|
623
773
|
}
|
|
624
774
|
const baseName = source.baseName ?? "document";
|
|
@@ -631,7 +781,7 @@ async function normalize(source, options, registry) {
|
|
|
631
781
|
case "markdown":
|
|
632
782
|
return normalizeMarkdown(source, options);
|
|
633
783
|
case "doc":
|
|
634
|
-
return normalizeDoc(source);
|
|
784
|
+
return normalizeDoc(source, options);
|
|
635
785
|
}
|
|
636
786
|
}
|
|
637
787
|
async function applyTransformStyle(input, transformStyle, themeId, registry) {
|
|
@@ -758,10 +908,11 @@ async function convert(source, to, options = {}) {
|
|
|
758
908
|
|
|
759
909
|
export {
|
|
760
910
|
BUILTIN_FORMAT_IDS,
|
|
911
|
+
DEFAULT_CONVERSION_LIMITS,
|
|
912
|
+
resolveConversionLimits,
|
|
761
913
|
defaultFormats,
|
|
762
914
|
createRegistry,
|
|
763
915
|
defaultRegistry,
|
|
764
916
|
prepareConversion,
|
|
765
917
|
convert
|
|
766
918
|
};
|
|
767
|
-
//# sourceMappingURL=chunk-WQSHGBLN.js.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
stripHtmlTags
|
|
3
|
+
} from "./chunk-SBUW7NHR.js";
|
|
4
|
+
|
|
5
|
+
// src/ooxml/relIds.ts
|
|
6
|
+
var RelIdAllocator = class {
|
|
7
|
+
constructor() {
|
|
8
|
+
/** part path → next free numeric suffix */
|
|
9
|
+
this.next = /* @__PURE__ */ new Map();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Allocate the next free relationship id for `part`'s rels file.
|
|
13
|
+
*
|
|
14
|
+
* @param part - Zip path of the part that owns the relationship
|
|
15
|
+
* (e.g. `"word/document.xml"`). Use `""` for the package root.
|
|
16
|
+
* Ids are only unique per `part`, which is exactly the OOXML rule.
|
|
17
|
+
*/
|
|
18
|
+
alloc(part = "") {
|
|
19
|
+
const n = this.next.get(part) ?? 1;
|
|
20
|
+
this.next.set(part, n + 1);
|
|
21
|
+
return `rId${n}`;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/shared/ooxmlColor.ts
|
|
26
|
+
var HEX3_RE = /^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/;
|
|
27
|
+
var HEX6_RE = /^#?([0-9a-fA-F]{6})$/;
|
|
28
|
+
var HEX8_RE = /^#?([0-9a-fA-F]{6})[0-9a-fA-F]{2}$/;
|
|
29
|
+
function toOoxmlHex(color, fallback = "000000") {
|
|
30
|
+
const normalized = normalizeOoxmlHex(color);
|
|
31
|
+
if (normalized) return normalized;
|
|
32
|
+
return normalizeOoxmlHex(fallback) ?? "000000";
|
|
33
|
+
}
|
|
34
|
+
function normalizeOoxmlHex(color) {
|
|
35
|
+
if (typeof color !== "string") return void 0;
|
|
36
|
+
const trimmed = color.trim();
|
|
37
|
+
const six = HEX6_RE.exec(trimmed);
|
|
38
|
+
if (six) return six[1].toUpperCase();
|
|
39
|
+
const eight = HEX8_RE.exec(trimmed);
|
|
40
|
+
if (eight) return eight[1].toUpperCase();
|
|
41
|
+
const three = HEX3_RE.exec(trimmed);
|
|
42
|
+
if (three) {
|
|
43
|
+
const [, r, g, b] = three;
|
|
44
|
+
return `${r}${r}${g}${g}${b}${b}`.toUpperCase();
|
|
45
|
+
}
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/shared/officeHyperlinks.ts
|
|
50
|
+
import { sanitizeUrl } from "@bendyline/squisq/markdown";
|
|
51
|
+
function sanitizeOfficeHyperlink(input, policy = {}) {
|
|
52
|
+
if (typeof input !== "string") return null;
|
|
53
|
+
const value = input.trim();
|
|
54
|
+
if (!value || hasControls(value)) return null;
|
|
55
|
+
if (value.startsWith("#")) return /^#[^\s]*$/.test(value) ? value : null;
|
|
56
|
+
if (/^(?:\\\\|\/\/|\/|[a-z]:[\\/])/i.test(value)) return null;
|
|
57
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:/i.test(value);
|
|
58
|
+
if (hasScheme) return sanitizeUrl(value);
|
|
59
|
+
if (!policy.allowRelative || value.includes("\\")) return null;
|
|
60
|
+
let decoded;
|
|
61
|
+
try {
|
|
62
|
+
decoded = decodeURIComponent(value);
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (hasControls(decoded) || decoded.includes("\\") || decoded.startsWith("//")) return null;
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
function hasControls(value) {
|
|
70
|
+
for (const char of value) {
|
|
71
|
+
const code = char.charCodeAt(0);
|
|
72
|
+
if (code <= 31 || code === 127) return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/shared/inlineRuns.ts
|
|
78
|
+
function inlineNodesToRuns(nodes, handlers, format = {}) {
|
|
79
|
+
let out = "";
|
|
80
|
+
for (const node of nodes) {
|
|
81
|
+
out += inlineNodeToRuns(node, handlers, format);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
function inlineNodeToRuns(node, handlers, format) {
|
|
86
|
+
switch (node.type) {
|
|
87
|
+
case "text":
|
|
88
|
+
return handlers.run(node.value, format);
|
|
89
|
+
case "strong":
|
|
90
|
+
return inlineNodesToRuns(node.children, handlers, { ...format, bold: true });
|
|
91
|
+
case "emphasis":
|
|
92
|
+
return inlineNodesToRuns(node.children, handlers, { ...format, italic: true });
|
|
93
|
+
case "delete":
|
|
94
|
+
return inlineNodesToRuns(node.children, handlers, { ...format, strike: true });
|
|
95
|
+
case "inlineCode":
|
|
96
|
+
return handlers.run(node.value, { ...format, code: true });
|
|
97
|
+
case "inlineMath":
|
|
98
|
+
return handlers.run(node.value, { ...format, code: true });
|
|
99
|
+
case "htmlInline":
|
|
100
|
+
return handlers.run(stripHtmlTags(node.rawHtml), format);
|
|
101
|
+
case "link":
|
|
102
|
+
return handlers.link(node, format);
|
|
103
|
+
case "image":
|
|
104
|
+
return handlers.image(node, format);
|
|
105
|
+
case "break":
|
|
106
|
+
return handlers.lineBreak();
|
|
107
|
+
case "footnoteReference":
|
|
108
|
+
return handlers.footnoteRef ? handlers.footnoteRef(node) : "";
|
|
109
|
+
default:
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
RelIdAllocator,
|
|
116
|
+
toOoxmlHex,
|
|
117
|
+
normalizeOoxmlHex,
|
|
118
|
+
sanitizeOfficeHyperlink,
|
|
119
|
+
inlineNodesToRuns,
|
|
120
|
+
inlineNodeToRuns
|
|
121
|
+
};
|