@bendyline/squisq-formats 2.1.0 → 2.2.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 +20 -0
- package/README.md +1 -1
- package/dist/{chunk-NNHKUXKA.js → chunk-26ISNJ7Y.js} +85 -65
- package/dist/{chunk-NKAJPJ4G.js → chunk-2JJ5RFDZ.js} +0 -1
- package/dist/{chunk-WQSHGBLN.js → chunk-3NKXBZSR.js} +193 -42
- 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-GX7RAUME.js +121 -0
- 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-GVS2XXV6.js → chunk-PJXJI2LY.js} +449 -57
- 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 +6 -3
- 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
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
escapeXml,
|
|
3
3
|
xmlDeclaration
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JU2RHXUB.js";
|
|
5
5
|
import {
|
|
6
6
|
CONTENT_TYPE_CORE_PROPERTIES,
|
|
7
7
|
CONTENT_TYPE_RELATIONSHIPS,
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
NS_RELATIONSHIPS,
|
|
13
13
|
NS_XSI,
|
|
14
14
|
REL_CORE_PROPERTIES
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-X2DEAXNK.js";
|
|
16
16
|
import {
|
|
17
17
|
assertSafeZipPath
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7AWFHP5U.js";
|
|
19
19
|
|
|
20
20
|
// src/ooxml/writer.ts
|
|
21
21
|
import JSZip from "jszip";
|
|
@@ -192,4 +192,3 @@ function groupRelationshipsBySource(pending) {
|
|
|
192
192
|
export {
|
|
193
193
|
createPackage
|
|
194
194
|
};
|
|
195
|
-
//# sourceMappingURL=chunk-KURGXM4I.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extToMime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AONELFLA.js";
|
|
4
4
|
|
|
5
5
|
// src/html/imageUtils.ts
|
|
6
6
|
function inferMimeType(filename) {
|
|
@@ -31,4 +31,3 @@ export {
|
|
|
31
31
|
arrayBufferToBase64DataUrl,
|
|
32
32
|
extractFilename
|
|
33
33
|
};
|
|
34
|
-
//# sourceMappingURL=chunk-MLX2BOJC.js.map
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ConversionError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KXOZMWBS.js";
|
|
4
4
|
import {
|
|
5
5
|
extractDocxTheme,
|
|
6
6
|
extractPptxTheme,
|
|
7
7
|
extractXlsxTheme
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PU7REGWV.js";
|
|
9
9
|
import {
|
|
10
10
|
colorHintsFromExtraction,
|
|
11
11
|
compileExtractedTheme
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-2JJ5RFDZ.js";
|
|
13
13
|
import {
|
|
14
14
|
openPackage,
|
|
15
15
|
throwIfOoxmlAborted
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-X2DEAXNK.js";
|
|
17
17
|
|
|
18
18
|
// src/infer/index.ts
|
|
19
19
|
async function looksLikePdf(data, signal) {
|
|
@@ -107,7 +107,7 @@ async function inferThemeFromFile(data, options = {}) {
|
|
|
107
107
|
if (format !== "pptx") {
|
|
108
108
|
warnings.push("Layout inference is only available for PowerPoint files; skipped.");
|
|
109
109
|
} else {
|
|
110
|
-
const { analyzePptxLayouts } = await import("./layouts-
|
|
110
|
+
const { analyzePptxLayouts } = await import("./layouts-DRWZGSPD.js");
|
|
111
111
|
const analysis = await analyzePptxLayouts(pkg, {
|
|
112
112
|
colors: colorHintsFromExtraction(extraction),
|
|
113
113
|
signal: options.signal
|
|
@@ -124,4 +124,3 @@ async function inferThemeFromFile(data, options = {}) {
|
|
|
124
124
|
export {
|
|
125
125
|
inferThemeFromFile
|
|
126
126
|
};
|
|
127
|
-
//# sourceMappingURL=chunk-EW54IRRS.js.map
|
|
@@ -1,27 +1,54 @@
|
|
|
1
1
|
// src/csv/index.ts
|
|
2
2
|
import { markdownToDoc } from "@bendyline/squisq/doc";
|
|
3
|
+
var DEFAULT_MAX_CSV_CELLS = 1e5;
|
|
4
|
+
var DEFAULT_MAX_CSV_ROWS = 1e4;
|
|
5
|
+
var DEFAULT_MAX_CSV_FIELD_CHARS = 1024 * 1024;
|
|
3
6
|
async function toText(data) {
|
|
4
7
|
if (typeof data === "string") return data;
|
|
5
8
|
if (typeof Blob !== "undefined" && data instanceof Blob) return data.text();
|
|
6
9
|
return new TextDecoder().decode(new Uint8Array(data));
|
|
7
10
|
}
|
|
8
|
-
function
|
|
11
|
+
function validateDelimiter(delimiter) {
|
|
12
|
+
if ([...delimiter].length !== 1 || /["\r\n\uFEFF]/u.test(delimiter)) {
|
|
13
|
+
throw new TypeError(
|
|
14
|
+
"CSV delimiter must be exactly one character and cannot be a quote, line break, or BOM"
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return delimiter;
|
|
18
|
+
}
|
|
19
|
+
function parseCsv(text, delimiter = ",", limits = {}) {
|
|
20
|
+
delimiter = validateDelimiter(delimiter);
|
|
21
|
+
const maxCells = safetyInteger("maxCells", limits.maxCells ?? DEFAULT_MAX_CSV_CELLS);
|
|
22
|
+
const maxRows = safetyInteger("maxRows", limits.maxRows ?? DEFAULT_MAX_CSV_ROWS);
|
|
23
|
+
const maxFieldChars = safetyInteger(
|
|
24
|
+
"maxFieldChars",
|
|
25
|
+
limits.maxFieldChars ?? DEFAULT_MAX_CSV_FIELD_CHARS
|
|
26
|
+
);
|
|
9
27
|
const rows = [];
|
|
10
28
|
let row = [];
|
|
11
29
|
let field = "";
|
|
12
30
|
let inQuotes = false;
|
|
13
31
|
let sawAny = false;
|
|
32
|
+
let cellCount = 0;
|
|
14
33
|
const pushField = () => {
|
|
34
|
+
if (++cellCount > maxCells)
|
|
35
|
+
throw new RangeError(`CSV exceeds the ${maxCells}-cell safety limit`);
|
|
15
36
|
row.push(field);
|
|
16
37
|
field = "";
|
|
17
38
|
};
|
|
18
39
|
const pushRow = () => {
|
|
40
|
+
if (rows.length >= maxRows) throw new RangeError(`CSV exceeds the ${maxRows}-row safety limit`);
|
|
19
41
|
pushField();
|
|
20
42
|
rows.push(row);
|
|
21
43
|
row = [];
|
|
22
44
|
};
|
|
23
45
|
for (let i = 0; i < text.length; i++) {
|
|
24
|
-
|
|
46
|
+
if ((i & 16383) === 0) limits.signal?.throwIfAborted();
|
|
47
|
+
let ch = text[i];
|
|
48
|
+
if (delimiter.length === 2 && text.slice(i, i + 2) === delimiter) {
|
|
49
|
+
ch = delimiter;
|
|
50
|
+
i++;
|
|
51
|
+
}
|
|
25
52
|
if (inQuotes) {
|
|
26
53
|
if (ch === '"') {
|
|
27
54
|
if (text[i + 1] === '"') {
|
|
@@ -32,6 +59,9 @@ function parseCsv(text, delimiter = ",") {
|
|
|
32
59
|
}
|
|
33
60
|
} else {
|
|
34
61
|
field += ch;
|
|
62
|
+
if (field.length > maxFieldChars) {
|
|
63
|
+
throw new RangeError(`CSV field exceeds the ${maxFieldChars}-character safety limit`);
|
|
64
|
+
}
|
|
35
65
|
}
|
|
36
66
|
continue;
|
|
37
67
|
}
|
|
@@ -53,10 +83,19 @@ function parseCsv(text, delimiter = ",") {
|
|
|
53
83
|
}
|
|
54
84
|
sawAny = true;
|
|
55
85
|
field += ch;
|
|
86
|
+
if (field.length > maxFieldChars) {
|
|
87
|
+
throw new RangeError(`CSV field exceeds the ${maxFieldChars}-character safety limit`);
|
|
88
|
+
}
|
|
56
89
|
}
|
|
57
90
|
if (field !== "" || row.length > 0 || sawAny) pushRow();
|
|
58
91
|
return rows;
|
|
59
92
|
}
|
|
93
|
+
function safetyInteger(name, value) {
|
|
94
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
95
|
+
throw new RangeError(`${name} must be a non-negative safe integer`);
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
60
99
|
function rowsToTable(rows, hasHeader) {
|
|
61
100
|
const maxCols = rows.reduce((m, r) => Math.max(m, r.length), 1);
|
|
62
101
|
const mdRows = rows.map((cells, rowIdx) => {
|
|
@@ -74,8 +113,8 @@ function rowsToTable(rows, hasHeader) {
|
|
|
74
113
|
return { type: "table", children: mdRows };
|
|
75
114
|
}
|
|
76
115
|
async function csvToMarkdownDoc(data, options = {}) {
|
|
77
|
-
const text = await toText(data);
|
|
78
|
-
const rows = parseCsv(text, options.delimiter ?? ",");
|
|
116
|
+
const text = (await toText(data)).replace(/^\uFEFF/, "");
|
|
117
|
+
const rows = parseCsv(text, options.delimiter ?? ",", options);
|
|
79
118
|
const hasHeader = options.hasHeader ?? true;
|
|
80
119
|
const children = rows.length > 0 ? [rowsToTable(rows, hasHeader)] : [];
|
|
81
120
|
return { type: "document", children };
|
|
@@ -89,6 +128,11 @@ function escapeCsvField(value, delimiter) {
|
|
|
89
128
|
}
|
|
90
129
|
return value;
|
|
91
130
|
}
|
|
131
|
+
var SPREADSHEET_FORMULA_PREFIX = /^[\t\r\n \uFEFF]*[=+\-@]/;
|
|
132
|
+
function neutralizeSpreadsheetFormula(value, handling) {
|
|
133
|
+
if (handling === "preserve" || !SPREADSHEET_FORMULA_PREFIX.test(value)) return value;
|
|
134
|
+
return `'${value}`;
|
|
135
|
+
}
|
|
92
136
|
function cellText(cell) {
|
|
93
137
|
const walk = (nodes) => nodes.map((n) => {
|
|
94
138
|
const node = n;
|
|
@@ -99,7 +143,8 @@ function cellText(cell) {
|
|
|
99
143
|
return walk(cell.children);
|
|
100
144
|
}
|
|
101
145
|
function markdownDocToCsv(doc, options = {}) {
|
|
102
|
-
const delimiter = options.delimiter ?? ",";
|
|
146
|
+
const delimiter = validateDelimiter(options.delimiter ?? ",");
|
|
147
|
+
const formulaHandling = options.formulaHandling ?? "escape";
|
|
103
148
|
const tables = doc.children.filter((n) => n.type === "table");
|
|
104
149
|
const index = options.tableIndex ?? 0;
|
|
105
150
|
if (options.tableIndex !== void 0 && (!Number.isInteger(index) || index < 0 || index >= tables.length)) {
|
|
@@ -109,9 +154,15 @@ function markdownDocToCsv(doc, options = {}) {
|
|
|
109
154
|
}
|
|
110
155
|
const table = tables[index];
|
|
111
156
|
if (!table) return "";
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
157
|
+
const maxCells = safetyInteger("maxCells", options.maxCells ?? DEFAULT_MAX_CSV_CELLS);
|
|
158
|
+
const cellCount = table.children.reduce((count, row) => count + row.children.length, 0);
|
|
159
|
+
if (cellCount > maxCells) throw new RangeError(`CSV exceeds the ${maxCells}-cell safety limit`);
|
|
160
|
+
return table.children.map((row, rowIndex) => {
|
|
161
|
+
if ((rowIndex & 255) === 0) options.signal?.throwIfAborted();
|
|
162
|
+
return row.children.map(
|
|
163
|
+
(cell) => escapeCsvField(neutralizeSpreadsheetFormula(cellText(cell), formulaHandling), delimiter)
|
|
164
|
+
).join(delimiter);
|
|
165
|
+
}).join("\r\n");
|
|
115
166
|
}
|
|
116
167
|
|
|
117
168
|
export {
|
|
@@ -120,4 +171,3 @@ export {
|
|
|
120
171
|
csvToDoc,
|
|
121
172
|
markdownDocToCsv
|
|
122
173
|
};
|
|
123
|
-
//# sourceMappingURL=chunk-RFAPOKHJ.js.map
|