@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getLocalName, parseXml } from "../xmlParser.js";
|
|
1
2
|
//#region src/docx/serializer/xmlUtils.ts
|
|
2
3
|
/**
|
|
3
4
|
* Shared XML utility functions for serializers.
|
|
@@ -21,5 +22,30 @@ function intAttr(value) {
|
|
|
21
22
|
if (value == null || !Number.isFinite(value)) return "0";
|
|
22
23
|
return String(Math.round(value));
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Verify that `xml` parses to exactly one root element named `expectedLocalName`
|
|
27
|
+
* (namespace prefix ignored). Used to gate raw XML snapshots — e.g. an SDT's
|
|
28
|
+
* `rawPropertiesXml`/`rawEndPropertiesXml` — before splicing them verbatim
|
|
29
|
+
* into a serialized document. Those snapshots are normally produced by our
|
|
30
|
+
* own parser, but they can also arrive from an untrusted surface (a
|
|
31
|
+
* programmatically constructed node, a collaboration payload); a value that
|
|
32
|
+
* is not a single well-formed `<w:sdtPr>`/`<w:sdtEndPr>` element could inject
|
|
33
|
+
* sibling markup or close the enclosing `<w:sdt>` early. Callers should fall
|
|
34
|
+
* back to a synthesized properties block when this returns `false`.
|
|
35
|
+
*/
|
|
36
|
+
function isSingleWellFormedElement(xml, expectedLocalName) {
|
|
37
|
+
const trimmed = xml.trim();
|
|
38
|
+
if (!trimmed) return false;
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = parseXml(trimmed);
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const roots = (parsed.elements ?? []).filter((element) => element.type === "element");
|
|
46
|
+
if (roots.length !== 1) return false;
|
|
47
|
+
const root = roots[0];
|
|
48
|
+
return root?.name !== void 0 && getLocalName(root.name) === expectedLocalName;
|
|
49
|
+
}
|
|
24
50
|
//#endregion
|
|
25
|
-
export { escapeXml, intAttr };
|
|
51
|
+
export { escapeXml, intAttr, isSingleWellFormedElement };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { DocxArchiveOptions } from "./boundedArchive.js";
|
|
2
|
+
import { FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FolioDocxPreparedXmlReplacement, FolioDocxXmlPatchProposalEvaluation, FolioDocxXmlPatchProposalLimits } from "./evaluateDocxXmlPatchProposal.js";
|
|
3
|
+
import { FOLIO_DOCX_CONFORMANCE_PROFILE, FolioDocxConformanceReport } from "./validateDocxConformance.js";
|
|
4
|
+
|
|
5
|
+
//#region src/docx/server/applyDocxXmlPatchProposal.d.ts
|
|
6
|
+
declare const FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION: 1;
|
|
7
|
+
declare const FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE: "folio-xml-patch-application-v1";
|
|
8
|
+
type ApplyDocxXmlPatchProposalArgs = {
|
|
9
|
+
readonly bytes: ArrayBuffer | Uint8Array;
|
|
10
|
+
readonly proposal: unknown; /** Exact package paths authorized by the server-side caller. */
|
|
11
|
+
readonly allowedParts: readonly string[];
|
|
12
|
+
readonly validationProfile: typeof FOLIO_DOCX_CONFORMANCE_PROFILE;
|
|
13
|
+
readonly archive?: DocxArchiveOptions;
|
|
14
|
+
readonly limits?: FolioDocxXmlPatchProposalLimits;
|
|
15
|
+
};
|
|
16
|
+
type FolioDocxXmlPatchApplicationReceipt = {
|
|
17
|
+
readonly version: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION;
|
|
18
|
+
readonly profile: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE;
|
|
19
|
+
readonly proposalProfile: typeof FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE;
|
|
20
|
+
readonly validationProfile: typeof FOLIO_DOCX_CONFORMANCE_PROFILE;
|
|
21
|
+
readonly input: {
|
|
22
|
+
readonly sha256: string;
|
|
23
|
+
readonly byteLength: number;
|
|
24
|
+
};
|
|
25
|
+
readonly output: {
|
|
26
|
+
readonly sha256: string;
|
|
27
|
+
readonly byteLength: number;
|
|
28
|
+
};
|
|
29
|
+
readonly replacements: readonly [FolioDocxPreparedXmlReplacement, ...FolioDocxPreparedXmlReplacement[]];
|
|
30
|
+
};
|
|
31
|
+
type FolioDocxXmlPatchApplication = {
|
|
32
|
+
readonly version: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION;
|
|
33
|
+
readonly profile: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE;
|
|
34
|
+
readonly status: "proposal-rejected";
|
|
35
|
+
readonly producesOutput: false;
|
|
36
|
+
readonly evaluation: Extract<FolioDocxXmlPatchProposalEvaluation, {
|
|
37
|
+
status: "rejected";
|
|
38
|
+
}>;
|
|
39
|
+
readonly conformance: null;
|
|
40
|
+
readonly receipt: null;
|
|
41
|
+
} | {
|
|
42
|
+
readonly version: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION;
|
|
43
|
+
readonly profile: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE;
|
|
44
|
+
readonly status: "output-rejected";
|
|
45
|
+
readonly producesOutput: false;
|
|
46
|
+
readonly evaluation: Extract<FolioDocxXmlPatchProposalEvaluation, {
|
|
47
|
+
status: "accepted";
|
|
48
|
+
}>;
|
|
49
|
+
readonly conformance: FolioDocxConformanceReport;
|
|
50
|
+
readonly receipt: null;
|
|
51
|
+
} | {
|
|
52
|
+
readonly version: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION;
|
|
53
|
+
readonly profile: typeof FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE;
|
|
54
|
+
readonly status: "applied";
|
|
55
|
+
readonly producesOutput: true;
|
|
56
|
+
readonly evaluation: Extract<FolioDocxXmlPatchProposalEvaluation, {
|
|
57
|
+
status: "accepted";
|
|
58
|
+
}>;
|
|
59
|
+
readonly conformance: FolioDocxConformanceReport & {
|
|
60
|
+
readonly status: "conformant";
|
|
61
|
+
};
|
|
62
|
+
readonly receipt: FolioDocxXmlPatchApplicationReceipt;
|
|
63
|
+
readonly bytes: Uint8Array;
|
|
64
|
+
};
|
|
65
|
+
declare const UnsupportedFolioDocxXmlPatchApplicationProfileError_base: import("better-result").TaggedErrorClass<"UnsupportedFolioDocxXmlPatchApplicationProfileError", {
|
|
66
|
+
message: string;
|
|
67
|
+
profile: unknown;
|
|
68
|
+
}>;
|
|
69
|
+
declare class UnsupportedFolioDocxXmlPatchApplicationProfileError extends UnsupportedFolioDocxXmlPatchApplicationProfileError_base {}
|
|
70
|
+
declare const FolioDocxXmlPatchApplicationError_base: import("better-result").TaggedErrorClass<"FolioDocxXmlPatchApplicationError", {
|
|
71
|
+
message: string;
|
|
72
|
+
stage: "verify" | "load" | "replace" | "generate";
|
|
73
|
+
part?: string;
|
|
74
|
+
cause?: unknown;
|
|
75
|
+
}>;
|
|
76
|
+
declare class FolioDocxXmlPatchApplicationError extends FolioDocxXmlPatchApplicationError_base {}
|
|
77
|
+
/** Apply a guarded XML proposal and return output only after full profile validation. */
|
|
78
|
+
declare const applyDocxXmlPatchProposal: ({
|
|
79
|
+
bytes,
|
|
80
|
+
proposal: rawProposal,
|
|
81
|
+
allowedParts,
|
|
82
|
+
validationProfile,
|
|
83
|
+
archive,
|
|
84
|
+
limits
|
|
85
|
+
}: ApplyDocxXmlPatchProposalArgs) => Promise<FolioDocxXmlPatchApplication>;
|
|
86
|
+
//#endregion
|
|
87
|
+
export { ApplyDocxXmlPatchProposalArgs, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FolioDocxXmlPatchApplication, FolioDocxXmlPatchApplicationError, FolioDocxXmlPatchApplicationReceipt, UnsupportedFolioDocxXmlPatchApplicationProfileError, applyDocxXmlPatchProposal };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { loadDocxArchive } from "./boundedArchive.js";
|
|
2
|
+
import { FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, InvalidFolioDocxXmlPatchProposalError, evaluateDocxXmlPatchProposal, parseFolioDocxXmlPatchProposal } from "./evaluateDocxXmlPatchProposal.js";
|
|
3
|
+
import { FOLIO_DOCX_CONFORMANCE_PROFILE, validateDocxConformance } from "./validateDocxConformance.js";
|
|
4
|
+
import { TaggedError, panic } from "better-result";
|
|
5
|
+
import JSZip from "jszip";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
//#region src/docx/server/applyDocxXmlPatchProposal.ts
|
|
8
|
+
const FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION = 1;
|
|
9
|
+
const FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE = "folio-xml-patch-application-v1";
|
|
10
|
+
const isConformantReport = (report) => report.status === "conformant";
|
|
11
|
+
var UnsupportedFolioDocxXmlPatchApplicationProfileError = class extends TaggedError("UnsupportedFolioDocxXmlPatchApplicationProfileError")() {};
|
|
12
|
+
var FolioDocxXmlPatchApplicationError = class extends TaggedError("FolioDocxXmlPatchApplicationError")() {};
|
|
13
|
+
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
14
|
+
const snapshotInput = (bytes) => Uint8Array.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes));
|
|
15
|
+
const snapshotArchiveOptions = (archive) => {
|
|
16
|
+
if (archive === void 0) return;
|
|
17
|
+
return {
|
|
18
|
+
...archive.maxInputBytes === void 0 ? {} : { maxInputBytes: archive.maxInputBytes },
|
|
19
|
+
...archive.maxEntryBytes === void 0 ? {} : { maxEntryBytes: archive.maxEntryBytes },
|
|
20
|
+
...archive.maxTotalBytes === void 0 ? {} : { maxTotalBytes: archive.maxTotalBytes },
|
|
21
|
+
...archive.maxEntries === void 0 ? {} : { maxEntries: archive.maxEntries }
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
const snapshotProposalLimits = (limits) => {
|
|
25
|
+
if (limits === void 0) return;
|
|
26
|
+
return {
|
|
27
|
+
...limits.maxReplacements === void 0 ? {} : { maxReplacements: limits.maxReplacements },
|
|
28
|
+
...limits.maxPartBytes === void 0 ? {} : { maxPartBytes: limits.maxPartBytes },
|
|
29
|
+
...limits.maxTotalBytes === void 0 ? {} : { maxTotalBytes: limits.maxTotalBytes }
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const evaluateProposal = async ({ bytes, proposal, allowedParts, archive, limits }) => await evaluateDocxXmlPatchProposal({
|
|
33
|
+
bytes,
|
|
34
|
+
proposal,
|
|
35
|
+
allowedParts,
|
|
36
|
+
...archive === void 0 ? {} : { archive },
|
|
37
|
+
...limits === void 0 ? {} : { limits }
|
|
38
|
+
});
|
|
39
|
+
const proposalRejected = ({ evaluation }) => ({
|
|
40
|
+
version: 1,
|
|
41
|
+
profile: FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE,
|
|
42
|
+
status: "proposal-rejected",
|
|
43
|
+
producesOutput: false,
|
|
44
|
+
evaluation,
|
|
45
|
+
conformance: null,
|
|
46
|
+
receipt: null
|
|
47
|
+
});
|
|
48
|
+
const verifyArchiveContents = async (bytes, options) => {
|
|
49
|
+
let archive;
|
|
50
|
+
try {
|
|
51
|
+
archive = await loadDocxArchive(bytes, options);
|
|
52
|
+
} catch (cause) {
|
|
53
|
+
throw new FolioDocxXmlPatchApplicationError({
|
|
54
|
+
message: "The evaluated package could not be verified within the archive limits.",
|
|
55
|
+
stage: "verify",
|
|
56
|
+
cause
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
for (const entry of archive.entryMetadata.toSorted(({ path: left }, { path: right }) => left < right ? -1 : Number(left > right))) {
|
|
60
|
+
if (entry.directory) continue;
|
|
61
|
+
try {
|
|
62
|
+
if (await archive.readEntryUint8(entry.path) === null) throw new FolioDocxXmlPatchApplicationError({
|
|
63
|
+
message: "An archive entry disappeared during verification.",
|
|
64
|
+
stage: "verify",
|
|
65
|
+
part: entry.path
|
|
66
|
+
});
|
|
67
|
+
} catch (cause) {
|
|
68
|
+
if (cause instanceof FolioDocxXmlPatchApplicationError) throw cause;
|
|
69
|
+
throw new FolioDocxXmlPatchApplicationError({
|
|
70
|
+
message: "An archive entry could not be verified within the configured limits.",
|
|
71
|
+
stage: "verify",
|
|
72
|
+
part: entry.path,
|
|
73
|
+
cause
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const applyReplacements = async ({ bytes, proposal }) => {
|
|
79
|
+
let zip;
|
|
80
|
+
try {
|
|
81
|
+
zip = await JSZip.loadAsync(bytes);
|
|
82
|
+
} catch (cause) {
|
|
83
|
+
throw new FolioDocxXmlPatchApplicationError({
|
|
84
|
+
message: "The evaluated package could not be loaded for replacement.",
|
|
85
|
+
stage: "load",
|
|
86
|
+
cause
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
for (const replacement of proposal.replacements) {
|
|
90
|
+
const current = zip.file(replacement.path);
|
|
91
|
+
if (current === null) throw new FolioDocxXmlPatchApplicationError({
|
|
92
|
+
message: "An evaluated package part was unavailable during replacement.",
|
|
93
|
+
stage: "replace"
|
|
94
|
+
});
|
|
95
|
+
zip.file(replacement.path, new TextEncoder().encode(replacement.replacementXml), {
|
|
96
|
+
binary: true,
|
|
97
|
+
date: current.date,
|
|
98
|
+
comment: current.comment,
|
|
99
|
+
createFolders: false,
|
|
100
|
+
unixPermissions: current.unixPermissions,
|
|
101
|
+
dosPermissions: current.dosPermissions,
|
|
102
|
+
compression: "DEFLATE",
|
|
103
|
+
compressionOptions: { level: 6 }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
return await zip.generateAsync({
|
|
108
|
+
type: "uint8array",
|
|
109
|
+
compression: "DEFLATE",
|
|
110
|
+
compressionOptions: { level: 6 }
|
|
111
|
+
});
|
|
112
|
+
} catch (cause) {
|
|
113
|
+
throw new FolioDocxXmlPatchApplicationError({
|
|
114
|
+
message: "The replacement package could not be generated.",
|
|
115
|
+
stage: "generate",
|
|
116
|
+
cause
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const outputRejected = ({ evaluation, conformance }) => ({
|
|
121
|
+
version: 1,
|
|
122
|
+
profile: FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE,
|
|
123
|
+
status: "output-rejected",
|
|
124
|
+
producesOutput: false,
|
|
125
|
+
evaluation,
|
|
126
|
+
conformance,
|
|
127
|
+
receipt: null
|
|
128
|
+
});
|
|
129
|
+
const applied = ({ input, output, evaluation, conformance }) => ({
|
|
130
|
+
version: 1,
|
|
131
|
+
profile: FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE,
|
|
132
|
+
status: "applied",
|
|
133
|
+
producesOutput: true,
|
|
134
|
+
evaluation,
|
|
135
|
+
conformance,
|
|
136
|
+
receipt: {
|
|
137
|
+
version: 1,
|
|
138
|
+
profile: FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE,
|
|
139
|
+
proposalProfile: FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE,
|
|
140
|
+
validationProfile: FOLIO_DOCX_CONFORMANCE_PROFILE,
|
|
141
|
+
input: {
|
|
142
|
+
sha256: sha256(input),
|
|
143
|
+
byteLength: input.byteLength
|
|
144
|
+
},
|
|
145
|
+
output: {
|
|
146
|
+
sha256: sha256(output),
|
|
147
|
+
byteLength: output.byteLength
|
|
148
|
+
},
|
|
149
|
+
replacements: evaluation.replacements
|
|
150
|
+
},
|
|
151
|
+
bytes: output
|
|
152
|
+
});
|
|
153
|
+
/** Apply a guarded XML proposal and return output only after full profile validation. */
|
|
154
|
+
const applyDocxXmlPatchProposal = async ({ bytes, proposal: rawProposal, allowedParts, validationProfile, archive, limits }) => {
|
|
155
|
+
if (validationProfile !== "folio-supported-v1") throw new UnsupportedFolioDocxXmlPatchApplicationProfileError({
|
|
156
|
+
message: "The requested package validation profile is not supported.",
|
|
157
|
+
profile: validationProfile
|
|
158
|
+
});
|
|
159
|
+
const input = snapshotInput(bytes);
|
|
160
|
+
const policyAllowedParts = [...allowedParts];
|
|
161
|
+
const policyArchive = snapshotArchiveOptions(archive);
|
|
162
|
+
const policyLimits = snapshotProposalLimits(limits);
|
|
163
|
+
let proposal;
|
|
164
|
+
try {
|
|
165
|
+
proposal = parseFolioDocxXmlPatchProposal(rawProposal);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (!(error instanceof InvalidFolioDocxXmlPatchProposalError)) throw error;
|
|
168
|
+
const evaluation = await evaluateProposal({
|
|
169
|
+
bytes: input,
|
|
170
|
+
proposal: rawProposal,
|
|
171
|
+
allowedParts: policyAllowedParts,
|
|
172
|
+
archive: policyArchive,
|
|
173
|
+
limits: policyLimits
|
|
174
|
+
});
|
|
175
|
+
if (evaluation.status !== "rejected") return panic("An invalid XML proposal produced an accepted evaluation");
|
|
176
|
+
return proposalRejected({ evaluation });
|
|
177
|
+
}
|
|
178
|
+
const evaluation = await evaluateProposal({
|
|
179
|
+
bytes: input,
|
|
180
|
+
proposal,
|
|
181
|
+
allowedParts: policyAllowedParts,
|
|
182
|
+
archive: policyArchive,
|
|
183
|
+
limits: policyLimits
|
|
184
|
+
});
|
|
185
|
+
if (evaluation.status === "rejected") return proposalRejected({ evaluation });
|
|
186
|
+
await verifyArchiveContents(input, policyArchive);
|
|
187
|
+
const output = await applyReplacements({
|
|
188
|
+
bytes: input,
|
|
189
|
+
proposal
|
|
190
|
+
});
|
|
191
|
+
const conformance = await validateDocxConformance(output, { ...policyArchive === void 0 ? {} : { archive: policyArchive } });
|
|
192
|
+
if (!isConformantReport(conformance)) return outputRejected({
|
|
193
|
+
evaluation,
|
|
194
|
+
conformance
|
|
195
|
+
});
|
|
196
|
+
return applied({
|
|
197
|
+
input,
|
|
198
|
+
output,
|
|
199
|
+
evaluation,
|
|
200
|
+
conformance
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
export { FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FolioDocxXmlPatchApplicationError, UnsupportedFolioDocxXmlPatchApplicationProfileError, applyDocxXmlPatchProposal };
|
|
@@ -2,23 +2,34 @@
|
|
|
2
2
|
declare const DOCX_MAX_ENTRY_BYTES: number;
|
|
3
3
|
declare const DOCX_MAX_TOTAL_BYTES: number;
|
|
4
4
|
declare const DOCX_MAX_ENTRIES = 4096;
|
|
5
|
+
declare const DOCX_MAX_INPUT_BYTES: number;
|
|
5
6
|
declare const DocxArchiveError_base: import("better-result").TaggedErrorClass<"DocxArchiveError", {
|
|
6
7
|
message: string;
|
|
7
|
-
reason: "load-failed" | "too-many-entries" | "entry-too-large" | "total-too-large";
|
|
8
|
+
reason: "load-failed" | "input-too-large" | "too-many-entries" | "entry-too-large" | "total-too-large" | "invalid-options";
|
|
8
9
|
cause?: unknown;
|
|
9
10
|
}>;
|
|
10
11
|
/** Error raised when a DOCX archive cannot be loaded within configured limits. */
|
|
11
12
|
declare class DocxArchiveError extends DocxArchiveError_base {}
|
|
12
13
|
type DocxArchiveOptions = {
|
|
14
|
+
maxInputBytes?: number;
|
|
13
15
|
maxEntryBytes?: number;
|
|
14
16
|
maxTotalBytes?: number;
|
|
15
17
|
maxEntries?: number;
|
|
16
18
|
};
|
|
19
|
+
type DocxArchiveEntry = {
|
|
20
|
+
readonly path: string;
|
|
21
|
+
readonly directory: boolean;
|
|
22
|
+
readonly declaredUncompressedBytes: number | null;
|
|
23
|
+
};
|
|
24
|
+
type DocxArchiveReadOptions = {
|
|
25
|
+
maxBytes?: number;
|
|
26
|
+
};
|
|
17
27
|
type DocxArchive = {
|
|
18
28
|
entries: readonly string[];
|
|
29
|
+
entryMetadata: readonly DocxArchiveEntry[];
|
|
19
30
|
readEntryString: (path: string) => Promise<string | null>;
|
|
20
|
-
readEntryUint8: (path: string) => Promise<Uint8Array | null>;
|
|
31
|
+
readEntryUint8: (path: string, options?: DocxArchiveReadOptions) => Promise<Uint8Array | null>;
|
|
21
32
|
};
|
|
22
33
|
declare const loadDocxArchive: (bytes: ArrayBuffer | Uint8Array, options?: DocxArchiveOptions) => Promise<DocxArchive>;
|
|
23
34
|
//#endregion
|
|
24
|
-
export { DOCX_MAX_ENTRIES, DOCX_MAX_ENTRY_BYTES, DOCX_MAX_TOTAL_BYTES, DocxArchive, DocxArchiveError, DocxArchiveOptions, loadDocxArchive };
|
|
35
|
+
export { DOCX_MAX_ENTRIES, DOCX_MAX_ENTRY_BYTES, DOCX_MAX_INPUT_BYTES, DOCX_MAX_TOTAL_BYTES, DocxArchive, DocxArchiveEntry, DocxArchiveError, DocxArchiveOptions, DocxArchiveReadOptions, loadDocxArchive };
|
|
@@ -4,6 +4,7 @@ import JSZip from "jszip";
|
|
|
4
4
|
const DOCX_MAX_ENTRY_BYTES = 128 * 1024 * 1024;
|
|
5
5
|
const DOCX_MAX_TOTAL_BYTES = 256 * 1024 * 1024;
|
|
6
6
|
const DOCX_MAX_ENTRIES = 4096;
|
|
7
|
+
const DOCX_MAX_INPUT_BYTES = 50 * 1024 * 1024;
|
|
7
8
|
/** Error raised when a DOCX archive cannot be loaded within configured limits. */
|
|
8
9
|
var DocxArchiveError = class extends TaggedError("DocxArchiveError")() {};
|
|
9
10
|
const collectStream = async ({ stream, maxEntryBytes, remainingBytes, maxTotalBytes, path }) => await new Promise((resolve, reject) => {
|
|
@@ -33,10 +34,44 @@ const collectStream = async ({ stream, maxEntryBytes, remainingBytes, maxTotalBy
|
|
|
33
34
|
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
34
35
|
stream.on("error", reject);
|
|
35
36
|
});
|
|
37
|
+
const getDeclaredUncompressedBytes = (entry) => {
|
|
38
|
+
const data = "_data" in entry ? entry._data : void 0;
|
|
39
|
+
const declaredBytes = typeof data === "object" && data !== null && "uncompressedSize" in data ? data.uncompressedSize : void 0;
|
|
40
|
+
return typeof declaredBytes === "number" && Number.isFinite(declaredBytes) ? declaredBytes : null;
|
|
41
|
+
};
|
|
42
|
+
const resolveByteLimit = ({ value, fallback, name }) => {
|
|
43
|
+
const limit = value ?? fallback;
|
|
44
|
+
if (!Number.isSafeInteger(limit) || limit < 0) throw new DocxArchiveError({
|
|
45
|
+
message: `${name} must be a non-negative safe integer`,
|
|
46
|
+
reason: "invalid-options"
|
|
47
|
+
});
|
|
48
|
+
return limit;
|
|
49
|
+
};
|
|
36
50
|
const loadDocxArchive = async (bytes, options = {}) => {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
const maxInputBytes = resolveByteLimit({
|
|
52
|
+
value: options.maxInputBytes,
|
|
53
|
+
fallback: DOCX_MAX_INPUT_BYTES,
|
|
54
|
+
name: "DOCX input byte limit"
|
|
55
|
+
});
|
|
56
|
+
const maxEntryBytes = resolveByteLimit({
|
|
57
|
+
value: options.maxEntryBytes,
|
|
58
|
+
fallback: DOCX_MAX_ENTRY_BYTES,
|
|
59
|
+
name: "DOCX entry byte limit"
|
|
60
|
+
});
|
|
61
|
+
const maxTotalBytes = resolveByteLimit({
|
|
62
|
+
value: options.maxTotalBytes,
|
|
63
|
+
fallback: DOCX_MAX_TOTAL_BYTES,
|
|
64
|
+
name: "DOCX cumulative byte limit"
|
|
65
|
+
});
|
|
66
|
+
const maxEntries = resolveByteLimit({
|
|
67
|
+
value: options.maxEntries,
|
|
68
|
+
fallback: DOCX_MAX_ENTRIES,
|
|
69
|
+
name: "DOCX entry limit"
|
|
70
|
+
});
|
|
71
|
+
if (bytes.byteLength > maxInputBytes) throw new DocxArchiveError({
|
|
72
|
+
message: `DOCX input contains ${bytes.byteLength} bytes (max ${maxInputBytes})`,
|
|
73
|
+
reason: "input-too-large"
|
|
74
|
+
});
|
|
40
75
|
let zip;
|
|
41
76
|
try {
|
|
42
77
|
zip = await JSZip.loadAsync(bytes);
|
|
@@ -54,9 +89,9 @@ const loadDocxArchive = async (bytes, options = {}) => {
|
|
|
54
89
|
});
|
|
55
90
|
let declaredTotalBytes = 0;
|
|
56
91
|
for (const entry of archiveEntries) {
|
|
57
|
-
|
|
58
|
-
const declaredBytes =
|
|
59
|
-
if (
|
|
92
|
+
if (entry.dir) continue;
|
|
93
|
+
const declaredBytes = getDeclaredUncompressedBytes(entry);
|
|
94
|
+
if (declaredBytes === null) {
|
|
60
95
|
declaredTotalBytes = NaN;
|
|
61
96
|
break;
|
|
62
97
|
}
|
|
@@ -72,13 +107,18 @@ const loadDocxArchive = async (bytes, options = {}) => {
|
|
|
72
107
|
});
|
|
73
108
|
let totalBytesRead = 0;
|
|
74
109
|
let readChain = Promise.resolve();
|
|
75
|
-
const readEntry = async (path) => {
|
|
110
|
+
const readEntry = async (path, readOptions = {}) => {
|
|
111
|
+
const requestedMaxBytes = resolveByteLimit({
|
|
112
|
+
value: readOptions.maxBytes,
|
|
113
|
+
fallback: maxEntryBytes,
|
|
114
|
+
name: "DOCX entry read byte limit"
|
|
115
|
+
});
|
|
76
116
|
const work = async () => {
|
|
77
117
|
const entry = zip.file(path);
|
|
78
118
|
if (!entry) return null;
|
|
79
119
|
const buffer = await collectStream({
|
|
80
120
|
stream: entry.nodeStream("nodebuffer"),
|
|
81
|
-
maxEntryBytes,
|
|
121
|
+
maxEntryBytes: Math.min(requestedMaxBytes, maxEntryBytes),
|
|
82
122
|
remainingBytes: maxTotalBytes - totalBytesRead,
|
|
83
123
|
maxTotalBytes,
|
|
84
124
|
path
|
|
@@ -92,15 +132,20 @@ const loadDocxArchive = async (bytes, options = {}) => {
|
|
|
92
132
|
};
|
|
93
133
|
return {
|
|
94
134
|
entries: Object.freeze(archiveEntries.map(({ name }) => name)),
|
|
135
|
+
entryMetadata: Object.freeze(archiveEntries.map((entry) => ({
|
|
136
|
+
path: entry.name,
|
|
137
|
+
directory: entry.dir,
|
|
138
|
+
declaredUncompressedBytes: getDeclaredUncompressedBytes(entry)
|
|
139
|
+
}))),
|
|
95
140
|
async readEntryString(path) {
|
|
96
141
|
const buffer = await readEntry(path);
|
|
97
142
|
return buffer === null ? null : buffer.toString("utf-8");
|
|
98
143
|
},
|
|
99
|
-
async readEntryUint8(path) {
|
|
100
|
-
const buffer = await readEntry(path);
|
|
144
|
+
async readEntryUint8(path, readOptions) {
|
|
145
|
+
const buffer = await readEntry(path, readOptions);
|
|
101
146
|
return buffer === null ? null : new Uint8Array(buffer);
|
|
102
147
|
}
|
|
103
148
|
};
|
|
104
149
|
};
|
|
105
150
|
//#endregion
|
|
106
|
-
export { DOCX_MAX_ENTRIES, DOCX_MAX_ENTRY_BYTES, DOCX_MAX_TOTAL_BYTES, DocxArchiveError, loadDocxArchive };
|
|
151
|
+
export { DOCX_MAX_ENTRIES, DOCX_MAX_ENTRY_BYTES, DOCX_MAX_INPUT_BYTES, DOCX_MAX_TOTAL_BYTES, DocxArchiveError, loadDocxArchive };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { DocxArchiveOptions } from "./boundedArchive.js";
|
|
2
|
+
|
|
3
|
+
//#region src/docx/server/evaluateDocxXmlPatchProposal.d.ts
|
|
4
|
+
declare const FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION: 1;
|
|
5
|
+
declare const FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE: "folio-xml-patch-proposal-v1";
|
|
6
|
+
declare const FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS: Readonly<{
|
|
7
|
+
readonly maxReplacements: 16;
|
|
8
|
+
readonly maxPartBytes: number;
|
|
9
|
+
readonly maxTotalBytes: number;
|
|
10
|
+
}>;
|
|
11
|
+
declare const FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES: readonly ["invalid-proposal", "unsupported-version", "too-many-replacements", "duplicate-part", "invalid-part-path", "invalid-base-sha256", "part-not-allowed", "part-not-found", "part-not-xml", "replacement-too-large", "replacements-too-large", "xml-doctype-forbidden", "xml-not-well-formed", "xml-encoding-mismatch", "base-hash-mismatch", "package-inspection-failed"];
|
|
12
|
+
type FolioDocxXmlPatchProposalIssueCode = (typeof FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES)[number];
|
|
13
|
+
type FolioDocxXmlReplacement = {
|
|
14
|
+
readonly path: string;
|
|
15
|
+
readonly baseSha256: string;
|
|
16
|
+
readonly replacementXml: string;
|
|
17
|
+
};
|
|
18
|
+
type FolioDocxXmlPatchProposal = {
|
|
19
|
+
readonly version: typeof FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION;
|
|
20
|
+
readonly replacements: readonly [FolioDocxXmlReplacement, ...FolioDocxXmlReplacement[]];
|
|
21
|
+
};
|
|
22
|
+
type FolioDocxXmlPatchProposalLimits = {
|
|
23
|
+
readonly maxReplacements?: number;
|
|
24
|
+
readonly maxPartBytes?: number;
|
|
25
|
+
readonly maxTotalBytes?: number;
|
|
26
|
+
};
|
|
27
|
+
type EvaluateDocxXmlPatchProposalArgs = {
|
|
28
|
+
readonly bytes: ArrayBuffer | Uint8Array;
|
|
29
|
+
readonly proposal: unknown; /** Exact package paths authorized by the server-side caller. */
|
|
30
|
+
readonly allowedParts: readonly string[];
|
|
31
|
+
readonly archive?: DocxArchiveOptions;
|
|
32
|
+
readonly limits?: FolioDocxXmlPatchProposalLimits;
|
|
33
|
+
};
|
|
34
|
+
type FolioDocxXmlPatchProposalIssue = {
|
|
35
|
+
readonly code: FolioDocxXmlPatchProposalIssueCode;
|
|
36
|
+
readonly message: string;
|
|
37
|
+
readonly proposalPath?: string;
|
|
38
|
+
readonly part?: string;
|
|
39
|
+
};
|
|
40
|
+
type FolioDocxPreparedXmlReplacement = {
|
|
41
|
+
readonly path: string;
|
|
42
|
+
readonly baseSha256: string;
|
|
43
|
+
readonly currentSha256: string;
|
|
44
|
+
readonly replacementSha256: string;
|
|
45
|
+
readonly replacementByteLength: number;
|
|
46
|
+
readonly encoding: "utf-8";
|
|
47
|
+
};
|
|
48
|
+
type FolioDocxXmlPatchProposalEvaluation = {
|
|
49
|
+
readonly version: typeof FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION;
|
|
50
|
+
readonly profile: typeof FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE;
|
|
51
|
+
readonly status: "accepted";
|
|
52
|
+
readonly producesOutput: false;
|
|
53
|
+
readonly issues: readonly [];
|
|
54
|
+
readonly replacements: readonly [FolioDocxPreparedXmlReplacement, ...FolioDocxPreparedXmlReplacement[]];
|
|
55
|
+
readonly limits: Required<FolioDocxXmlPatchProposalLimits>;
|
|
56
|
+
} | {
|
|
57
|
+
readonly version: typeof FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION;
|
|
58
|
+
readonly profile: typeof FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE;
|
|
59
|
+
readonly status: "rejected";
|
|
60
|
+
readonly producesOutput: false;
|
|
61
|
+
readonly issues: readonly [FolioDocxXmlPatchProposalIssue, ...FolioDocxXmlPatchProposalIssue[]];
|
|
62
|
+
readonly replacements: readonly [];
|
|
63
|
+
readonly limits: Required<FolioDocxXmlPatchProposalLimits>;
|
|
64
|
+
};
|
|
65
|
+
declare const InvalidFolioDocxXmlPatchProposalError_base: import("better-result").TaggedErrorClass<"InvalidFolioDocxXmlPatchProposalError", {
|
|
66
|
+
message: string;
|
|
67
|
+
path: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
}>;
|
|
70
|
+
declare class InvalidFolioDocxXmlPatchProposalError extends InvalidFolioDocxXmlPatchProposalError_base {}
|
|
71
|
+
declare const InvalidFolioDocxXmlPatchProposalOptionsError_base: import("better-result").TaggedErrorClass<"InvalidFolioDocxXmlPatchProposalOptionsError", {
|
|
72
|
+
message: string;
|
|
73
|
+
path: string;
|
|
74
|
+
}>;
|
|
75
|
+
declare class InvalidFolioDocxXmlPatchProposalOptionsError extends InvalidFolioDocxXmlPatchProposalOptionsError_base {}
|
|
76
|
+
declare const parseFolioDocxXmlPatchProposal: (value: unknown) => FolioDocxXmlPatchProposal;
|
|
77
|
+
declare const evaluateDocxXmlPatchProposal: ({
|
|
78
|
+
bytes,
|
|
79
|
+
proposal: rawProposal,
|
|
80
|
+
allowedParts,
|
|
81
|
+
archive,
|
|
82
|
+
limits: rawLimits
|
|
83
|
+
}: EvaluateDocxXmlPatchProposalArgs) => Promise<FolioDocxXmlPatchProposalEvaluation>;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { EvaluateDocxXmlPatchProposalArgs, 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, FolioDocxPreparedXmlReplacement, FolioDocxXmlPatchProposal, FolioDocxXmlPatchProposalEvaluation, FolioDocxXmlPatchProposalIssue, FolioDocxXmlPatchProposalIssueCode, FolioDocxXmlPatchProposalLimits, FolioDocxXmlReplacement, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, evaluateDocxXmlPatchProposal, parseFolioDocxXmlPatchProposal };
|