@stll/folio-core 0.37.5 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/ai-edits/apply.js +275 -88
  2. package/dist/ai-edits/clean-text.js +12 -1
  3. package/dist/ai-edits/headless.d.ts +110 -0
  4. package/dist/ai-edits/headless.js +569 -40
  5. package/dist/ai-edits/snapshot.d.ts +24 -1
  6. package/dist/ai-edits/snapshot.js +206 -9
  7. package/dist/ai-edits/table-geometry.d.ts +7 -7
  8. package/dist/ai-edits/table-geometry.js +43 -27
  9. package/dist/ai-edits/table-template.d.ts +2 -1
  10. package/dist/ai-edits/table-template.js +9 -3
  11. package/dist/ai-edits/types.d.ts +37 -15
  12. package/dist/compare/__fixtures__/body-sequence.d.ts +4 -0
  13. package/dist/compare/__fixtures__/body-sequence.js +5 -3
  14. package/dist/compare/compare.d.ts +26 -2
  15. package/dist/compare/compare.js +444 -16
  16. package/dist/compare/content-alignment.js +3 -2
  17. package/dist/compare/content-types.d.ts +34 -2
  18. package/dist/compare/content.d.ts +13 -10
  19. package/dist/compare/content.js +69 -23
  20. package/dist/compare/formatting.d.ts +4 -22
  21. package/dist/compare/formatting.js +8 -112
  22. package/dist/compare/inline-atom-resources.d.ts +6 -0
  23. package/dist/compare/inline-atom-resources.js +26 -0
  24. package/dist/compare/inline-atoms.d.ts +35 -0
  25. package/dist/compare/inline-atoms.js +383 -0
  26. package/dist/compare/inline-provenance.d.ts +36 -0
  27. package/dist/compare/inline-provenance.js +334 -0
  28. package/dist/compare/plan.d.ts +4 -1
  29. package/dist/compare/plan.js +32 -4
  30. package/dist/compare/scenario.d.ts +4 -6
  31. package/dist/compare/section-boundary-properties.d.ts +63 -0
  32. package/dist/compare/section-boundary-properties.js +235 -0
  33. package/dist/compare/style-resources.d.ts +30 -0
  34. package/dist/compare/style-resources.js +404 -0
  35. package/dist/compare/types.d.ts +50 -11
  36. package/dist/compare/types.js +3 -1
  37. package/dist/compare/verification.d.ts +3 -5
  38. package/dist/compare/verification.js +8 -39
  39. package/dist/compat/eigenpal.d.ts +4 -4
  40. package/dist/compat/eigenpal.js +3 -3
  41. package/dist/document-operations.d.ts +5 -3
  42. package/dist/document-operations.js +112 -5
  43. package/dist/docx/blockContentParser.js +14 -2
  44. package/dist/docx/drawingIdNormalization.js +43 -2
  45. package/dist/docx/drawingRelationships.d.ts +10 -0
  46. package/dist/docx/drawingRelationships.js +28 -0
  47. package/dist/docx/fieldParser.js +1 -1
  48. package/dist/docx/hyperlinkParser.js +3 -5
  49. package/dist/docx/imageRawXml.d.ts +8 -0
  50. package/dist/docx/imageRawXml.js +13 -0
  51. package/dist/docx/newImage.d.ts +2 -2
  52. package/dist/docx/newImage.js +3 -3
  53. package/dist/docx/numberingParser.js +30 -27
  54. package/dist/docx/paragraphParser.js +11 -4
  55. package/dist/docx/removeHeaderFooterParts.d.ts +14 -0
  56. package/dist/docx/removeHeaderFooterParts.js +87 -0
  57. package/dist/docx/rezip.d.ts +3 -1
  58. package/dist/docx/rezip.js +143 -41
  59. package/dist/docx/runParser.js +16 -5
  60. package/dist/docx/sectionParser.js +3 -0
  61. package/dist/docx/sectionReferenceHistory.d.ts +17 -0
  62. package/dist/docx/sectionReferenceHistory.js +59 -0
  63. package/dist/docx/selectiveSave.js +11 -31
  64. package/dist/docx/selectiveXmlPatch.d.ts +8 -1
  65. package/dist/docx/selectiveXmlPatch.js +62 -2
  66. package/dist/docx/serializer/paragraphSerializer.js +2 -0
  67. package/dist/docx/serializer/partNamespaces.d.ts +1 -1
  68. package/dist/docx/serializer/partNamespaces.js +4 -0
  69. package/dist/docx/serializer/runSerializer.js +2 -1
  70. package/dist/docx/serializer/sectionPropertiesSerializer.js +5 -2
  71. package/dist/docx/serializer/textFormattingSerializer.d.ts +1 -1
  72. package/dist/docx/serializer/textFormattingSerializer.js +3 -1
  73. package/dist/docx/tableParser.js +21 -11
  74. package/dist/docx/xmlParser.d.ts +10 -1
  75. package/dist/docx/xmlParser.js +23 -1
  76. package/dist/index.d.ts +4 -4
  77. package/dist/index.js +3 -3
  78. package/dist/internal/compare/inline-presentation.d.ts +20 -0
  79. package/dist/internal/compare/inline-presentation.js +222 -0
  80. package/dist/internal/paragraphFormattingSerialization.d.ts +3 -1
  81. package/dist/internal/paragraphFormattingSerialization.js +8 -1
  82. package/dist/internal/sectionReferenceResolution.d.ts +21 -0
  83. package/dist/internal/sectionReferenceResolution.js +72 -0
  84. package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
  85. package/dist/markdown/renderRuns.js +1 -0
  86. package/dist/markdown/renderTable.js +1 -0
  87. package/dist/prosemirror/attrs/index.js +20 -1
  88. package/dist/prosemirror/commands/comments.d.ts +11 -1
  89. package/dist/prosemirror/commands/comments.js +68 -6
  90. package/dist/prosemirror/commands/propertyChangeScope.d.ts +9 -3
  91. package/dist/prosemirror/commands/propertyChangeScope.js +4 -10
  92. package/dist/prosemirror/conversion/fromProseDoc.js +16 -8
  93. package/dist/prosemirror/conversion/toProseDoc.js +16 -57
  94. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
  95. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +3 -0
  96. package/dist/prosemirror/imageCommit.js +7 -9
  97. package/dist/prosemirror/paragraphIndentation.d.ts +21 -0
  98. package/dist/prosemirror/paragraphIndentation.js +72 -0
  99. package/dist/prosemirror/plugins/documentNumbering.d.ts +3 -1
  100. package/dist/prosemirror/plugins/documentNumbering.js +7 -1
  101. package/dist/prosemirror/plugins/documentStyles.d.ts +3 -1
  102. package/dist/prosemirror/plugins/documentStyles.js +7 -1
  103. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +3 -1
  104. package/dist/prosemirror/rebaseParagraphRunFormatting.js +2 -1
  105. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +15 -2
  106. package/dist/prosemirror/runFormattingInlineCarriers.js +33 -3
  107. package/dist/prosemirror/runStyleFormatting.d.ts +15 -2
  108. package/dist/prosemirror/runStyleFormatting.js +76 -2
  109. package/dist/prosemirror/schema/marks.d.ts +3 -1
  110. package/dist/prosemirror/schema/marks.js +2 -0
  111. package/dist/prosemirror/schema/nodes.d.ts +3 -0
  112. package/dist/prosemirror/tableCellRevisionVisibility.d.ts +6 -0
  113. package/dist/prosemirror/tableCellRevisionVisibility.js +14 -0
  114. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
  115. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
  116. package/dist/redline.js +15 -2
  117. package/dist/server.d.ts +2 -2
  118. package/dist/server.js +2 -2
  119. package/dist/version-comparison.d.ts +2 -0
  120. package/dist/version-comparison.js +7 -2
  121. package/package.json +2 -2
@@ -7,7 +7,7 @@ import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
7
7
  import { parseSdtProperties } from "./sdtProperties.js";
8
8
  import { parseTable } from "./tableParser.js";
9
9
  import { captureVerbatimXml } from "./verbatimCapture.js";
10
- import { findChild, getChildElements, getLocalName, mergeXmlnsDeclarations } from "./xmlParser.js";
10
+ import { findChild, getChildElements, getLocalName, mergeXmlnsDeclarations, selectAlternateContentBranch } from "./xmlParser.js";
11
11
  //#region src/docx/blockContentParser.ts
12
12
  const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
13
13
  const listRendering = paragraph.listRendering;
@@ -80,6 +80,13 @@ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounter
80
80
  }
81
81
  listRendering.marker = computedMarker;
82
82
  };
83
+ const withContainerXmlns = (state, element) => ({
84
+ ...state,
85
+ options: {
86
+ ...state.options,
87
+ rootXmlns: mergeXmlnsDeclarations(state.options?.rootXmlns ?? {}, element)
88
+ }
89
+ });
83
90
  const parseBlockContent = (parent, styles, theme, numbering, rels, media, options) => parseBlockContentWithState(parent, styles, theme, numbering, rels, media, {
84
91
  listCounters: /* @__PURE__ */ new Map(),
85
92
  abstractCounters: /* @__PURE__ */ new Map(),
@@ -132,12 +139,17 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
132
139
  const blockSdt = {
133
140
  type: "blockSdt",
134
141
  properties,
135
- content: sdtContent ? parseBlockContentWithState(sdtContent, styles, theme, numbering, rels, media, state) : []
142
+ content: sdtContent ? parseBlockContentWithState(sdtContent, styles, theme, numbering, rels, media, withContainerXmlns(withContainerXmlns(state, child), sdtContent)) : []
136
143
  };
137
144
  if (prependBookmarkMarkersToFirstParagraphInBlocks(blockSdt.content, pendingBookmarkMarkers)) pendingBookmarkMarkers.length = 0;
138
145
  content.push(blockSdt);
139
146
  continue;
140
147
  }
148
+ if (localName === "AlternateContent") {
149
+ const selectedBranch = selectAlternateContentBranch(child);
150
+ if (selectedBranch) content.push(...parseBlockContentWithState(selectedBranch, styles, theme, numbering, rels, media, withContainerXmlns(withContainerXmlns(state, child), selectedBranch)));
151
+ continue;
152
+ }
141
153
  if (localName === "bookmarkStart" || localName === "bookmarkEnd") {
142
154
  const marker = parseBookmarkMarker(child, localName);
143
155
  if (!appendBookmarkMarkerToLastParagraphInBlocks(content, marker)) pendingBookmarkMarkers.push(marker);
@@ -1,6 +1,27 @@
1
+ import { isNewDataUrlDrawing } from "./newImage.js";
1
2
  import { visitDocxParagraphs, visitParagraphRuns } from "./paragraphTraversal.js";
3
+ import { toTransitionalNamespaceUri } from "./transitionalSpelling.js";
4
+ import { captureVerbatimXml } from "./verbatimCapture.js";
5
+ import { NAMESPACES, OOXML_NAMESPACE_SCOPE, getLocalName, getNamespaceUri, parseXml } from "./xmlParser.js";
6
+ import { panic } from "better-result";
2
7
  //#region src/docx/drawingIdNormalization.ts
3
8
  const GENERATED_DRAWING_ID_START = 1e5;
9
+ const isDetachedRawDrawing = (drawing) => drawing?.rawXml !== void 0 && isNewDataUrlDrawing(drawing);
10
+ const reassignRawDrawingId = ({ xml, id }) => {
11
+ const root = parseXml(xml, OOXML_NAMESPACE_SCOPE);
12
+ let foundDocPr = false;
13
+ const visit = (element) => {
14
+ if (toTransitionalNamespaceUri(getNamespaceUri(element) ?? "") === NAMESPACES.wp && getLocalName(element.name) === "docPr" || toTransitionalNamespaceUri(getNamespaceUri(element) ?? "") === NAMESPACES.pic && getLocalName(element.name) === "cNvPr") {
15
+ for (const name of Object.keys(element.attributes ?? {})) if (getLocalName(name) === "id" && element.attributes) {
16
+ element.attributes[name] = id;
17
+ if (toTransitionalNamespaceUri(getNamespaceUri(element) ?? "") === NAMESPACES.wp && getLocalName(element.name) === "docPr") foundDocPr = true;
18
+ }
19
+ }
20
+ for (const child of element.elements ?? []) visit(child);
21
+ };
22
+ visit(root);
23
+ return foundDocPr ? (root.elements ?? []).map(captureVerbatimXml).join("") : null;
24
+ };
4
25
  const needsGeneratedId = ({ id }) => id === void 0 || id === "" || id === "0";
5
26
  const normalizeDrawingIds = (surfaces) => {
6
27
  const entries = [];
@@ -16,12 +37,13 @@ const normalizeDrawingIds = (surfaces) => {
16
37
  }
17
38
  if (content.type === "drawing") entries.push({
18
39
  drawing: content.image,
19
- generateWhenMissing: content.rawXml === void 0
40
+ generateWhenMissing: content.rawXml === void 0,
41
+ ...content.rawXml !== void 0 ? { rawDrawing: content } : {}
20
42
  });
21
43
  }
22
44
  });
23
45
  });
24
- const usedIds = new Set(entries.flatMap(({ drawing }) => needsGeneratedId(drawing) ? [] : [drawing.id]));
46
+ const usedIds = new Set(entries.flatMap(({ drawing, rawDrawing }) => needsGeneratedId(drawing) || isDetachedRawDrawing(rawDrawing) ? [] : [drawing.id]));
25
47
  let nextId = GENERATED_DRAWING_ID_START;
26
48
  for (const { drawing, generateWhenMissing } of entries) {
27
49
  if (!generateWhenMissing || !needsGeneratedId(drawing)) continue;
@@ -30,6 +52,25 @@ const normalizeDrawingIds = (surfaces) => {
30
52
  usedIds.add(drawing.id);
31
53
  nextId += 1;
32
54
  }
55
+ for (const { drawing, rawDrawing } of entries) {
56
+ if (!isDetachedRawDrawing(rawDrawing)) continue;
57
+ if (!needsGeneratedId(drawing) && !usedIds.has(drawing.id)) {
58
+ usedIds.add(drawing.id);
59
+ continue;
60
+ }
61
+ while (usedIds.has(String(nextId))) nextId += 1;
62
+ const sourceXml = rawDrawing.rawXml;
63
+ if (sourceXml === void 0) panic("Detached raw drawing must retain its XML.");
64
+ const rawXml = reassignRawDrawingId({
65
+ xml: sourceXml,
66
+ id: String(nextId)
67
+ });
68
+ if (rawXml === null) panic("Detached raw drawing must contain wp:docPr.");
69
+ drawing.id = String(nextId);
70
+ rawDrawing.rawXml = rawXml;
71
+ usedIds.add(drawing.id);
72
+ nextId += 1;
73
+ }
33
74
  };
34
75
  //#endregion
35
76
  export { normalizeDrawingIds };
@@ -0,0 +1,10 @@
1
+ //#region src/docx/drawingRelationships.d.ts
2
+ type RebindDrawingImageRelationshipOptions = {
3
+ xml: string;
4
+ previousId: string;
5
+ nextId: string;
6
+ };
7
+ /** Only a single embedded image can travel without importing other package resources. */
8
+ declare const rebindDrawingImageRelationship: ({ xml, previousId, nextId }: RebindDrawingImageRelationshipOptions) => string | null;
9
+ //#endregion
10
+ export { rebindDrawingImageRelationship };
@@ -0,0 +1,28 @@
1
+ import { captureVerbatimXml } from "./verbatimCapture.js";
2
+ import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, findAttributeByNamespaceUri, getLocalName, getNamespaceUri, parseXml } from "./xmlParser.js";
3
+ //#region src/docx/drawingRelationships.ts
4
+ /** Only a single embedded image can travel without importing other package resources. */
5
+ const rebindDrawingImageRelationship = ({ xml, previousId, nextId }) => {
6
+ const root = parseXml(xml, OOXML_NAMESPACE_SCOPE);
7
+ let embeddedCount = 0;
8
+ let unsupported = false;
9
+ const visit = (element) => {
10
+ for (const name of Object.keys(element.attributes ?? {})) {
11
+ const localName = getLocalName(name);
12
+ const attribute = findAttributeByNamespaceUri(element, OFFICE_RELATIONSHIP_NAMESPACE_URIS, localName);
13
+ if (!attribute || attribute.name !== name) continue;
14
+ if (!(localName === "embed" || localName === "id" && getLocalName(element.name) === "imagedata" && getNamespaceUri(element) === "urn:schemas-microsoft-com:vml") || attribute.value !== previousId) {
15
+ unsupported = true;
16
+ continue;
17
+ }
18
+ embeddedCount++;
19
+ if (element.attributes) element.attributes[name] = nextId;
20
+ }
21
+ for (const child of element.elements ?? []) visit(child);
22
+ };
23
+ visit(root);
24
+ if (unsupported || embeddedCount !== 1) return null;
25
+ return (root.elements ?? []).map(captureVerbatimXml).join("");
26
+ };
27
+ //#endregion
28
+ export { rebindDrawingImageRelationship };
@@ -209,7 +209,7 @@ function resetComplexFieldContext(ctx) {
209
209
  function finalizeComplexField(ctx) {
210
210
  return {
211
211
  type: "complexField",
212
- instruction: ctx.instruction.trim(),
212
+ instruction: ctx.instruction,
213
213
  fieldType: parseFieldType(ctx.instruction),
214
214
  fieldCode: ctx.codeRuns,
215
215
  fieldResult: ctx.resultRuns,
@@ -52,8 +52,7 @@ function parseHyperlink(node, rels, styles = null, theme = null, media = null, r
52
52
  if (rels) {
53
53
  const rel = rels.get(rId);
54
54
  if (rel) {
55
- const safeHref = sanitizeExternalUrl(rel.target);
56
- if (safeHref) hyperlink.href = safeHref;
55
+ if (sanitizeExternalUrl(rel.target)) hyperlink.href = rel.target;
57
56
  }
58
57
  }
59
58
  }
@@ -172,9 +171,8 @@ function resolveHyperlinkUrl(hyperlink, rels) {
172
171
  if (hyperlink.rId) {
173
172
  const rel = rels.get(hyperlink.rId);
174
173
  if (rel) {
175
- const safeHref = sanitizeExternalUrl(rel.target);
176
- if (safeHref) {
177
- hyperlink.href = safeHref;
174
+ if (sanitizeExternalUrl(rel.target)) {
175
+ hyperlink.href = rel.target;
178
176
  return hyperlink.href;
179
177
  }
180
178
  }
@@ -0,0 +1,8 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/imageRawXml.d.ts
3
+ /** Fingerprints modeled image fields which make raw DrawingML stale when edited. */
4
+ declare const imageRawXmlFingerprint: (image: document_d_exports.Image) => string;
5
+ /** Editable raw drawing XML can replay only while its modeled projection is unchanged. */
6
+ declare const canReplayEditableImageRawXml: (drawing: document_d_exports.DrawingContent) => boolean;
7
+ //#endregion
8
+ export { canReplayEditableImageRawXml, imageRawXmlFingerprint };
@@ -0,0 +1,13 @@
1
+ import { canonicalJson } from "../utils/canonicalJson.js";
2
+ import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
3
+ //#region src/docx/imageRawXml.ts
4
+ const editableImageProjection = ({ id: _id, rId: _rId, src: _src, mimeType: _mimeType, filename: _filename, ...image }) => image;
5
+ /** Fingerprints modeled image fields which make raw DrawingML stale when edited. */
6
+ const imageRawXmlFingerprint = (image) => canonicalJson(editableImageProjection(image));
7
+ /** Editable raw drawing XML can replay only while its modeled projection is unchanged. */
8
+ const canReplayEditableImageRawXml = (drawing) => {
9
+ if (drawing.rawXmlMode === DRAWING_RAW_XML_MODES.PRESERVE_ONLY) return true;
10
+ return drawing.rawImageFingerprint === void 0 || drawing.rawImageFingerprint === imageRawXmlFingerprint(drawing.image);
11
+ };
12
+ //#endregion
13
+ export { canReplayEditableImageRawXml, imageRawXmlFingerprint };
@@ -3,8 +3,8 @@ import { DrawingContent, RunContent } from "../types/content.js";
3
3
  /**
4
4
  * A drawing needs a new package image part only when it is model-driven.
5
5
  *
6
- * Raw OOXML drawings can carry data-URL previews for browser rendering, but
7
- * save replays their raw XML instead of serializing the preview image.
6
+ * Raw OOXML drawings can carry previews for browser rendering. Only an explicit
7
+ * synthetic relationship opts a detached raw picture into media registration.
8
8
  */
9
9
  declare const isNewDataUrlDrawing: (content: RunContent) => content is DrawingContent;
10
10
  //#endregion
@@ -3,9 +3,9 @@ const SYNTHETIC_IMAGE_RID_PREFIX = "rId_img_";
3
3
  /**
4
4
  * A drawing needs a new package image part only when it is model-driven.
5
5
  *
6
- * Raw OOXML drawings can carry data-URL previews for browser rendering, but
7
- * save replays their raw XML instead of serializing the preview image.
6
+ * Raw OOXML drawings can carry previews for browser rendering. Only an explicit
7
+ * synthetic relationship opts a detached raw picture into media registration.
8
8
  */
9
- const isNewDataUrlDrawing = (content) => content.type === "drawing" && !content.rawXml && content.image.src?.startsWith("data:") === true && (!content.image.rId || content.image.rId.startsWith(SYNTHETIC_IMAGE_RID_PREFIX));
9
+ const isNewDataUrlDrawing = (content) => content.type === "drawing" && (!content.rawXml || content.image.rId?.startsWith(SYNTHETIC_IMAGE_RID_PREFIX) === true) && content.image.src?.startsWith("data:") === true && (!content.image.rId || content.image.rId.startsWith(SYNTHETIC_IMAGE_RID_PREFIX));
10
10
  //#endregion
11
11
  export { isNewDataUrlDrawing };
@@ -1,7 +1,7 @@
1
1
  import { formatOoxmlCounter, padDecimal } from "./ooxmlCounterFormatter.js";
2
2
  import { LevelSuffixSchema, narrowEnum } from "./parserEnums.js";
3
3
  import { parseRunProperties } from "./runParser.js";
4
- import { findChild, findChildren, getAttribute, parseBooleanElement, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
4
+ import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildren, getAttribute, getLocalName, getNamespaceUri, parseBooleanElement, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
5
5
  //#region src/docx/numberingParser.ts
6
6
  const NUMBER_FORMAT_MAP = {
7
7
  decimal: "decimal",
@@ -65,6 +65,7 @@ const NUMBER_FORMAT_MAP = {
65
65
  thaiNumbers: "thaiNumbers",
66
66
  thaiCounting: "thaiCounting"
67
67
  };
68
+ const wordprocessingLocalName = (element) => WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "") ? getLocalName(element.name) : null;
68
69
  /**
69
70
  * Parse numbering.xml into NumberingDefinitions
70
71
  *
@@ -110,20 +111,20 @@ function parseAbstractNumbering(element) {
110
111
  const levelElements = [];
111
112
  for (const child of element.elements ?? []) {
112
113
  if (child.type !== "element") continue;
113
- switch (child.name) {
114
- case "w:multiLevelType":
114
+ switch (wordprocessingLocalName(child)) {
115
+ case "multiLevelType":
115
116
  multiLevelTypeEl ??= child;
116
117
  break;
117
- case "w:name":
118
+ case "name":
118
119
  nameEl ??= child;
119
120
  break;
120
- case "w:numStyleLink":
121
+ case "numStyleLink":
121
122
  numStyleLinkEl ??= child;
122
123
  break;
123
- case "w:styleLink":
124
+ case "styleLink":
124
125
  styleLinkEl ??= child;
125
126
  break;
126
- case "w:lvl":
127
+ case "lvl":
127
128
  levelElements.push(child);
128
129
  break;
129
130
  default: break;
@@ -164,11 +165,11 @@ function parseNumberingInstance(element) {
164
165
  const overrideElements = [];
165
166
  for (const child of element.elements ?? []) {
166
167
  if (child.type !== "element") continue;
167
- if (child.name === "w:abstractNumId") {
168
+ if (wordprocessingLocalName(child) === "abstractNumId") {
168
169
  abstractNumIdEl ??= child;
169
170
  continue;
170
171
  }
171
- if (child.name === "w:lvlOverride") overrideElements.push(child);
172
+ if (wordprocessingLocalName(child) === "lvlOverride") overrideElements.push(child);
172
173
  }
173
174
  if (!abstractNumIdEl) return null;
174
175
  const abstractNumIdStr = getAttribute(abstractNumIdEl, "w", "val");
@@ -191,11 +192,11 @@ function parseNumberingInstance(element) {
191
192
  let lvlEl;
192
193
  for (const child of overrideEl.elements ?? []) {
193
194
  if (child.type !== "element") continue;
194
- if (child.name === "w:startOverride") {
195
+ if (wordprocessingLocalName(child) === "startOverride") {
195
196
  startOverrideEl ??= child;
196
197
  continue;
197
198
  }
198
- if (child.name === "w:lvl") lvlEl ??= child;
199
+ if (wordprocessingLocalName(child) === "lvl") lvlEl ??= child;
199
200
  }
200
201
  if (startOverrideEl) {
201
202
  const startVal = getAttribute(startOverrideEl, "w", "val");
@@ -239,38 +240,40 @@ function parseListLevel(element) {
239
240
  let rPrEl;
240
241
  for (const child of element.elements ?? []) {
241
242
  if (child.type !== "element") continue;
242
- switch (child.name) {
243
- case "w:start":
243
+ const localName = wordprocessingLocalName(child);
244
+ if (localName === null) {
245
+ if (getLocalName(child.name) === "AlternateContent") alternateEl ??= child;
246
+ continue;
247
+ }
248
+ switch (localName) {
249
+ case "start":
244
250
  startEl ??= child;
245
251
  break;
246
- case "w:numFmt":
252
+ case "numFmt":
247
253
  numFmtEl ??= child;
248
254
  break;
249
- case "mc:AlternateContent":
250
- alternateEl ??= child;
251
- break;
252
- case "w:lvlText":
255
+ case "lvlText":
253
256
  lvlTextEl ??= child;
254
257
  break;
255
- case "w:lvlJc":
258
+ case "lvlJc":
256
259
  lvlJcEl ??= child;
257
260
  break;
258
- case "w:suff":
261
+ case "suff":
259
262
  suffEl ??= child;
260
263
  break;
261
- case "w:isLgl":
264
+ case "isLgl":
262
265
  isLglEl ??= child;
263
266
  break;
264
- case "w:lvlRestart":
267
+ case "lvlRestart":
265
268
  lvlRestartEl ??= child;
266
269
  break;
267
- case "w:legacy":
270
+ case "legacy":
268
271
  legacyEl ??= child;
269
272
  break;
270
- case "w:pPr":
273
+ case "pPr":
271
274
  pPrEl ??= child;
272
275
  break;
273
- case "w:rPr":
276
+ case "rPr":
274
277
  rPrEl ??= child;
275
278
  break;
276
279
  default: break;
@@ -361,11 +364,11 @@ function parseLevelParagraphProps(pPr) {
361
364
  let tabsEl;
362
365
  for (const child of pPr.elements ?? []) {
363
366
  if (child.type !== "element") continue;
364
- if (child.name === "w:ind") {
367
+ if (wordprocessingLocalName(child) === "ind") {
365
368
  indEl ??= child;
366
369
  continue;
367
370
  }
368
- if (child.name === "w:tabs") tabsEl ??= child;
371
+ if (wordprocessingLocalName(child) === "tabs") tabsEl ??= child;
369
372
  }
370
373
  if (indEl) {
371
374
  const left = parseNumericAttribute(indEl, "w", "left");
@@ -13,10 +13,11 @@ import { parseSdtProperties } from "./sdtProperties.js";
13
13
  import { parseSectionProperties } from "./sectionParser.js";
14
14
  import { parsePropertyChangeInfo, parseTrackedChangeInfo } from "./trackedChangeInfo.js";
15
15
  import { captureVerbatimXml } from "./verbatimCapture.js";
16
- import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findChild, findChildByNamespaceUri, findChildren, findChildrenByNamespaceUri, getAttribute, getChildElements, getLocalName, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute } from "./xmlParser.js";
16
+ import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findChild, findChildByNamespaceUri, findChildren, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute, selectAlternateContentBranch } from "./xmlParser.js";
17
17
  import { panic } from "better-result";
18
- import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
18
+ import { PARAGRAPH_MARK_CHANGE_KINDS, REVIEW_CARRIERS } from "@stll/docx-core/model";
19
19
  //#region src/docx/paragraphParser.ts
20
+ const FOLIO_REVIEW_HISTORY_NAMESPACES = /* @__PURE__ */ new Set(["urn:stella:folio:review-history:1"]);
20
21
  /**
21
22
  * Extract plain text from a math element (recursive text content extraction)
22
23
  */
@@ -532,7 +533,7 @@ function parseParagraphMarkChange(pPr) {
532
533
  }
533
534
  }
534
535
  function isTrackedChangeWrapperChild(content) {
535
- return content.type === "run" || content.type === "hyperlink" || content.type === "bookmarkStart" || content.type === "bookmarkEnd" || content.type === "simpleField" || content.type === "complexField" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo";
536
+ return content.type === "run" || content.type === "hyperlink" || content.type === "bookmarkStart" || content.type === "bookmarkEnd" || content.type === "simpleField" || content.type === "complexField" || content.type === "mathEquation" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo";
536
537
  }
537
538
  function isInlineSdtContent(content) {
538
539
  return content.type === "run" || content.type === "hyperlink" || content.type === "simpleField" || content.type === "complexField" || content.type === "inlineSdt" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo" || content.type === "mathEquation";
@@ -887,7 +888,7 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
887
888
  }];
888
889
  const complexField = {
889
890
  type: "complexField",
890
- instruction: complexFieldInstr.trim(),
891
+ instruction: complexFieldInstr,
891
892
  fieldType: parseFieldType(complexFieldInstr),
892
893
  fieldCode: complexFieldCodeRuns,
893
894
  fieldResult: resultRuns
@@ -911,6 +912,11 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
911
912
  } else if (run.content.length > 0 || hasRunPayloadElement(runElement)) contents.push(run);
912
913
  break;
913
914
  }
915
+ case "AlternateContent": {
916
+ const selectedBranch = selectAlternateContentBranch(child);
917
+ if (selectedBranch) contents.push(...parseParagraphContents(selectedBranch, styles, theme, null, rels, media, trackedContext, mergeXmlnsDeclarations(inScopeXmlns, child)));
918
+ break;
919
+ }
914
920
  case "hyperlink":
915
921
  contents.push(...parseHyperlinkParagraphContents(child, rels, styles, theme, media, inScopeXmlns));
916
922
  break;
@@ -1078,6 +1084,7 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
1078
1084
  if (paraId && isValidHexId(paraId)) paragraph.paraId = paraIdInRange(paraId);
1079
1085
  const textId = getAttribute(node, "w14", "textId") ?? getAttribute(node, "w", "textId");
1080
1086
  if (textId && isValidHexId(textId)) paragraph.textId = paraIdInRange(textId);
1087
+ if (getAttributeByNamespaceUri(node, FOLIO_REVIEW_HISTORY_NAMESPACES, "reviewCarrier") === REVIEW_CARRIERS.TERMINAL_TABLE) paragraph.reviewCarrier = REVIEW_CARRIERS.TERMINAL_TABLE;
1081
1088
  if (!options?.inHeaderFooter && paragraphStartsWithRenderedPageBreak(node)) paragraph.renderedPageBreakBefore = true;
1082
1089
  const pPr = findChild(node, "w", "pPr");
1083
1090
  if (pPr) {
@@ -0,0 +1,14 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { RemovedSectionReference } from "../internal/sectionEndpointResolution.js";
3
+ import JSZip from "jszip";
4
+ //#region src/docx/removeHeaderFooterParts.d.ts
5
+ type RemoveResolvedHeaderFooterPartsOptions = {
6
+ document: document_d_exports.Document;
7
+ zip: JSZip;
8
+ removedReferences: readonly RemovedSectionReference[];
9
+ compressionLevel: number;
10
+ };
11
+ /** Remove only parts whose last selection was explicitly resolved out of the document. */
12
+ declare const removeResolvedHeaderFooterParts: ({ document, zip, removedReferences, compressionLevel }: RemoveResolvedHeaderFooterPartsOptions) => Promise<void>;
13
+ //#endregion
14
+ export { removeResolvedHeaderFooterParts };
@@ -0,0 +1,87 @@
1
+ import { isUnsafePackagePath } from "./packageParts.js";
2
+ import { parseRelationships, resolveRelativePath } from "./relsParser.js";
3
+ import { captureVerbatimXml } from "./verbatimCapture.js";
4
+ import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, cloneElement, getAttribute, getLocalName, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
5
+ import { panic } from "better-result";
6
+ import { OOXML_NS } from "@stll/docx-utils";
7
+ //#region src/docx/removeHeaderFooterParts.ts
8
+ const DOCUMENT_RELS_PATH = "word/_rels/document.xml.rels";
9
+ const officeRelationshipHasType = (type, kind) => [...OFFICE_RELATIONSHIP_NAMESPACE_URIS].some((namespace) => type === `${namespace}/${kind}`);
10
+ const withoutChildren = (xml, remove) => {
11
+ const root = parseXmlDocument(xml);
12
+ if (!root) return panic("Cannot update malformed package metadata");
13
+ return captureVerbatimXml(cloneElement(root, { elements: (root.elements ?? []).filter((child) => !remove(child)) }));
14
+ };
15
+ /** Remove only parts whose last selection was explicitly resolved out of the document. */
16
+ const removeResolvedHeaderFooterParts = async ({ document, zip, removedReferences, compressionLevel }) => {
17
+ if (removedReferences.length === 0) return;
18
+ const relsFile = zip.file(DOCUMENT_RELS_PATH);
19
+ if (!relsFile) return panic("A resolved header/footer part has no document relationships");
20
+ const relsXml = await relsFile.async("text");
21
+ const relationships = parseRelationships(relsXml);
22
+ const removedIds = /* @__PURE__ */ new Set();
23
+ const candidates = /* @__PURE__ */ new Set();
24
+ for (const { part, relationshipId } of removedReferences) {
25
+ if ((part === "header" ? document.package.headers : document.package.footers)?.has(relationshipId)) continue;
26
+ const relationship = relationships.get(relationshipId);
27
+ if (!relationship) continue;
28
+ if (!officeRelationshipHasType(relationship.type, part) || relationship.targetMode === "External") return panic("Resolved header/footer relationship has an unexpected part type");
29
+ const path = resolveRelativePath(DOCUMENT_RELS_PATH, relationship.target);
30
+ if (isUnsafePackagePath(path)) return panic("Resolved header/footer has an unsafe package path");
31
+ removedIds.add(relationshipId);
32
+ candidates.add(path);
33
+ }
34
+ if (removedIds.size === 0) return;
35
+ for (const relationship of relationships.values()) {
36
+ if (removedIds.has(relationship.id) || relationship.targetMode === "External") continue;
37
+ candidates.delete(resolveRelativePath(DOCUMENT_RELS_PATH, relationship.target));
38
+ }
39
+ for (const file of Object.values(zip.files)) {
40
+ if (file.dir || !file.name.endsWith(".rels") || file.name === DOCUMENT_RELS_PATH) continue;
41
+ const otherRelationships = parseRelationships(await file.async("text"));
42
+ for (const relationship of otherRelationships.values()) if (relationship.targetMode !== "External") candidates.delete(resolveRelativePath(file.name, relationship.target));
43
+ }
44
+ const retiredMedia = /* @__PURE__ */ new Set();
45
+ for (const path of candidates) {
46
+ const slash = path.lastIndexOf("/");
47
+ const relsPath = `${path.slice(0, slash + 1)}_rels/${path.slice(slash + 1)}.rels`;
48
+ const partRels = zip.file(relsPath);
49
+ if (!partRels) continue;
50
+ for (const relationship of parseRelationships(await partRels.async("text")).values()) {
51
+ if (!officeRelationshipHasType(relationship.type, "image") || relationship.targetMode === "External") continue;
52
+ const mediaPath = resolveRelativePath(relsPath, relationship.target);
53
+ if (!isUnsafePackagePath(mediaPath) && mediaPath.startsWith("word/media/")) retiredMedia.add(mediaPath);
54
+ }
55
+ }
56
+ const compressionOptions = { level: compressionLevel };
57
+ zip.file(DOCUMENT_RELS_PATH, withoutChildren(relsXml, (child) => getNamespaceUri(child) === OOXML_NS.pr && getLocalName(child.name) === "Relationship" && removedIds.has(getAttribute(child, null, "Id") ?? "")), {
58
+ compression: "DEFLATE",
59
+ compressionOptions
60
+ });
61
+ if (document.package.relationships) {
62
+ document.package.relationships = new Map(document.package.relationships);
63
+ for (const id of removedIds) document.package.relationships.delete(id);
64
+ }
65
+ for (const path of candidates) {
66
+ zip.remove(path);
67
+ const slash = path.lastIndexOf("/");
68
+ zip.remove(`${path.slice(0, slash + 1)}_rels/${path.slice(slash + 1)}.rels`);
69
+ }
70
+ for (const file of Object.values(zip.files)) {
71
+ if (file.dir || !file.name.endsWith(".rels")) continue;
72
+ for (const relationship of parseRelationships(await file.async("text")).values()) if (relationship.targetMode !== "External") retiredMedia.delete(resolveRelativePath(file.name, relationship.target));
73
+ }
74
+ for (const path of retiredMedia) {
75
+ zip.remove(path);
76
+ candidates.add(path);
77
+ }
78
+ const contentTypes = zip.file("[Content_Types].xml");
79
+ if (!contentTypes) return panic("The package has no content types");
80
+ const contentTypesXml = await contentTypes.async("text");
81
+ zip.file("[Content_Types].xml", withoutChildren(contentTypesXml, (child) => getNamespaceUri(child) === OOXML_NS.ct && getLocalName(child.name) === "Override" && candidates.has((getAttribute(child, null, "PartName") ?? "").replace(/^\//u, ""))), {
82
+ compression: "DEFLATE",
83
+ compressionOptions
84
+ });
85
+ };
86
+ //#endregion
87
+ export { removeResolvedHeaderFooterParts };
@@ -15,6 +15,8 @@ declare function findMaxRId(relsXml: string): number;
15
15
  * These are newly created hyperlinks that need relationship entries.
16
16
  */
17
17
  declare function collectHyperlinksWithoutRId(blocks: BlockContent[]): Hyperlink[];
18
+ /** The selective save boundary must use the same resource census as full repack. */
19
+ declare const hasUnmaterializedInlineResources: (blocks: BlockContent[]) => boolean;
18
20
  /**
19
21
  * Options for repacking DOCX
20
22
  */
@@ -197,4 +199,4 @@ declare function createEmptyDocx(): Promise<ArrayBuffer>;
197
199
  */
198
200
  declare function createDocx(doc: document_d_exports.Document): Promise<ArrayBuffer>;
199
201
  //#endregion
200
- export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
202
+ export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };