@stll/folio-core 0.11.0 → 0.13.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/dist/ai-edits/apply.d.ts +2 -1
- package/dist/ai-edits/apply.js +693 -63
- package/dist/ai-edits/block-identity.d.ts +4 -0
- package/dist/ai-edits/block-identity.js +9 -0
- package/dist/ai-edits/blockRange.d.ts +28 -1
- package/dist/ai-edits/blockRange.js +68 -2
- package/dist/ai-edits/clean-text.js +2 -1
- package/dist/ai-edits/headless.d.ts +4 -4
- package/dist/ai-edits/headless.js +9 -9
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.d.ts +7 -7
- package/dist/ai-edits/read.js +110 -3
- package/dist/ai-edits/snapshot.js +21 -3
- package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
- package/dist/ai-edits/table-cell-mutations.js +238 -0
- package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
- package/dist/ai-edits/table-mutation-plan.js +90 -0
- package/dist/ai-edits/table-row-column-mutations.d.ts +108 -0
- package/dist/ai-edits/table-row-column-mutations.js +300 -0
- package/dist/ai-edits/table-targets.d.ts +32 -0
- package/dist/ai-edits/table-targets.js +70 -0
- package/dist/ai-edits/types.d.ts +62 -1
- package/dist/ai-edits/word-diff.js +21 -0
- package/dist/compat/eigenpal.d.ts +3 -3
- package/dist/compat/eigenpal.js +3 -3
- package/dist/content-controls/mutateContentControls.js +1 -1
- package/dist/controller/activeEditorStory.d.ts +36 -0
- package/dist/controller/activeEditorStory.js +30 -0
- package/dist/controller/contentControlWidgetController.d.ts +55 -0
- package/dist/controller/contentControlWidgetController.js +141 -0
- package/dist/controller/fontReadiness.js +2 -0
- package/dist/controller/headerFooterEditorManager.js +2 -2
- package/dist/controller/hiddenEditorApi.js +2 -2
- package/dist/controller/hiddenEditorManager.d.ts +12 -1
- package/dist/controller/hiddenEditorManager.js +44 -12
- package/dist/controller/layoutPipeline.d.ts +2 -0
- package/dist/controller/layoutPipeline.js +17 -2
- package/dist/controller/noteEditorManager.d.ts +36 -0
- package/dist/controller/noteEditorManager.js +210 -0
- package/dist/document-operations.d.ts +43 -11
- package/dist/document-operations.js +186 -18
- package/dist/docx/blockContentParser.js +21 -7
- package/dist/docx/commentParser.js +6 -1
- package/dist/docx/commentRangeIntegrity.d.ts +6 -0
- package/dist/docx/commentRangeIntegrity.js +186 -0
- package/dist/docx/conformance.js +62 -1
- package/dist/docx/documentGrid.d.ts +8 -0
- package/dist/docx/documentGrid.js +8 -0
- package/dist/docx/drawingUtils.js +1 -1
- package/dist/docx/encryption/agileDecryption.js +12 -2
- package/dist/docx/encryption/compoundFile.js +6 -1
- package/dist/docx/encryption/encryptionInfo.d.ts +9 -1
- package/dist/docx/encryption/encryptionInfo.js +18 -2
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/ensureParaIds.js +8 -7
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/groupDrawingParser.js +10 -5
- package/dist/docx/hyperlinkParser.js +1 -1
- package/dist/docx/imageParser.js +10 -6
- package/dist/docx/metadataPrivacy.js +17 -3
- package/dist/docx/notePropertiesParser.js +1 -1
- package/dist/docx/numberingParser.js +1 -1
- package/dist/docx/paragraphParser.js +70 -61
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/parser.js +3 -3
- package/dist/docx/rezip.js +20 -9
- package/dist/docx/runParser.js +17 -6
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sectionParser.js +18 -5
- package/dist/docx/selectiveSave.js +5 -1
- package/dist/docx/selectiveXmlPatch.d.ts +22 -1
- package/dist/docx/selectiveXmlPatch.js +61 -1
- package/dist/docx/serializer/blockSdtSerializer.js +3 -2
- package/dist/docx/serializer/commentSerializer.js +5 -6
- package/dist/docx/serializer/documentSerializer.js +2 -2
- package/dist/docx/serializer/headerFooterSerializer.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +2 -2
- package/dist/docx/serializer/paragraphSerializer.js +23 -15
- package/dist/docx/serializer/runSerializer.js +25 -21
- package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -1
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/tableSerializer.d.ts +5 -4
- package/dist/docx/serializer/tableSerializer.js +26 -20
- package/dist/docx/serializer/xmlUtils.d.ts +13 -1
- package/dist/docx/serializer/xmlUtils.js +27 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
- package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
- package/dist/docx/server/boundedArchive.d.ts +14 -3
- package/dist/docx/server/boundedArchive.js +56 -11
- package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
- package/dist/docx/server/extractDocxText.js +55 -8
- package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
- package/dist/docx/server/inspectDocxPackage.js +217 -0
- package/dist/docx/server/validateDocxConformance.d.ts +41 -0
- package/dist/docx/server/validateDocxConformance.js +336 -0
- package/dist/docx/settingsParser.js +26 -3
- package/dist/docx/shapeParser.js +1 -1
- package/dist/docx/styleParser.js +4 -2
- package/dist/docx/tableParser.d.ts +8 -1
- package/dist/docx/tableParser.js +31 -10
- package/dist/docx/textBoxParser.d.ts +3 -3
- package/dist/docx/textBoxParser.js +16 -12
- package/dist/docx/unzip.js +1 -0
- package/dist/docx/vmlImageParser.js +11 -3
- package/dist/docx/xmlParser.js +11 -1
- package/dist/docx/xmlSafety.d.ts +5 -0
- package/dist/docx/xmlSafety.js +9 -0
- package/dist/fields/resolveFieldValues.js +1 -3
- package/dist/fonts/embeddedFonts.d.ts +33 -5
- package/dist/fonts/embeddedFonts.js +37 -8
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +151 -20
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
- package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
- package/dist/layout-bridge/engine/clickToPosition.js +2 -2
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +58 -30
- package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
- package/dist/layout-engine/justificationCompatibility.js +8 -0
- package/dist/layout-engine/keep-together.js +11 -1
- package/dist/layout-engine/measure/cache.js +23 -5
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +8 -2
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +2 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +56 -37
- package/dist/layout-engine/measure/measureBlocks.js +15 -4
- package/dist/layout-engine/measure/measureContainer.js +1 -1
- package/dist/layout-engine/measure/measureHelpers.d.ts +11 -2
- package/dist/layout-engine/measure/measureHelpers.js +15 -4
- package/dist/layout-engine/measure/measureParagraph.js +85 -25
- package/dist/layout-engine/measure/measureProvider.js +4 -3
- package/dist/layout-engine/measure/tableCellFlow.js +5 -2
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
- package/dist/layout-engine/paragraphSpacing.js +107 -13
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +48 -15
- package/dist/layout-painter/registry/modules/textBox.js +5 -1
- package/dist/layout-painter/renderImage.d.ts +12 -1
- package/dist/layout-painter/renderImage.js +21 -4
- package/dist/layout-painter/renderPage.d.ts +5 -2
- package/dist/layout-painter/renderPage.js +59 -14
- package/dist/layout-painter/renderParagraph.js +55 -15
- package/dist/layout-painter/renderTable.d.ts +5 -1
- package/dist/layout-painter/renderTable.js +7 -3
- package/dist/layout-painter/renderTextBox.d.ts +2 -1
- package/dist/layout-painter/renderTextBox.js +25 -14
- package/dist/layout-painter/renderWatermark.d.ts +1 -1
- package/dist/layout-painter/renderWatermark.js +2 -1
- package/dist/managers/DocumentLoaderManager.js +1 -1
- package/dist/markdown/fromMarkdown.js +1 -1
- package/dist/model.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +175 -18
- package/dist/prosemirror/commands/comments.d.ts +79 -4
- package/dist/prosemirror/commands/comments.js +622 -13
- package/dist/prosemirror/commands/contentControls.js +1 -1
- package/dist/prosemirror/commands/hyperlink.js +15 -1
- package/dist/prosemirror/commands/image.js +7 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
- package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
- package/dist/prosemirror/commentIdAllocator.js +13 -3
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +404 -62
- package/dist/prosemirror/conversion/index.js +1 -1
- package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
- package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/toProseDoc.js +364 -103
- package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
- package/dist/prosemirror/extensions/StarterKit.js +6 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +6 -2
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +10 -2
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +15 -2
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +30 -4
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +70 -11
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +14 -6
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
- package/dist/prosemirror/index.d.ts +1 -1
- package/dist/prosemirror/index.js +1 -1
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +36 -10
- package/dist/prosemirror/plugins/contentControlWidgets.js +128 -94
- package/dist/prosemirror/plugins/revisionIds.d.ts +23 -0
- package/dist/prosemirror/plugins/revisionIds.js +57 -0
- package/dist/prosemirror/plugins/selectionTracker.js +1 -0
- package/dist/prosemirror/plugins/suggestionMode.js +5 -4
- package/dist/prosemirror/runFormattingMarkNames.d.ts +14 -0
- package/dist/prosemirror/runFormattingMarkNames.js +40 -0
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +30 -3
- package/dist/prosemirror/schema/marks.js +15 -0
- package/dist/prosemirror/schema/nodes.d.ts +111 -6
- package/dist/prosemirror/selectionState.js +1 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
- package/dist/prosemirror/tableCellMergeRevision.js +22 -0
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
- package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
- package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
- package/dist/prosemirror/validation.js +9 -1
- package/dist/render-dom/RemoteSelectionOverlay.js +2 -2
- package/dist/server.d.ts +7 -3
- package/dist/server.js +7 -3
- package/dist/utils/clipboard.d.ts +10 -1
- package/dist/utils/clipboard.js +33 -6
- package/dist/utils/colorResolver.d.ts +8 -1
- package/dist/utils/colorResolver.js +35 -5
- package/dist/utils/fontFamilyMerge.js +9 -11
- package/dist/utils/fontLoader.js +3 -2
- package/dist/utils/fontResolver.d.ts +2 -1
- package/dist/utils/fontResolver.js +31 -7
- package/dist/utils/fontWeights.d.ts +1 -1
- package/dist/utils/fontWeights.js +1 -1
- package/dist/utils/hexId.d.ts +11 -1
- package/dist/utils/hexId.js +11 -1
- package/dist/utils/paragraphFormattingMerge.js +1 -0
- package/dist/utils/sanitizeImageSrc.d.ts +21 -0
- package/dist/utils/sanitizeImageSrc.js +30 -0
- package/dist/utils/stripXmlDeclarations.d.ts +11 -0
- package/dist/utils/stripXmlDeclarations.js +35 -0
- package/dist/version-comparison.d.ts +22 -2
- package/dist/version-comparison.js +41 -12
- package/dist/watermark/index.d.ts +11 -1
- package/dist/watermark/index.js +22 -1
- package/package.json +3 -2
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { DocxArchiveError } from "./boundedArchive.js";
|
|
2
|
+
import { getDocxXmlSafetyIssue } from "../xmlSafety.js";
|
|
3
|
+
import { FolioDocxPackageInspectionError, inspectDocxPackage } from "./inspectDocxPackage.js";
|
|
4
|
+
import { TaggedError, panic } from "better-result";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
//#region src/docx/server/evaluateDocxXmlPatchProposal.ts
|
|
7
|
+
const FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION = 1;
|
|
8
|
+
const FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE = "folio-xml-patch-proposal-v1";
|
|
9
|
+
const FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS = Object.freeze({
|
|
10
|
+
maxReplacements: 16,
|
|
11
|
+
maxPartBytes: 8 * 1024 * 1024,
|
|
12
|
+
maxTotalBytes: 16 * 1024 * 1024
|
|
13
|
+
});
|
|
14
|
+
const FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES = Object.freeze([
|
|
15
|
+
"invalid-proposal",
|
|
16
|
+
"unsupported-version",
|
|
17
|
+
"too-many-replacements",
|
|
18
|
+
"duplicate-part",
|
|
19
|
+
"invalid-part-path",
|
|
20
|
+
"invalid-base-sha256",
|
|
21
|
+
"part-not-allowed",
|
|
22
|
+
"part-not-found",
|
|
23
|
+
"part-not-xml",
|
|
24
|
+
"replacement-too-large",
|
|
25
|
+
"replacements-too-large",
|
|
26
|
+
"xml-doctype-forbidden",
|
|
27
|
+
"xml-not-well-formed",
|
|
28
|
+
"xml-encoding-mismatch",
|
|
29
|
+
"base-hash-mismatch",
|
|
30
|
+
"package-inspection-failed"
|
|
31
|
+
]);
|
|
32
|
+
var InvalidFolioDocxXmlPatchProposalError = class extends TaggedError("InvalidFolioDocxXmlPatchProposalError")() {};
|
|
33
|
+
var InvalidFolioDocxXmlPatchProposalOptionsError = class extends TaggedError("InvalidFolioDocxXmlPatchProposalOptionsError")() {};
|
|
34
|
+
const SHA256_PATTERN = /^[\da-f]{64}$/u;
|
|
35
|
+
const XML_DECLARED_ENCODING_PATTERN = /^\uFEFF?<\?xml[^>]*\bencoding\s*=\s*["']([^"']+)["'][^>]*\?>/iu;
|
|
36
|
+
const MAX_PACKAGE_PATH_CODE_UNITS = 1024;
|
|
37
|
+
const prepareXmlReplacement = ({ replacement, encodedByPath, inspectedByPath }) => {
|
|
38
|
+
const replacementBytes = encodedByPath.get(replacement.path);
|
|
39
|
+
const current = inspectedByPath.get(replacement.path);
|
|
40
|
+
if (replacementBytes === void 0 || current === void 0) return panic("Accepted XML replacement is missing prepared package data");
|
|
41
|
+
return {
|
|
42
|
+
path: replacement.path,
|
|
43
|
+
baseSha256: replacement.baseSha256,
|
|
44
|
+
currentSha256: current.sha256,
|
|
45
|
+
replacementSha256: createHash("sha256").update(replacementBytes).digest("hex"),
|
|
46
|
+
replacementByteLength: replacementBytes.byteLength,
|
|
47
|
+
encoding: "utf-8"
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
51
|
+
const invalidProposal = (path, reason) => {
|
|
52
|
+
throw new InvalidFolioDocxXmlPatchProposalError({
|
|
53
|
+
message: `Invalid XML patch proposal at ${path}: ${reason}.`,
|
|
54
|
+
path,
|
|
55
|
+
reason
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
const assertAllowedKeys = (value, path, allowedKeys) => {
|
|
59
|
+
const allowed = new Set(allowedKeys);
|
|
60
|
+
const unexpected = Object.keys(value).find((key) => !allowed.has(key));
|
|
61
|
+
if (unexpected !== void 0) invalidProposal(`${path}.${unexpected}`, "unexpected property");
|
|
62
|
+
};
|
|
63
|
+
const readString = (value, key, path) => {
|
|
64
|
+
const result = value[key];
|
|
65
|
+
if (typeof result !== "string") return invalidProposal(`${path}.${key}`, "expected a string");
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
const parseXmlReplacement = (value, index) => {
|
|
69
|
+
const path = `$.replacements[${index}]`;
|
|
70
|
+
if (!isPlainObject(value)) return invalidProposal(path, "expected an object");
|
|
71
|
+
assertAllowedKeys(value, path, [
|
|
72
|
+
"path",
|
|
73
|
+
"baseSha256",
|
|
74
|
+
"replacementXml"
|
|
75
|
+
]);
|
|
76
|
+
return {
|
|
77
|
+
path: readString(value, "path", path),
|
|
78
|
+
baseSha256: readString(value, "baseSha256", path),
|
|
79
|
+
replacementXml: readString(value, "replacementXml", path)
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
const parseFolioDocxXmlPatchProposal = (value) => {
|
|
83
|
+
if (!isPlainObject(value)) return invalidProposal("$", "expected an object");
|
|
84
|
+
assertAllowedKeys(value, "$", ["version", "replacements"]);
|
|
85
|
+
const version = value["version"];
|
|
86
|
+
if (typeof version !== "number" || !Number.isSafeInteger(version)) return invalidProposal("$.version", "expected an integer contract version");
|
|
87
|
+
if (version !== 1) return invalidProposal("$.version", "unsupported contract version");
|
|
88
|
+
const replacements = value["replacements"];
|
|
89
|
+
if (!Array.isArray(replacements) || replacements.length === 0) return invalidProposal("$.replacements", "expected a non-empty array");
|
|
90
|
+
if (replacements.length > FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS.maxReplacements) return invalidProposal("$.replacements", `expected at most ${FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS.maxReplacements} replacements`);
|
|
91
|
+
const firstReplacement = replacements.at(0);
|
|
92
|
+
if (firstReplacement === void 0) return invalidProposal("$.replacements", "expected a non-empty array");
|
|
93
|
+
return {
|
|
94
|
+
version: 1,
|
|
95
|
+
replacements: [parseXmlReplacement(firstReplacement, 0), ...replacements.slice(1).map((replacement, index) => parseXmlReplacement(replacement, index + 1))]
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
const resolveLimit = ({ value, fallback, path }) => {
|
|
99
|
+
const limit = value ?? fallback;
|
|
100
|
+
if (!Number.isSafeInteger(limit) || limit < 0) throw new InvalidFolioDocxXmlPatchProposalOptionsError({
|
|
101
|
+
message: `${path} must be a non-negative safe integer.`,
|
|
102
|
+
path
|
|
103
|
+
});
|
|
104
|
+
return limit;
|
|
105
|
+
};
|
|
106
|
+
const resolveLimits = (limits) => {
|
|
107
|
+
const maxReplacements = resolveLimit({
|
|
108
|
+
value: limits?.maxReplacements,
|
|
109
|
+
fallback: FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS.maxReplacements,
|
|
110
|
+
path: "limits.maxReplacements"
|
|
111
|
+
});
|
|
112
|
+
if (maxReplacements > FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS.maxReplacements) throw new InvalidFolioDocxXmlPatchProposalOptionsError({
|
|
113
|
+
message: `limits.maxReplacements must not exceed ${FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS.maxReplacements}.`,
|
|
114
|
+
path: "limits.maxReplacements"
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
maxReplacements,
|
|
118
|
+
maxPartBytes: resolveLimit({
|
|
119
|
+
value: limits?.maxPartBytes,
|
|
120
|
+
fallback: FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS.maxPartBytes,
|
|
121
|
+
path: "limits.maxPartBytes"
|
|
122
|
+
}),
|
|
123
|
+
maxTotalBytes: resolveLimit({
|
|
124
|
+
value: limits?.maxTotalBytes,
|
|
125
|
+
fallback: FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS.maxTotalBytes,
|
|
126
|
+
path: "limits.maxTotalBytes"
|
|
127
|
+
})
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
const isNormalizedPackagePath = (path) => path.length > 0 && path.length <= MAX_PACKAGE_PATH_CODE_UNITS && !path.startsWith("/") && !path.endsWith("/") && !path.includes("\\") && path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
131
|
+
const resolveAllowedParts = (allowedParts) => {
|
|
132
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
133
|
+
for (const [index, path] of allowedParts.entries()) {
|
|
134
|
+
if (typeof path !== "string" || !isNormalizedPackagePath(path)) throw new InvalidFolioDocxXmlPatchProposalOptionsError({
|
|
135
|
+
message: `allowedParts[${index}] must be a normalized package path.`,
|
|
136
|
+
path: `allowedParts[${index}]`
|
|
137
|
+
});
|
|
138
|
+
if (resolved.has(path)) throw new InvalidFolioDocxXmlPatchProposalOptionsError({
|
|
139
|
+
message: `allowedParts[${index}] must be unique.`,
|
|
140
|
+
path: `allowedParts[${index}]`
|
|
141
|
+
});
|
|
142
|
+
resolved.add(path);
|
|
143
|
+
}
|
|
144
|
+
return resolved;
|
|
145
|
+
};
|
|
146
|
+
const rejectedEvaluation = ({ issues, limits }) => ({
|
|
147
|
+
version: 1,
|
|
148
|
+
profile: FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE,
|
|
149
|
+
status: "rejected",
|
|
150
|
+
producesOutput: false,
|
|
151
|
+
issues,
|
|
152
|
+
replacements: [],
|
|
153
|
+
limits
|
|
154
|
+
});
|
|
155
|
+
const acceptedEvaluation = ({ replacements, limits }) => ({
|
|
156
|
+
version: 1,
|
|
157
|
+
profile: FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE,
|
|
158
|
+
status: "accepted",
|
|
159
|
+
producesOutput: false,
|
|
160
|
+
issues: [],
|
|
161
|
+
replacements,
|
|
162
|
+
limits
|
|
163
|
+
});
|
|
164
|
+
const inspectionIssue = (error) => {
|
|
165
|
+
if (error.code === "part-not-found" || error.code === "part-not-xml") return {
|
|
166
|
+
code: error.code,
|
|
167
|
+
message: error.message,
|
|
168
|
+
...error.part !== void 0 && { part: error.part }
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
code: "package-inspection-failed",
|
|
172
|
+
message: "The package could not be inspected within the configured limits.",
|
|
173
|
+
...error.part !== void 0 && { part: error.part }
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
const evaluateDocxXmlPatchProposal = async ({ bytes, proposal: rawProposal, allowedParts, archive, limits: rawLimits }) => {
|
|
177
|
+
const limits = resolveLimits(rawLimits);
|
|
178
|
+
const allowed = resolveAllowedParts(allowedParts);
|
|
179
|
+
const rawReplacements = isPlainObject(rawProposal) ? rawProposal["replacements"] : void 0;
|
|
180
|
+
if (isPlainObject(rawProposal) && rawProposal["version"] === 1 && Array.isArray(rawReplacements) && rawReplacements.length > limits.maxReplacements) return rejectedEvaluation({
|
|
181
|
+
issues: [{
|
|
182
|
+
code: "too-many-replacements",
|
|
183
|
+
message: `Proposal contains ${rawReplacements.length} replacements (max ${limits.maxReplacements}).`,
|
|
184
|
+
proposalPath: "$.replacements"
|
|
185
|
+
}],
|
|
186
|
+
limits
|
|
187
|
+
});
|
|
188
|
+
let proposal;
|
|
189
|
+
try {
|
|
190
|
+
proposal = parseFolioDocxXmlPatchProposal(rawProposal);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (!(error instanceof InvalidFolioDocxXmlPatchProposalError)) throw error;
|
|
193
|
+
return rejectedEvaluation({
|
|
194
|
+
issues: [{
|
|
195
|
+
code: error.path === "$.version" && isPlainObject(rawProposal) && typeof rawProposal["version"] === "number" && Number.isSafeInteger(rawProposal["version"]) ? "unsupported-version" : "invalid-proposal",
|
|
196
|
+
message: error.message,
|
|
197
|
+
proposalPath: error.path
|
|
198
|
+
}],
|
|
199
|
+
limits
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
const issues = [];
|
|
203
|
+
const seen = /* @__PURE__ */ new Set();
|
|
204
|
+
const encodedByPath = /* @__PURE__ */ new Map();
|
|
205
|
+
let encodedTotalBytes = 0;
|
|
206
|
+
let totalCodeUnits = 0;
|
|
207
|
+
let totalLimitReported = false;
|
|
208
|
+
for (const [index, replacement] of proposal.replacements.entries()) {
|
|
209
|
+
const proposalPath = `$.replacements[${index}]`;
|
|
210
|
+
if (seen.has(replacement.path)) issues.push({
|
|
211
|
+
code: "duplicate-part",
|
|
212
|
+
message: "Each package part may appear only once in a proposal.",
|
|
213
|
+
proposalPath: `${proposalPath}.path`,
|
|
214
|
+
part: replacement.path
|
|
215
|
+
});
|
|
216
|
+
seen.add(replacement.path);
|
|
217
|
+
if (!isNormalizedPackagePath(replacement.path)) issues.push({
|
|
218
|
+
code: "invalid-part-path",
|
|
219
|
+
message: "Replacement paths must be normalized package paths.",
|
|
220
|
+
proposalPath: `${proposalPath}.path`,
|
|
221
|
+
part: replacement.path
|
|
222
|
+
});
|
|
223
|
+
else if (!allowed.has(replacement.path)) issues.push({
|
|
224
|
+
code: "part-not-allowed",
|
|
225
|
+
message: "The server-side policy does not allow replacing this package part.",
|
|
226
|
+
proposalPath: `${proposalPath}.path`,
|
|
227
|
+
part: replacement.path
|
|
228
|
+
});
|
|
229
|
+
if (replacement.baseSha256.length !== 64 || !SHA256_PATTERN.test(replacement.baseSha256)) issues.push({
|
|
230
|
+
code: "invalid-base-sha256",
|
|
231
|
+
message: "baseSha256 must be a lowercase hexadecimal SHA-256 digest.",
|
|
232
|
+
proposalPath: `${proposalPath}.baseSha256`,
|
|
233
|
+
part: replacement.path
|
|
234
|
+
});
|
|
235
|
+
const replacementCodeUnits = replacement.replacementXml.length;
|
|
236
|
+
const exceedsPartLowerBound = replacementCodeUnits > limits.maxPartBytes;
|
|
237
|
+
if (exceedsPartLowerBound) issues.push({
|
|
238
|
+
code: "replacement-too-large",
|
|
239
|
+
message: `Replacement XML exceeds the ${limits.maxPartBytes}-byte per-part limit.`,
|
|
240
|
+
proposalPath: `${proposalPath}.replacementXml`,
|
|
241
|
+
part: replacement.path
|
|
242
|
+
});
|
|
243
|
+
totalCodeUnits += replacementCodeUnits;
|
|
244
|
+
if (totalCodeUnits > limits.maxTotalBytes && !totalLimitReported) {
|
|
245
|
+
totalLimitReported = true;
|
|
246
|
+
issues.push({
|
|
247
|
+
code: "replacements-too-large",
|
|
248
|
+
message: `Replacement XML exceeds the ${limits.maxTotalBytes}-byte cumulative limit.`,
|
|
249
|
+
proposalPath: "$.replacements"
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (exceedsPartLowerBound || totalLimitReported) continue;
|
|
253
|
+
const replacementBytes = new TextEncoder().encode(replacement.replacementXml);
|
|
254
|
+
const exceedsEncodedPart = replacementBytes.byteLength > limits.maxPartBytes;
|
|
255
|
+
const nextEncodedTotalBytes = encodedTotalBytes + replacementBytes.byteLength;
|
|
256
|
+
if (exceedsEncodedPart) issues.push({
|
|
257
|
+
code: "replacement-too-large",
|
|
258
|
+
message: `Replacement XML exceeds the ${limits.maxPartBytes}-byte per-part limit.`,
|
|
259
|
+
proposalPath: `${proposalPath}.replacementXml`,
|
|
260
|
+
part: replacement.path
|
|
261
|
+
});
|
|
262
|
+
if (nextEncodedTotalBytes > limits.maxTotalBytes) {
|
|
263
|
+
totalLimitReported = true;
|
|
264
|
+
issues.push({
|
|
265
|
+
code: "replacements-too-large",
|
|
266
|
+
message: `Replacement XML exceeds the ${limits.maxTotalBytes}-byte cumulative limit.`,
|
|
267
|
+
proposalPath: "$.replacements"
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
encodedTotalBytes = nextEncodedTotalBytes;
|
|
271
|
+
if (exceedsEncodedPart || totalLimitReported) continue;
|
|
272
|
+
encodedByPath.set(replacement.path, replacementBytes);
|
|
273
|
+
const safetyIssue = getDocxXmlSafetyIssue(replacement.replacementXml);
|
|
274
|
+
if (safetyIssue === "doctype-forbidden") issues.push({
|
|
275
|
+
code: "xml-doctype-forbidden",
|
|
276
|
+
message: "Replacement XML must not declare a document type.",
|
|
277
|
+
proposalPath: `${proposalPath}.replacementXml`,
|
|
278
|
+
part: replacement.path
|
|
279
|
+
});
|
|
280
|
+
else if (safetyIssue === "not-well-formed") issues.push({
|
|
281
|
+
code: "xml-not-well-formed",
|
|
282
|
+
message: "Replacement XML must be well formed.",
|
|
283
|
+
proposalPath: `${proposalPath}.replacementXml`,
|
|
284
|
+
part: replacement.path
|
|
285
|
+
});
|
|
286
|
+
else {
|
|
287
|
+
const declaredEncoding = replacement.replacementXml.match(XML_DECLARED_ENCODING_PATTERN)?.at(1);
|
|
288
|
+
if (declaredEncoding !== void 0 && declaredEncoding.toLowerCase() !== "utf-8" && declaredEncoding.toLowerCase() !== "utf8") issues.push({
|
|
289
|
+
code: "xml-encoding-mismatch",
|
|
290
|
+
message: "Replacement XML declarations must use UTF-8 encoding.",
|
|
291
|
+
proposalPath: `${proposalPath}.replacementXml`,
|
|
292
|
+
part: replacement.path
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (issues.length > 0) {
|
|
297
|
+
const firstIssue = issues.at(0);
|
|
298
|
+
if (firstIssue === void 0) return panic("Rejected XML patch proposal is missing an issue");
|
|
299
|
+
return rejectedEvaluation({
|
|
300
|
+
issues: [firstIssue, ...issues.slice(1)],
|
|
301
|
+
limits
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
let inspected;
|
|
305
|
+
try {
|
|
306
|
+
inspected = await inspectDocxPackage(bytes, {
|
|
307
|
+
...archive === void 0 ? {} : { archive },
|
|
308
|
+
xmlParts: proposal.replacements.map(({ path }) => path),
|
|
309
|
+
limits: {
|
|
310
|
+
maxXmlParts: limits.maxReplacements,
|
|
311
|
+
maxXmlPartBytes: limits.maxPartBytes,
|
|
312
|
+
maxXmlTotalBytes: limits.maxTotalBytes
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (error instanceof FolioDocxPackageInspectionError) return rejectedEvaluation({
|
|
317
|
+
issues: [inspectionIssue(error)],
|
|
318
|
+
limits
|
|
319
|
+
});
|
|
320
|
+
if (error instanceof DocxArchiveError) return rejectedEvaluation({
|
|
321
|
+
issues: [{
|
|
322
|
+
code: "package-inspection-failed",
|
|
323
|
+
message: "The package could not be inspected within the configured limits."
|
|
324
|
+
}],
|
|
325
|
+
limits
|
|
326
|
+
});
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
const inspectedByPath = new Map(inspected.xmlParts.map((part) => [part.path, part]));
|
|
330
|
+
for (const [index, replacement] of proposal.replacements.entries()) {
|
|
331
|
+
const current = inspectedByPath.get(replacement.path);
|
|
332
|
+
if (current === void 0 || current.sha256 === replacement.baseSha256) continue;
|
|
333
|
+
issues.push({
|
|
334
|
+
code: "base-hash-mismatch",
|
|
335
|
+
message: "The package part changed after the proposal base was inspected.",
|
|
336
|
+
proposalPath: `$.replacements[${index}].baseSha256`,
|
|
337
|
+
part: replacement.path
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
if (issues.length > 0) {
|
|
341
|
+
const firstIssue = issues.at(0);
|
|
342
|
+
if (firstIssue === void 0) return panic("Rejected XML patch proposal is missing an issue");
|
|
343
|
+
return rejectedEvaluation({
|
|
344
|
+
issues: [firstIssue, ...issues.slice(1)],
|
|
345
|
+
limits
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
const preparedReplacements = proposal.replacements.map((replacement) => prepareXmlReplacement({
|
|
349
|
+
replacement,
|
|
350
|
+
encodedByPath,
|
|
351
|
+
inspectedByPath
|
|
352
|
+
}));
|
|
353
|
+
const firstPreparedReplacement = preparedReplacements.at(0);
|
|
354
|
+
if (firstPreparedReplacement === void 0) return panic("Accepted XML patch proposal is missing a prepared replacement");
|
|
355
|
+
return acceptedEvaluation({
|
|
356
|
+
replacements: [firstPreparedReplacement, ...preparedReplacements.slice(1)],
|
|
357
|
+
limits
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
//#endregion
|
|
361
|
+
export { FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, evaluateDocxXmlPatchProposal, parseFolioDocxXmlPatchProposal };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { findAllDeep, findChild, findDeep, getAttributeAnyPrefix, getLocalName, getTextContent, parseXml } from "../xmlParser.js";
|
|
1
|
+
import { findAllDeep, findChild, findDeep, getAttribute, getAttributeAnyPrefix, getLocalName, getTextContent, parseXml } from "../xmlParser.js";
|
|
2
|
+
import { RELATIONSHIP_TYPES, parseRelationships } from "../relsParser.js";
|
|
2
3
|
import { loadDocxArchive } from "./boundedArchive.js";
|
|
3
4
|
//#region src/docx/server/extractDocxText.ts
|
|
4
|
-
const
|
|
5
|
+
const DOCUMENT_RELS_PATH = "word/_rels/document.xml.rels";
|
|
5
6
|
const childElements = (element) => element.elements?.filter((child) => child.type === "element") ?? [];
|
|
6
7
|
const collectText = (element) => {
|
|
7
8
|
let text = "";
|
|
@@ -86,12 +87,10 @@ const extractContainer = ({ container, source, startIndex }) => {
|
|
|
86
87
|
charCount
|
|
87
88
|
};
|
|
88
89
|
};
|
|
89
|
-
const extractParts = async ({ archive, source, rootName, startIndex }) => {
|
|
90
|
+
const extractParts = async ({ archive, source, rootName, startIndex, paths }) => {
|
|
90
91
|
const paragraphs = [];
|
|
91
92
|
let charCount = 0;
|
|
92
93
|
let nextIndex = startIndex;
|
|
93
|
-
const prefix = `word/${source}`;
|
|
94
|
-
const paths = archive.entries.filter((path) => HEADER_FOOTER_PATH.test(path) && path.startsWith(prefix)).toSorted();
|
|
95
94
|
for (const path of paths) {
|
|
96
95
|
const xml = await archive.readEntryString(path);
|
|
97
96
|
if (xml === null) continue;
|
|
@@ -111,6 +110,50 @@ const extractParts = async ({ archive, source, rootName, startIndex }) => {
|
|
|
111
110
|
charCount
|
|
112
111
|
};
|
|
113
112
|
};
|
|
113
|
+
/** A `word/_rels/document.xml.rels` `Target` is relative to `word/`; resolve it to a full archive-entry path. */
|
|
114
|
+
const resolveWordPartPath = (target) => target.startsWith("/") ? target.slice(1) : `word/${target}`;
|
|
115
|
+
/**
|
|
116
|
+
* Resolve the header/footer parts actually wired into the document via
|
|
117
|
+
* `word/_rels/document.xml.rels` + each section's `w:headerReference` /
|
|
118
|
+
* `w:footerReference`, instead of extracting every `word/header*.xml` /
|
|
119
|
+
* `word/footer*.xml` entry by filename. A DOCX can carry an orphaned
|
|
120
|
+
* header/footer part (stale, or planted by an attacker) that no section
|
|
121
|
+
* references — reading it unconditionally would surface prompt-injection or
|
|
122
|
+
* stale content that Word itself never renders.
|
|
123
|
+
*/
|
|
124
|
+
const resolveReferencedHeaderFooterParts = async (archive, documentRoot) => {
|
|
125
|
+
const relsXml = await archive.readEntryString(DOCUMENT_RELS_PATH);
|
|
126
|
+
if (relsXml === null) return {
|
|
127
|
+
headers: [],
|
|
128
|
+
footers: []
|
|
129
|
+
};
|
|
130
|
+
const relationships = parseRelationships(relsXml);
|
|
131
|
+
const headerRIds = /* @__PURE__ */ new Set();
|
|
132
|
+
const footerRIds = /* @__PURE__ */ new Set();
|
|
133
|
+
for (const sectPr of findAllDeep(documentRoot, "w", "sectPr")) {
|
|
134
|
+
for (const ref of findAllDeep(sectPr, "w", "headerReference")) {
|
|
135
|
+
const rId = getAttribute(ref, "r", "id");
|
|
136
|
+
if (rId !== null) headerRIds.add(rId);
|
|
137
|
+
}
|
|
138
|
+
for (const ref of findAllDeep(sectPr, "w", "footerReference")) {
|
|
139
|
+
const rId = getAttribute(ref, "r", "id");
|
|
140
|
+
if (rId !== null) footerRIds.add(rId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const resolvePaths = (rIds, relationshipType) => {
|
|
144
|
+
const paths = /* @__PURE__ */ new Set();
|
|
145
|
+
for (const rId of rIds) {
|
|
146
|
+
const relationship = relationships.get(rId);
|
|
147
|
+
if (!relationship || relationship.type !== relationshipType || relationship.targetMode === "External") continue;
|
|
148
|
+
paths.add(resolveWordPartPath(relationship.target));
|
|
149
|
+
}
|
|
150
|
+
return [...paths].toSorted();
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
headers: resolvePaths(headerRIds, RELATIONSHIP_TYPES.header),
|
|
154
|
+
footers: resolvePaths(footerRIds, RELATIONSHIP_TYPES.footer)
|
|
155
|
+
};
|
|
156
|
+
};
|
|
114
157
|
const createEmptyResult = () => ({
|
|
115
158
|
paragraphs: [],
|
|
116
159
|
charCount: 0,
|
|
@@ -121,13 +164,16 @@ const extractDocxText = async (bytes) => {
|
|
|
121
164
|
const archive = await loadDocxArchive(bytes);
|
|
122
165
|
const documentXml = await archive.readEntryString("word/document.xml");
|
|
123
166
|
if (documentXml === null) return createEmptyResult();
|
|
124
|
-
const
|
|
167
|
+
const root = parseXml(documentXml);
|
|
168
|
+
const body = findDeep(root, "w", "body");
|
|
125
169
|
if (!body) return createEmptyResult();
|
|
170
|
+
const referencedParts = await resolveReferencedHeaderFooterParts(archive, root);
|
|
126
171
|
const headers = await extractParts({
|
|
127
172
|
archive,
|
|
128
173
|
source: "header",
|
|
129
174
|
rootName: "hdr",
|
|
130
|
-
startIndex: 0
|
|
175
|
+
startIndex: 0,
|
|
176
|
+
paths: referencedParts.headers
|
|
131
177
|
});
|
|
132
178
|
const bodyResult = extractContainer({
|
|
133
179
|
container: body,
|
|
@@ -138,7 +184,8 @@ const extractDocxText = async (bytes) => {
|
|
|
138
184
|
archive,
|
|
139
185
|
source: "footer",
|
|
140
186
|
rootName: "ftr",
|
|
141
|
-
startIndex: headers.paragraphs.length + bodyResult.paragraphs.length
|
|
187
|
+
startIndex: headers.paragraphs.length + bodyResult.paragraphs.length,
|
|
188
|
+
paths: referencedParts.footers
|
|
142
189
|
});
|
|
143
190
|
return {
|
|
144
191
|
paragraphs: [
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { DocxArchiveOptions } from "./boundedArchive.js";
|
|
2
|
+
|
|
3
|
+
//#region src/docx/server/inspectDocxPackage.d.ts
|
|
4
|
+
declare const FOLIO_DOCX_PACKAGE_INSPECTION_VERSION: 1;
|
|
5
|
+
declare const FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS: Readonly<{
|
|
6
|
+
readonly maxXmlParts: 16;
|
|
7
|
+
readonly maxXmlPartBytes: number;
|
|
8
|
+
readonly maxXmlTotalBytes: number;
|
|
9
|
+
}>;
|
|
10
|
+
declare const FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES: readonly ["invalid-limits", "too-many-xml-parts", "duplicate-xml-part", "part-not-found", "part-not-xml", "xml-part-too-large", "xml-total-too-large", "xml-decode-failed"];
|
|
11
|
+
type FolioDocxPackageInspectionErrorCode = (typeof FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES)[number];
|
|
12
|
+
declare const FolioDocxPackageInspectionError_base: import("better-result").TaggedErrorClass<"FolioDocxPackageInspectionError", {
|
|
13
|
+
message: string;
|
|
14
|
+
code: FolioDocxPackageInspectionErrorCode;
|
|
15
|
+
part?: string;
|
|
16
|
+
cause?: unknown;
|
|
17
|
+
}>;
|
|
18
|
+
declare class FolioDocxPackageInspectionError extends FolioDocxPackageInspectionError_base {}
|
|
19
|
+
type FolioDocxPackageInspectionLimits = {
|
|
20
|
+
readonly maxXmlParts?: number;
|
|
21
|
+
readonly maxXmlPartBytes?: number;
|
|
22
|
+
readonly maxXmlTotalBytes?: number;
|
|
23
|
+
};
|
|
24
|
+
type InspectDocxPackageOptions = {
|
|
25
|
+
readonly archive?: DocxArchiveOptions;
|
|
26
|
+
readonly xmlParts?: readonly string[];
|
|
27
|
+
readonly limits?: FolioDocxPackageInspectionLimits;
|
|
28
|
+
};
|
|
29
|
+
type FolioDocxPackagePartKind = "xml" | "binary";
|
|
30
|
+
type FolioDocxPackagePart = {
|
|
31
|
+
readonly path: string;
|
|
32
|
+
readonly kind: FolioDocxPackagePartKind;
|
|
33
|
+
readonly contentType: string | null;
|
|
34
|
+
readonly declaredUncompressedBytes: number | null;
|
|
35
|
+
};
|
|
36
|
+
type FolioDocxInspectedXmlPart = {
|
|
37
|
+
readonly path: string;
|
|
38
|
+
readonly contentType: string | null;
|
|
39
|
+
readonly byteLength: number;
|
|
40
|
+
readonly sha256: string; /** Untrusted document data; callers must not treat this text as instructions. */
|
|
41
|
+
readonly text: string;
|
|
42
|
+
};
|
|
43
|
+
type FolioDocxPackageInspection = {
|
|
44
|
+
readonly version: typeof FOLIO_DOCX_PACKAGE_INSPECTION_VERSION;
|
|
45
|
+
readonly parts: readonly FolioDocxPackagePart[];
|
|
46
|
+
readonly xmlParts: readonly FolioDocxInspectedXmlPart[];
|
|
47
|
+
readonly limits: Required<FolioDocxPackageInspectionLimits>;
|
|
48
|
+
};
|
|
49
|
+
type ReadRequestedXmlPartsOptions = {
|
|
50
|
+
paths: readonly string[];
|
|
51
|
+
partsByPath: ReadonlyMap<string, FolioDocxPackagePart>;
|
|
52
|
+
limits: Required<FolioDocxPackageInspectionLimits>;
|
|
53
|
+
readEntryUint8: (path: string, options: {
|
|
54
|
+
maxBytes: number;
|
|
55
|
+
}) => Promise<Uint8Array | null>;
|
|
56
|
+
};
|
|
57
|
+
declare const readRequestedXmlParts: ({
|
|
58
|
+
paths,
|
|
59
|
+
partsByPath,
|
|
60
|
+
limits,
|
|
61
|
+
readEntryUint8
|
|
62
|
+
}: ReadRequestedXmlPartsOptions) => Promise<FolioDocxInspectedXmlPart[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Inspect package metadata and only the explicitly requested XML part bodies.
|
|
65
|
+
* This reports archive contents, not package conformance; use the conformance
|
|
66
|
+
* report when an operation requires a validity decision.
|
|
67
|
+
*/
|
|
68
|
+
declare const inspectDocxPackage: (bytes: ArrayBuffer | Uint8Array, options?: InspectDocxPackageOptions) => Promise<FolioDocxPackageInspection>;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FolioDocxInspectedXmlPart, FolioDocxPackageInspection, FolioDocxPackageInspectionError, FolioDocxPackageInspectionErrorCode, FolioDocxPackageInspectionLimits, FolioDocxPackagePart, FolioDocxPackagePartKind, InspectDocxPackageOptions, inspectDocxPackage, readRequestedXmlParts };
|