@stll/folio-core 0.58.5 → 0.60.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 (208) hide show
  1. package/README.md +10 -1
  2. package/dist/ai-edits/apply.d.ts +11 -12
  3. package/dist/ai-edits/apply.js +179 -168
  4. package/dist/ai-edits/headless.d.ts +15 -13
  5. package/dist/ai-edits/headless.js +40 -13
  6. package/dist/ai-edits/index.d.ts +3 -3
  7. package/dist/ai-edits/newListNumbering.d.ts +3 -9
  8. package/dist/ai-edits/newListNumbering.js +36 -41
  9. package/dist/ai-edits/referenceClassification.d.ts +22 -0
  10. package/dist/ai-edits/referenceClassification.js +21 -0
  11. package/dist/ai-edits/snapshot.js +39 -40
  12. package/dist/ai-edits/types.d.ts +24 -32
  13. package/dist/compare/column-alignment.d.ts +4 -4
  14. package/dist/compare/compare.d.ts +3 -7
  15. package/dist/compare/compare.js +25 -27
  16. package/dist/compare/content-alignment.d.ts +9 -9
  17. package/dist/compare/content-types.d.ts +23 -10
  18. package/dist/compare/content-types.js +14 -0
  19. package/dist/compare/content.d.ts +16 -17
  20. package/dist/compare/content.js +30 -7
  21. package/dist/compare/formatting.d.ts +3 -3
  22. package/dist/compare/import-style-closure.d.ts +22 -0
  23. package/dist/compare/import-style-closure.js +33 -0
  24. package/dist/compare/inserted-resource-snapshot.d.ts +15 -0
  25. package/dist/compare/inserted-resource-snapshot.js +63 -0
  26. package/dist/compare/plan.js +20 -13
  27. package/dist/compare/scenario.js +12 -2
  28. package/dist/compare/style-resources.d.ts +8 -2
  29. package/dist/compare/style-resources.js +63 -14
  30. package/dist/compare/verification.d.ts +15 -3
  31. package/dist/compare/verification.js +47 -8
  32. package/dist/compat/eigenpal.d.ts +5 -5
  33. package/dist/compat/eigenpal.js +2 -2
  34. package/dist/controller/canonicalListRendering.js +13 -22
  35. package/dist/controller/canonicalPublicOperations.js +1 -1
  36. package/dist/controller/canonicalSession.js +3 -2
  37. package/dist/controller/canonicalStructure.js +55 -28
  38. package/dist/controller/fontReadiness.js +1 -1
  39. package/dist/controller/layoutPipeline.js +7 -1
  40. package/dist/controller/layoutSession.d.ts +2 -0
  41. package/dist/display-list/build/fontTable.js +7 -5
  42. package/dist/display-list/build/furniture.d.ts +1 -0
  43. package/dist/display-list/build/furniture.js +1 -0
  44. package/dist/display-list/build/watermarkPrimitives.d.ts +2 -1
  45. package/dist/display-list/build/watermarkPrimitives.js +9 -3
  46. package/dist/display-list/editor/pageRenderer.js +1 -0
  47. package/dist/document-operations.d.ts +4 -9
  48. package/dist/document-operations.js +45 -41
  49. package/dist/docx/attributeRemainder.d.ts +1 -1
  50. package/dist/docx/bookmarkIdNormalization.d.ts +10 -0
  51. package/dist/docx/bookmarkIdNormalization.js +100 -0
  52. package/dist/docx/bookmarkParser.js +4 -2
  53. package/dist/docx/importedIdCollisions.d.ts +136 -0
  54. package/dist/docx/importedIdCollisions.js +215 -0
  55. package/dist/docx/index.d.ts +1 -1
  56. package/dist/docx/listMarkerComputation.js +9 -14
  57. package/dist/docx/listTemplate.d.ts +22 -0
  58. package/dist/docx/listTemplate.js +40 -0
  59. package/dist/docx/numberingParser.js +4 -4
  60. package/dist/docx/numberingReferenceNormalization.js +3 -3
  61. package/dist/docx/paragraphParser.js +3 -4
  62. package/dist/docx/paragraphPropertySource.js +1 -2
  63. package/dist/docx/parseContext.d.ts +2 -1
  64. package/dist/docx/parseContext.js +1 -0
  65. package/dist/docx/parseWarningMessage.js +1 -0
  66. package/dist/docx/parser.js +5 -1
  67. package/dist/docx/relsParser.js +1 -1
  68. package/dist/docx/rezip.js +4 -3
  69. package/dist/docx/selectedXmlIdentityScan.d.ts +10 -0
  70. package/dist/docx/selectedXmlIdentityScan.js +36 -0
  71. package/dist/docx/selectiveSave.js +5 -4
  72. package/dist/docx/selectiveXmlPatch.js +44 -52
  73. package/dist/docx/serializer/commentSerializer.d.ts +3 -1
  74. package/dist/docx/serializer/commentSerializer.js +11 -2
  75. package/dist/docx/serializer/runSerializer.js +2 -2
  76. package/dist/docx/serializer/trackedChangeAttributes.d.ts +1 -3
  77. package/dist/docx/serializer/trackedChangeAttributes.js +1 -11
  78. package/dist/docx/server/materializeYjsDocx.js +2 -2
  79. package/dist/docx/server/migrateYjsAttrSchema.js +5 -5
  80. package/dist/fonts/embeddedFontFingerprint.d.ts +5 -0
  81. package/dist/fonts/embeddedFontFingerprint.js +18 -0
  82. package/dist/fonts/embeddedFonts.d.ts +1 -1
  83. package/dist/fonts/embeddedFonts.js +1 -1
  84. package/dist/fonts/headlessMeasure.js +0 -0
  85. package/dist/generated/text_shaper.js +8 -4
  86. package/dist/generated/text_shaper_bg.wasm +0 -0
  87. package/dist/headless-layout.js +1 -0
  88. package/dist/index.d.ts +5 -5
  89. package/dist/index.js +2 -2
  90. package/dist/internal/paragraphFormattingSerialization.d.ts +1 -8
  91. package/dist/internal/paragraphFormattingSerialization.js +6 -20
  92. package/dist/layout-bridge/convert/flowConversionShared.d.ts +3 -1
  93. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -1
  94. package/dist/layout-bridge/convert/footnoteLayout.js +3 -0
  95. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  96. package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
  97. package/dist/layout-bridge/convert/listMarkers.d.ts +1 -2
  98. package/dist/layout-bridge/convert/listMarkers.js +3 -11
  99. package/dist/layout-bridge/convert/paragraphAttrs.d.ts +1 -1
  100. package/dist/layout-bridge/convert/paragraphAttrs.js +8 -4
  101. package/dist/layout-bridge/convert/paragraphConversion.js +8 -1
  102. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -1
  103. package/dist/layout-bridge/convert/toFlowBlocks.js +27 -2
  104. package/dist/layout-engine/measure/cache.js +39 -7
  105. package/dist/layout-engine/measure/lineTypography.js +1 -0
  106. package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
  107. package/dist/layout-engine/measure/listMarkerWidth.js +1 -0
  108. package/dist/layout-engine/measure/measureContainer.js +3 -4
  109. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -9
  110. package/dist/layout-engine/measure/measureHelpers.js +21 -18
  111. package/dist/layout-engine/measure/measureParagraph.js +2 -0
  112. package/dist/layout-engine/measure/measureTypes.d.ts +2 -0
  113. package/dist/layout-engine/types.d.ts +4 -1
  114. package/dist/layout-painter/renderPage.d.ts +1 -0
  115. package/dist/layout-painter/renderPage.js +11 -3
  116. package/dist/layout-painter/renderParagraph.js +13 -3
  117. package/dist/layout-painter/renderWatermark.d.ts +1 -0
  118. package/dist/layout-painter/renderWatermark.js +7 -3
  119. package/dist/managers/editorShortcuts.d.ts +2 -1
  120. package/dist/managers/editorShortcuts.js +2 -1
  121. package/dist/markdown/renderParagraph.js +29 -3
  122. package/dist/prosemirror/canonicalSelectionRange.d.ts +4 -1
  123. package/dist/prosemirror/canonicalSelectionRange.js +9 -3
  124. package/dist/prosemirror/commands/comments.js +5 -32
  125. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +7 -1
  126. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +35 -1
  127. package/dist/prosemirror/commands/propertyChangeScope.js +6 -0
  128. package/dist/prosemirror/commands/resolveParagraphProperties.js +10 -5
  129. package/dist/prosemirror/conversion/fromProseDoc.js +23 -9
  130. package/dist/prosemirror/conversion/toProseDoc.js +31 -7
  131. package/dist/prosemirror/extensions/core/ParagraphExtension.js +29 -14
  132. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +8 -2
  133. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +39 -3
  134. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts +8 -2
  135. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +4 -3
  136. package/dist/prosemirror/extensions/features/ImageDragExtension.js +4 -3
  137. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +4 -3
  138. package/dist/prosemirror/extensions/features/ListExtension.js +51 -21
  139. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +9 -3
  140. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +6 -5
  141. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +8 -2
  142. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +11 -3
  143. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +4 -3
  144. package/dist/prosemirror/extensions/marks/markUtils.js +1 -1
  145. package/dist/prosemirror/listInstanceReferences.js +21 -15
  146. package/dist/prosemirror/listLabels.d.ts +4 -3
  147. package/dist/prosemirror/listLabels.js +18 -51
  148. package/dist/prosemirror/listMarker.d.ts +2 -19
  149. package/dist/prosemirror/listMarker.js +28 -57
  150. package/dist/prosemirror/listNumbering.js +16 -6
  151. package/dist/prosemirror/listRendering.d.ts +4 -3
  152. package/dist/prosemirror/listRendering.js +22 -3
  153. package/dist/prosemirror/listRenderingAttrs.d.ts +2 -1
  154. package/dist/prosemirror/listRenderingAttrs.js +6 -11
  155. package/dist/prosemirror/numberedRefFields.js +9 -5
  156. package/dist/prosemirror/numberingAttr.d.ts +19 -1
  157. package/dist/prosemirror/numberingAttr.js +16 -2
  158. package/dist/prosemirror/paragraphIndentation.js +5 -2
  159. package/dist/prosemirror/paragraphMarkJoin.js +17 -3
  160. package/dist/prosemirror/paragraphPropertyCarry.js +5 -6
  161. package/dist/prosemirror/pluginKeyOwner.d.ts +13 -0
  162. package/dist/prosemirror/pluginKeyOwner.js +35 -0
  163. package/dist/prosemirror/plugins/aiCitationDecorations.d.ts +11 -4
  164. package/dist/prosemirror/plugins/aiCitationDecorations.js +16 -3
  165. package/dist/prosemirror/plugins/aiSuggestionDecorations.d.ts +11 -4
  166. package/dist/prosemirror/plugins/aiSuggestionDecorations.js +18 -3
  167. package/dist/prosemirror/plugins/anonymizationDecorations.d.ts +11 -11
  168. package/dist/prosemirror/plugins/anonymizationDecorations.js +23 -13
  169. package/dist/prosemirror/plugins/autocompleteSuggestion.d.ts +8 -2
  170. package/dist/prosemirror/plugins/autocompleteSuggestion.js +27 -5
  171. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +8 -2
  172. package/dist/prosemirror/plugins/contentControlWidgets.js +4 -3
  173. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +6 -5
  174. package/dist/prosemirror/plugins/createDocScanPlugin.js +1 -1
  175. package/dist/prosemirror/plugins/documentNumbering.js +5 -3
  176. package/dist/prosemirror/plugins/documentStyleState.d.ts +8 -2
  177. package/dist/prosemirror/plugins/documentStyleState.js +2 -2
  178. package/dist/prosemirror/plugins/documentStyles.js +1 -1
  179. package/dist/prosemirror/plugins/selectionTracker.d.ts +8 -2
  180. package/dist/prosemirror/plugins/selectionTracker.js +4 -3
  181. package/dist/prosemirror/plugins/suggestionMode.d.ts +8 -2
  182. package/dist/prosemirror/plugins/suggestionMode.js +4 -3
  183. package/dist/prosemirror/plugins/templateDirectives.d.ts +8 -2
  184. package/dist/prosemirror/plugins/templateDirectives.js +19 -4
  185. package/dist/prosemirror/plugins/templatePreviewValues.d.ts +8 -2
  186. package/dist/prosemirror/plugins/templatePreviewValues.js +27 -5
  187. package/dist/prosemirror/plugins/templateSlashMenu.d.ts +8 -2
  188. package/dist/prosemirror/plugins/templateSlashMenu.js +23 -5
  189. package/dist/prosemirror/plugins/transactionInvariants.js +29 -5
  190. package/dist/prosemirror/schema/nodes.d.ts +2 -1
  191. package/dist/prosemirror/selectionState.js +5 -1
  192. package/dist/prosemirror/storyListNumbering.js +11 -12
  193. package/dist/prosemirror/styles/paragraphStyleCascade.js +2 -6
  194. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +11 -4
  195. package/dist/prosemirror/styles/resolvedStyleAttrs.js +28 -18
  196. package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
  197. package/dist/prosemirror/yjsDocumentMetadata.js +42 -10
  198. package/dist/redline.d.ts +17 -1
  199. package/dist/redline.js +118 -57
  200. package/dist/server.d.ts +3 -3
  201. package/dist/server.js +2 -2
  202. package/dist/utils/fontLoader.d.ts +2 -2
  203. package/dist/utils/fontLoader.js +2 -7
  204. package/dist/utils/fontResolver.d.ts +7 -3
  205. package/dist/utils/fontResolver.js +6 -8
  206. package/dist/version-comparison.d.ts +1 -2
  207. package/dist/version-comparison.js +1 -2
  208. package/package.json +26 -9
@@ -1,6 +1,6 @@
1
1
  import { getCachedNumberingMap } from "./numberingParser.js";
2
- import { NO_NUMBERING_NUM_ID, NO_PARAGRAPH_NUMBERING, ParagraphNumberingOverride, ParagraphNumberingSlots, ResolvedParagraphNumbering, isNumberingReference, mergeParagraphNumbering, paragraphNumberingFromSlots, paragraphNumberingLevel, paragraphNumberingReferenceId, paragraphNumberingSlots, readParagraphNumbering, resolveParagraphNumbering, sameEffectiveParagraphNumbering, sameStatedParagraphNumbering } from "./numberingReference.js";
3
2
  import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, DocxEncryptionErrorCode, isDocxEncryptionError } from "./encryption/errors.js";
4
3
  import { DecryptDocxOptions, DecryptDocxResult, decryptDocxIfNeeded, openDocxBuffer } from "./encryption/openEncryptedDocx.js";
4
+ import { NO_NUMBERING_NUM_ID, NO_PARAGRAPH_NUMBERING, ParagraphNumberingOverride, ParagraphNumberingSlots, ResolvedParagraphNumbering, isNumberingReference, mergeParagraphNumbering, paragraphNumberingFromSlots, paragraphNumberingLevel, paragraphNumberingReferenceId, paragraphNumberingSlots, readParagraphNumbering, resolveParagraphNumbering, sameEffectiveParagraphNumbering, sameStatedParagraphNumbering } from "./numberingReference.js";
5
5
  import { FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FolioDocumentMetadataProperty, FolioDocumentPrivacyArchiveError, FolioDocumentPrivacyOptions, FolioDocumentPrivacyReport, FolioDocumentPrivacyTransform, InvalidFolioDocumentPrivacyOptionsError, RewriteDocxMetadataPrivacyResult, isFolioDocumentPrivacyTransform, rewriteDocxMetadataPrivacy } from "./metadataPrivacy.js";
6
6
  export { DOCX_ENCRYPTION_ERROR_CODES, type DecryptDocxOptions, type DecryptDocxResult, DocxEncryptionError, type DocxEncryptionErrorCode, FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, type FolioDocumentMetadataProperty, FolioDocumentPrivacyArchiveError, type FolioDocumentPrivacyOptions, type FolioDocumentPrivacyReport, type FolioDocumentPrivacyTransform, InvalidFolioDocumentPrivacyOptionsError, NO_NUMBERING_NUM_ID, NO_PARAGRAPH_NUMBERING, type ParagraphNumberingOverride, type ParagraphNumberingSlots, type ResolvedParagraphNumbering, type RewriteDocxMetadataPrivacyResult, decryptDocxIfNeeded, getCachedNumberingMap, isDocxEncryptionError, isFolioDocumentPrivacyTransform, isNumberingReference, mergeParagraphNumbering, openDocxBuffer, paragraphNumberingFromSlots, paragraphNumberingLevel, paragraphNumberingReferenceId, paragraphNumberingSlots, readParagraphNumbering, resolveParagraphNumbering, rewriteDocxMetadataPrivacy, sameEffectiveParagraphNumbering, sameStatedParagraphNumbering };
@@ -1,6 +1,6 @@
1
1
  import { bulletMarkerFontName, convertBulletToUnicode } from "./bulletMarkers.js";
2
+ import { resolveListTemplate } from "./listTemplate.js";
2
3
  import { isNumberingReference } from "./numberingReference.js";
3
- import { formatOoxmlCounter } from "./ooxmlCounterFormatter.js";
4
4
  //#region src/docx/listMarkerComputation.ts
5
5
  const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
6
6
  const listRendering = paragraph.listRendering;
@@ -22,7 +22,7 @@ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounter
22
22
  let counters = listCounters.get(numId);
23
23
  if (!counters) return;
24
24
  const abstractNumId = numbering.getAbstractNumId(numId);
25
- const styleNumbering = paragraph.formatting?.numPrFromStyle;
25
+ const styleNumbering = paragraph.formatting?.numPr?.kind !== "reference" && paragraph.formatting?.numPrFromStyle?.kind === "reference";
26
26
  const resumesRestartedInstance = firstEncounter && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.numId !== null && previousList.numId !== numId && restartedNumIds.has(previousList.numId);
27
27
  const resumesStyleInstance = firstEncounter && !styleNumbering && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.fromStyle === true;
28
28
  if (abstractNumId !== null && (styleNumbering || resumesRestartedInstance || resumesStyleInstance)) {
@@ -60,18 +60,13 @@ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounter
60
60
  previousList.numId = null;
61
61
  return;
62
62
  }
63
- let computedMarker = pattern;
64
- const useLegalNumbering = numbering.getLevel(numId, level)?.isLgl === true || listRendering.isLegal === true;
65
- for (let lvl = 0; lvl <= level; lvl += 1) {
66
- const placeholder = `%${lvl + 1}`;
67
- if (computedMarker.includes(placeholder)) {
68
- const value = counters[lvl] ?? 0;
69
- const levelInfo = numbering.getLevel(numId, lvl);
70
- const formatted = formatOoxmlCounter(value, useLegalNumbering ? "decimal" : levelInfo?.numFmt || "decimal");
71
- computedMarker = computedMarker.replaceAll(placeholder, formatted);
72
- }
73
- }
74
- listRendering.marker = computedMarker;
63
+ const currentLevelInfo = numbering.getLevel(numId, level);
64
+ listRendering.marker = resolveListTemplate({
65
+ template: pattern,
66
+ counters,
67
+ levelFormats: listRendering.levelNumFmts ?? counters.map((_counter, ilvl) => numbering.getLevel(numId, ilvl)?.numFmt),
68
+ forceDecimal: currentLevelInfo?.isLgl === true || listRendering.isLegal === true
69
+ });
75
70
  };
76
71
  //#endregion
77
72
  export { computeListMarker };
@@ -0,0 +1,22 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/listTemplate.d.ts
3
+ type ResolvedListComponent = {
4
+ level: number;
5
+ relativeStart: number;
6
+ start: number;
7
+ end: number;
8
+ };
9
+ type ResolvedListTemplate = {
10
+ value: string;
11
+ components: readonly ResolvedListComponent[];
12
+ };
13
+ type ResolveListTemplateOptions = {
14
+ template: string;
15
+ counters: readonly number[];
16
+ levelFormats?: readonly (document_d_exports.CounterFormat | undefined)[] | undefined;
17
+ forceDecimal?: boolean | undefined;
18
+ };
19
+ declare function resolveListTemplate(options: ResolveListTemplateOptions): string;
20
+ declare function resolveListTemplateWithComponents({ template, counters, levelFormats, forceDecimal }: ResolveListTemplateOptions): ResolvedListTemplate;
21
+ //#endregion
22
+ export { ResolvedListComponent, ResolvedListTemplate, resolveListTemplate, resolveListTemplateWithComponents };
@@ -0,0 +1,40 @@
1
+ import { formatOoxmlCounter } from "./ooxmlCounterFormatter.js";
2
+ //#region src/docx/listTemplate.ts
3
+ function resolveListTemplate(options) {
4
+ return resolveListTemplateWithComponents(options).value;
5
+ }
6
+ function resolveListTemplateWithComponents({ template, counters, levelFormats, forceDecimal = false }) {
7
+ const components = [];
8
+ let value = "";
9
+ let sourceEnd = 0;
10
+ for (const match of template.matchAll(/%(?<digit>\d)(?<punct>[.):\]])?/gu)) {
11
+ const sourceStart = match.index;
12
+ const relativeStart = value.length;
13
+ value += template.slice(sourceEnd, sourceStart);
14
+ sourceEnd = sourceStart + match[0].length;
15
+ const index = Number.parseInt(match.groups?.["digit"] ?? "", 10) - 1;
16
+ const counter = counters[index];
17
+ if (index < 0 || counter === void 0 || !Number.isFinite(counter)) {
18
+ value += match.groups?.["punct"] ?? "";
19
+ continue;
20
+ }
21
+ const formatted = formatOoxmlCounter(counter, forceDecimal ? "decimal" : levelFormats?.[index]);
22
+ if (!formatted) continue;
23
+ const start = value.length;
24
+ value += formatted;
25
+ components.push({
26
+ level: index,
27
+ relativeStart,
28
+ start,
29
+ end: value.length
30
+ });
31
+ value += match.groups?.["punct"] ?? "";
32
+ }
33
+ value += template.slice(sourceEnd);
34
+ return {
35
+ value,
36
+ components
37
+ };
38
+ }
39
+ //#endregion
40
+ export { resolveListTemplate, resolveListTemplateWithComponents };
@@ -1,6 +1,6 @@
1
1
  import { DERIVED_PART_ROOT_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
2
2
  import { CAPTURE, dispatchChildrenWithContext, sequencePositions } from "./containerChildren.js";
3
- import { isNumberingReference } from "./numberingReference.js";
3
+ import { isNumberingReference as isNumberingReference$1 } from "./numberingReference.js";
4
4
  import { formatOoxmlCounter, padDecimal } from "./ooxmlCounterFormatter.js";
5
5
  import { parseParagraphProperties } from "./paragraphProperties.js";
6
6
  import { LevelSuffixSchema, NumberFormatSchema, ParagraphAlignmentSchema, narrowEnum } from "./parserEnums.js";
@@ -398,9 +398,9 @@ function getCachedNumberingMap(definitions) {
398
398
  */
399
399
  function createNumberingMap(definitions) {
400
400
  const abstractMap = /* @__PURE__ */ new Map();
401
- for (const abs of definitions.abstractNums) abstractMap.set(abs.abstractNumId, abs);
401
+ for (const abs of definitions.abstractNums) if (!abstractMap.has(abs.abstractNumId)) abstractMap.set(abs.abstractNumId, abs);
402
402
  const numMap = /* @__PURE__ */ new Map();
403
- for (const num of definitions.nums) numMap.set(num.numId, num);
403
+ for (const num of definitions.nums) if (!numMap.has(num.numId)) numMap.set(num.numId, num);
404
404
  return {
405
405
  definitions,
406
406
  getLevel(numId, ilvl) {
@@ -459,7 +459,7 @@ function createNumberingMap(definitions) {
459
459
  */
460
460
  function computeListRendering(numPr, numbering) {
461
461
  const { numId, ilvl = 0 } = numPr;
462
- if (!isNumberingReference(numId)) return null;
462
+ if (!isNumberingReference$1(numId)) return null;
463
463
  const level = numbering.getLevel(numId, ilvl);
464
464
  if (!level) return null;
465
465
  const levelNumFmts = [];
@@ -1,4 +1,4 @@
1
- import { NO_PARAGRAPH_NUMBERING as NO_PARAGRAPH_NUMBERING$1, paragraphNumberingReferenceId as paragraphNumberingReferenceId$1 } from "./numberingReference.js";
1
+ import { NO_PARAGRAPH_NUMBERING as NO_PARAGRAPH_NUMBERING$1, paragraphNumberingReferenceId } from "./numberingReference.js";
2
2
  import { visitDocxParagraphs } from "./paragraphTraversal.js";
3
3
  import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
4
4
  //#region src/docx/numberingReferenceNormalization.ts
@@ -40,7 +40,7 @@ const normalizeNumberingReferences = ({ documentBody, numbering, headers, footer
40
40
  endnotes
41
41
  }, (paragraph) => {
42
42
  const formatting = paragraph.formatting;
43
- const numId = paragraphNumberingReferenceId$1(formatting?.numPr);
43
+ const numId = paragraphNumberingReferenceId(formatting?.numPr);
44
44
  if (!formatting || numId === void 0 || resolvesNumbering(numId, numbering)) return;
45
45
  formatting.numPr = NO_PARAGRAPH_NUMBERING$1;
46
46
  delete formatting.numPrFromStyle;
@@ -53,7 +53,7 @@ const normalizeStyleNumberingReferences = ({ styles, numbering }) => {
53
53
  const unnumberedStyleIds = [];
54
54
  for (const style of styles) {
55
55
  const pPr = style.pPr;
56
- const numId = paragraphNumberingReferenceId$1(pPr?.numPr);
56
+ const numId = paragraphNumberingReferenceId(pPr?.numPr);
57
57
  if (!pPr || numId === void 0 || resolvesNumbering(numId, numbering)) continue;
58
58
  pPr.numPr = NO_PARAGRAPH_NUMBERING$1;
59
59
  unnumberedStyleIds.push(style.styleId);
@@ -12,7 +12,7 @@ import { inlineWrapperOf } from "./inlineWrapperParser.js";
12
12
  import { preserveLinkedSdt } from "./linkedSdtPreservation.js";
13
13
  import { parseMarkupRangeMarker, parseMoveBookmarkMarker } from "./markupRangeMarker.js";
14
14
  import { counterFormatOf, markerAlignmentForLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot } from "./numberingParser.js";
15
- import { mergeParagraphNumbering as mergeParagraphNumbering$1, paragraphNumberingReferenceId as paragraphNumberingReferenceId$1, resolveParagraphNumbering as resolveParagraphNumbering$1 } from "./numberingReference.js";
15
+ import { mergeParagraphNumbering as mergeParagraphNumbering$1, paragraphNumberingReferenceId, resolveParagraphNumbering as resolveParagraphNumbering$1 } from "./numberingReference.js";
16
16
  import { paraIdAttribute, textIdAttribute } from "./paraIdAttribute.js";
17
17
  import { paraIdInRange } from "./paraIdRangeNormalization.js";
18
18
  import { EMPTY_MARK_PROPERTIES_ATTRIBUTE, restoreEmptyMarkProperties } from "./paragraphMarkPropertyPresence.js";
@@ -1253,7 +1253,6 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
1253
1253
  let effectiveNumPr = directNumPr;
1254
1254
  if (paragraphFormatting && numPrFromStyle) {
1255
1255
  effectiveNumPr = mergeParagraphNumbering$1(styleNumPr, directNumPr);
1256
- if (effectiveNumPr !== void 0) paragraphFormatting.numPr = effectiveNumPr;
1257
1256
  paragraphFormatting.numPrFromStyle = styleNumPr;
1258
1257
  }
1259
1258
  const resolvedNumbering = resolveParagraphNumbering$1(effectiveNumPr);
@@ -1444,7 +1443,7 @@ function isEmptyParagraph(paragraph) {
1444
1443
  * @returns true if paragraph has numbering properties
1445
1444
  */
1446
1445
  function isListItem(paragraph) {
1447
- return paragraphNumberingReferenceId$1(paragraph.formatting?.numPr) !== void 0;
1446
+ return paragraphNumberingReferenceId(mergeParagraphNumbering$1(paragraph.formatting?.numPrFromStyle, paragraph.formatting?.numPr)) !== void 0;
1448
1447
  }
1449
1448
  /**
1450
1449
  * Get the list level of a paragraph (0-8)
@@ -1453,7 +1452,7 @@ function isListItem(paragraph) {
1453
1452
  * @returns List level or undefined if not a list item
1454
1453
  */
1455
1454
  function getListLevel(paragraph) {
1456
- const resolved = resolveParagraphNumbering$1(paragraph.formatting?.numPr);
1455
+ const resolved = resolveParagraphNumbering$1(mergeParagraphNumbering$1(paragraph.formatting?.numPrFromStyle, paragraph.formatting?.numPr));
1457
1456
  return resolved.kind === "reference" ? resolved.ilvl : void 0;
1458
1457
  }
1459
1458
  /**
@@ -5,7 +5,6 @@ import { parseParagraphProperties } from "./paragraphProperties.js";
5
5
  import { visitDocxParagraphs } from "./paragraphTraversal.js";
6
6
  import { parseXmlDocument } from "./xmlParser.js";
7
7
  import { TaggedError, panic } from "better-result";
8
- import { PluginKey } from "prosemirror-state";
9
8
  //#region src/docx/paragraphPropertySource.ts
10
9
  const paragraphPropertySourceEmissionFingerprint = Symbol("paragraphPropertySourceEmissionFingerprint");
11
10
  const paragraphPropertySourceIndentationBaseline = Symbol("paragraphPropertySourceIndentationBaseline");
@@ -793,7 +792,7 @@ const createProseParagraphWithPropertySource = (nodeType, sourceParagraph, optio
793
792
  linkProseParagraphPropertySource(paragraph, sourceParagraph);
794
793
  return paragraph;
795
794
  };
796
- const paragraphPropertySourceTransfersKey = new PluginKey("paragraphPropertySourceTransfers");
795
+ const paragraphPropertySourceTransfersKey = "stll.folio.meta.paragraphPropertySourceTransfers";
797
796
  /** Join adjacent paragraphs when revision semantics deliberately select the right owner. */
798
797
  const joinProseParagraphsWithRightPropertySource = ({ attrs, pos, transaction }) => {
799
798
  const $pos = transaction.doc.resolve(pos);
@@ -1,8 +1,9 @@
1
- import { ParseWarning, ParseWarningCode } from "@stll/docx-core/model";
1
+ import { ImportedIdCollisionKind, ParseWarning, ParseWarningCode } from "@stll/docx-core/model";
2
2
  //#region src/docx/parseContext.d.ts
3
3
  /** What a call site states; the collector supplies the rest. */
4
4
  type ParseWarningReport = {
5
5
  code: ParseWarningCode;
6
+ identityKind?: ImportedIdCollisionKind;
6
7
  /** The element as written, prefix included. */
7
8
  element?: string;
8
9
  /** The best position this part can name, e.g. `style "Heading1"`. */
@@ -30,6 +30,7 @@ const createParseWarningCollector = (part = PACKAGE_PART) => {
30
30
  retainedByCode.set(report.code, kept + 1);
31
31
  retained.push({
32
32
  code: report.code,
33
+ ...report.identityKind === void 0 ? {} : { identityKind: report.identityKind },
33
34
  location: {
34
35
  ...location,
35
36
  ...report.element === void 0 ? {} : { element: report.element },
@@ -22,6 +22,7 @@ const PARSE_WARNING_MESSAGES = {
22
22
  [PARSE_WARNING_CODES.documentPartMissing]: () => "No document.xml found in DOCX",
23
23
  [PARSE_WARNING_CODES.documentModelIssue]: (warning) => warning.detail ?? "The document model reported an issue.",
24
24
  [PARSE_WARNING_CODES.duplicateCommentId]: (warning) => `Dropped ${plural(warning.count, "comment")} repeating a w:id another comment already defines.`,
25
+ [PARSE_WARNING_CODES.duplicateImportedId]: (warning) => `Repeated imported identity${quoted(warning.value)}${where(warning)}: ${warning.detail ?? "multiple definitions share an identity."}`,
25
26
  [PARSE_WARNING_CODES.missingCommentId]: (warning) => `Dropped ${plural(warning.count, "comment")} with no readable w:id.`,
26
27
  [PARSE_WARNING_CODES.duplicateNoteId]: (warning) => `Dropped ${plural(warning.count, "note")} repeating a w:id another note already defines.`,
27
28
  [PARSE_WARNING_CODES.danglingCommentReference]: (warning) => `Removed ${plural(warning.count, "dangling comment reference marker")} whose comments.xml entries are missing.`,
@@ -2,6 +2,7 @@ import { toArrayBuffer } from "../utils/docxInput.js";
2
2
  import { loadFontsWithMapping } from "../utils/fontLoader.js";
3
3
  import { MAX_PACKAGE_TIFF_PIXELS, convertTiffToPngDataUrl, isTiffMimeType } from "../utils/tiffConverter.js";
4
4
  import { ALT_CHUNK_PLAIN_TEXT_LIMIT_BYTES, getAltChunkRelationshipId, isAltChunkMarkup } from "./altChunk.js";
5
+ import { normalizeBookmarkIdsInXmlParts } from "./bookmarkIdNormalization.js";
5
6
  import { DUPLICATE_COMMENT_ID_WARNING, normalizeCommentIds } from "./commentIdNormalization.js";
6
7
  import { parseComments } from "./commentParser.js";
7
8
  import { DANGLING_COMMENT_REFERENCE_WARNING, UNBALANCED_COMMENT_RANGE_WARNING, normalizeCommentReferences } from "./commentReferenceNormalization.js";
@@ -16,6 +17,7 @@ import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
16
17
  import { parseFooter, parseHeader } from "./headerFooterParser.js";
17
18
  import { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, assignHeaderFooterRoles, normalizeHeaderFooterReferences } from "./headerFooterReferenceNormalization.js";
18
19
  import { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, captureDocumentSourceBaseline, captureHeaderFooterPackageBaselines, refreshHeaderFooterVerbatimFingerprint } from "./headerFooterVerbatim.js";
20
+ import { diagnoseImportedIdCollisions } from "./importedIdCollisions.js";
19
21
  import { extractMetafileRaster, isMetafileMimeType } from "./metafileRaster.js";
20
22
  import { renderEmfSvg } from "./metafileSvg.js";
21
23
  import { DocxModelValidationError, formatDocumentModelIssues, validateFolioDocumentModel } from "./modelValidation.js";
@@ -34,7 +36,7 @@ import { parseSettings } from "./settingsParser.js";
34
36
  import { parseStylesPackage } from "./styleParser.js";
35
37
  import { applyThemeFontLang, parseTheme } from "./themeParser.js";
36
38
  import { UNBALANCED_MOVE_RANGE_WARNING, normalizeTrackedMoveRanges } from "./trackedMoveRangeNormalization.js";
37
- import { getEntryUncompressedSize, getMediaMimeType, mediaToDataUrl, unzipDocx } from "./unzip.js";
39
+ import { getEntryUncompressedSize, getMediaMimeType, mediaToDataUrl, replaceRawDocxXmlParts, unzipDocx } from "./unzip.js";
38
40
  import { getAttribute, getChildElements, getLocalName, parseXml, parseXmlDocument } from "./xmlParser.js";
39
41
  import { FOLIO_XML_RESOURCE_LIMITS } from "./xmlResourceLimits.js";
40
42
  import { TaggedError, panic } from "better-result";
@@ -192,6 +194,8 @@ async function parseDocxWithPreviewBudget(input, options, previewBudget) {
192
194
  password,
193
195
  extractAllXml: false
194
196
  }));
197
+ diagnoseImportedIdCollisions(raw.allXml, parseContext);
198
+ await replaceRawDocxXmlParts(raw, normalizeBookmarkIdsInXmlParts(raw.allXml));
195
199
  const repairedDocumentTree = await normalizeRawDocxNumericIds(raw);
196
200
  const paragraphPropertySourceDigest = sha256Hex(raw.originalBuffer);
197
201
  if (raw.wasEncrypted) parseContext.warn({ code: PARSE_WARNING_CODES.packageDecrypted });
@@ -86,7 +86,7 @@ function parseRelationships(relsXml) {
86
86
  };
87
87
  if (targetMode === "External") relationship.targetMode = "External";
88
88
  else if (targetMode === "Internal") relationship.targetMode = "Internal";
89
- map.set(id, relationship);
89
+ if (!map.has(id)) map.set(id, relationship);
90
90
  }
91
91
  return map;
92
92
  }
@@ -16,7 +16,7 @@ import { assertValidFolioDocumentModel } from "./modelValidation.js";
16
16
  import { isNewDataUrlDrawing } from "./newImage.js";
17
17
  import { missingNoteReferenceStyles, noteReferenceNeeds } from "./noteReferenceStyles.js";
18
18
  import { parseNumbering } from "./numberingParser.js";
19
- import { paragraphNumberingReferenceId as paragraphNumberingReferenceId$1 } from "./numberingReference.js";
19
+ import { paragraphNumberingReferenceId } from "./numberingReference.js";
20
20
  import { isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries } from "./packageParts.js";
21
21
  import { normalizeParaIdRangeInXmlParts } from "./paraIdRangeNormalization.js";
22
22
  import { cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, copyDocumentParagraphPropertySourceContract } from "./paragraphPropertySource.js";
@@ -25,7 +25,7 @@ import { RELATIONSHIP_TYPES, parseRelationships, resolveRelativePath } from "./r
25
25
  import { removeResolvedHeaderFooterParts } from "./removeHeaderFooterParts.js";
26
26
  import { normalizeRevisionIdsInXmlParts } from "./revisionIdNormalization.js";
27
27
  import { buildPatchedNotePartXml, collectChangedNoteParaIds, collectParaIds, patchNumberingDefinitions, spliceXml } from "./selectiveXmlPatch.js";
28
- import { hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
28
+ import { canReplayCommentsPart, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
29
29
  import { serializeDocument } from "./serializer/documentSerializer.js";
30
30
  import { serializeFontTableXml } from "./serializer/fontTableSerializer.js";
31
31
  import { serializeHeaderFooter } from "./serializer/headerFooterSerializer.js";
@@ -201,6 +201,7 @@ async function serializeCommentsToZip(doc, zip, compressionLevel) {
201
201
  const comments = doc.package.document.comments ?? [];
202
202
  const sourceCommentsFile = findZipEntryCaseInsensitive(zip, "word/comments.xml");
203
203
  const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.async("text") : void 0;
204
+ if (sourceCommentsXml !== void 0 && canReplayCommentsPart(doc, sourceCommentsXml)) return;
204
205
  if (comments.length === 0) {
205
206
  const ownsCommentsPart = hasOwnedCommentsPart(doc);
206
207
  if (sourceCommentsXml === void 0 ? !ownsCommentsPart : !hasCommentEntries(sourceCommentsXml)) return;
@@ -2462,7 +2463,7 @@ const overrideXml = (partName, contentType) => `<Override PartName="${partName}"
2462
2463
  const assertStyleNumberingReferences = (doc) => {
2463
2464
  const referenced = /* @__PURE__ */ new Set();
2464
2465
  for (const style of doc.package.styles?.styles ?? []) {
2465
- const numId = paragraphNumberingReferenceId$1(style.pPr?.numPr);
2466
+ const numId = paragraphNumberingReferenceId(style.pPr?.numPr);
2466
2467
  if (numId !== void 0) referenced.add(numId);
2467
2468
  }
2468
2469
  if (referenced.size === 0) return;
@@ -0,0 +1,10 @@
1
+ import { scanStreamingXmlElements } from "./streamingXmlParser.js";
2
+ //#region src/docx/selectedXmlIdentityScan.d.ts
3
+ /** Identity census follows the parser's first-Choice, otherwise-Fallback policy. */
4
+ declare const scanSelectedStreamingXmlElements: (xml: string, visitor: Parameters<typeof scanStreamingXmlElements>[1]) => {
5
+ status: "unsupported";
6
+ } | {
7
+ status: "scanned";
8
+ };
9
+ //#endregion
10
+ export { scanSelectedStreamingXmlElements };
@@ -0,0 +1,36 @@
1
+ import { scanStreamingXmlElements } from "./streamingXmlParser.js";
2
+ import { NAMESPACES, getLocalName, getNamespaceUri, selectAlternateContentBranch } from "./xmlParser.js";
3
+ //#region src/docx/selectedXmlIdentityScan.ts
4
+ const isCompatibilityElement = (element, name) => getNamespaceUri(element) === NAMESPACES.mc && getLocalName(element.name) === name;
5
+ /** Identity census follows the parser's first-Choice, otherwise-Fallback policy. */
6
+ const scanSelectedStreamingXmlElements = (xml, visitor) => {
7
+ if (!xml.includes("AlternateContent")) return scanStreamingXmlElements(xml, visitor);
8
+ const alternatives = /* @__PURE__ */ new Map();
9
+ const positions = /* @__PURE__ */ new WeakMap();
10
+ const scanned = scanStreamingXmlElements(xml, ({ element, parent, nameEnd }) => {
11
+ positions.set(element, nameEnd);
12
+ if (isCompatibilityElement(element, "AlternateContent")) {
13
+ element.elements = [];
14
+ alternatives.set(nameEnd, element);
15
+ }
16
+ if (!parent || !isCompatibilityElement(parent, "AlternateContent")) return;
17
+ parent.elements?.push(element);
18
+ });
19
+ if (scanned.status === "unsupported") return scanned;
20
+ const selectedPositions = /* @__PURE__ */ new Map();
21
+ for (const [position, alternative] of alternatives) {
22
+ const selected = selectAlternateContentBranch(alternative);
23
+ selectedPositions.set(position, selected === void 0 ? void 0 : positions.get(selected));
24
+ }
25
+ const active = /* @__PURE__ */ new WeakMap();
26
+ return scanStreamingXmlElements(xml, (entry) => {
27
+ const { element, parent, nameEnd } = entry;
28
+ positions.set(element, nameEnd);
29
+ let included = parent === void 0 || active.get(parent) === true;
30
+ if (parent && isCompatibilityElement(parent, "AlternateContent")) included &&= nameEnd === selectedPositions.get(positions.get(parent) ?? -1);
31
+ active.set(element, included);
32
+ if (included) visitor(entry);
33
+ });
34
+ };
35
+ //#endregion
36
+ export { scanSelectedStreamingXmlElements };
@@ -10,7 +10,7 @@ import { RELATIONSHIP_TYPES } from "./relsParser.js";
10
10
  import { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_PART_LOWER, addCommentsExtendedOverride, addCommentsExtendedRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedHyperlinkBindings, hasUnmaterializedInlineResources, planAddedStyles, publishCanonicalImageResources, updateCoreProperties, withoutAttachedTemplate } from "./rezip.js";
11
11
  import "./selectiveSaveFlags.js";
12
12
  import { buildPatchedDocumentXml, buildPatchedNoteXml, collectAddedNumberingDefs, collectParaIds, patchNumberingDefinitions } from "./selectiveXmlPatch.js";
13
- import { hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
13
+ import { canReplayCommentsPart, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
14
14
  import { serializeDocument } from "./serializer/documentSerializer.js";
15
15
  import { serializeEndnotes, serializeFootnotes } from "./serializer/noteSerializer.js";
16
16
  import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
@@ -262,8 +262,9 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
262
262
  }
263
263
  const sourceCommentsFile = findZipEntryCaseInsensitive(zip, "word/comments.xml");
264
264
  const commentsPartPath = sourceCommentsFile?.name ?? "word/comments.xml";
265
- if (hasComments || ownsCommentsPart || sourceCommentsFile) {
266
- const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.async("text") : void 0;
265
+ const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.async("text") : void 0;
266
+ const commentsUnchanged = sourceCommentsXml !== void 0 && canReplayCommentsPart(doc, sourceCommentsXml);
267
+ if (!commentsUnchanged && (hasComments || ownsCommentsPart || sourceCommentsFile)) {
267
268
  const commentsXml = serializeComments(commentPlan, sourceCommentsXml === void 0 ? void 0 : readRootNamespaceBindings(sourceCommentsXml));
268
269
  if (commentsXml !== sourceCommentsXml) updates.set(commentsPartPath, commentsXml);
269
270
  }
@@ -283,7 +284,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
283
284
  }
284
285
  }
285
286
  }
286
- if (!await patchCommentsExtended(zip, commentPlan, updates)) return null;
287
+ if (!commentsUnchanged && !await patchCommentsExtended(zip, commentPlan, updates)) return null;
287
288
  if (!await patchNumberingPart(zip, doc, updates)) return null;
288
289
  const styles = await planAddedStyles(doc, zip);
289
290
  switch (styles.type) {
@@ -1,6 +1,7 @@
1
1
  import { patchBreaksCommentRangeBalance } from "./commentRangeIntegrity.js";
2
2
  import { resolveParagraphIdentities } from "./paraIdAttribute.js";
3
3
  import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
4
+ import { scanStreamingXmlElements } from "./streamingXmlParser.js";
4
5
  import { captureVerbatimXml } from "./verbatimCapture.js";
5
6
  import { PARAGRAPH_SCAN_NAMES, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, resolveSelectiveScanPrefixes, splicesAsCanonical } from "./wordprocessingPrefixes.js";
6
7
  import { resolveNamespaceUri } from "./xmlNamespaceContext.js";
@@ -650,36 +651,32 @@ function extractElementByIdAttr(xml, openLiteral, closeTag, idAttr, id) {
650
651
  const qualifiedName = (prefix, localName) => prefix.length === 0 ? localName : `${prefix}:${localName}`;
651
652
  const collectNoteElementSyntax = (xml, elementName) => {
652
653
  const byId = /* @__PURE__ */ new Map();
653
- const root = parseXmlDocument(xml);
654
- for (const element of getChildElements(root)) {
655
- if (getLocalName(element.name) !== elementName || !WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) continue;
654
+ let root;
655
+ scanStreamingXmlElements(xml, ({ element, parent, nameEnd }) => {
656
+ if (parent === void 0) {
657
+ root = element;
658
+ return;
659
+ }
660
+ if (parent !== root || getLocalName(element.name) !== elementName || !WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) return;
656
661
  const idAttribute = findAttributeByNamespaceUri(element, WORDPROCESSINGML_NAMESPACE_URIS, "id");
657
- if (!element.name || !idAttribute) continue;
662
+ if (!element.name || !idAttribute) return;
663
+ const spelling = idAttribute.value.trim();
664
+ if (!/^[+-]?\d+$/u.test(spelling) || !Number.isSafeInteger(Number(spelling))) return;
665
+ const id = String(Number(spelling));
666
+ const offsets = scanElementRange(xml, nameEnd - element.name.length - 1, `<${element.name}`, `</${element.name}>`);
667
+ if (!offsets) return;
658
668
  const syntax = {
659
- elementName: element.name,
660
- idAttributeName: idAttribute.name,
669
+ offsets,
661
670
  elementPrefix: getNamespacePrefix(element.name) ?? "",
662
671
  attributePrefix: getNamespacePrefix(idAttribute.name) ?? ""
663
672
  };
664
- const entries = byId.get(idAttribute.value);
673
+ const entries = byId.get(id);
665
674
  if (entries) entries.push(syntax);
666
- else byId.set(idAttribute.value, [syntax]);
667
- }
675
+ else byId.set(id, [syntax]);
676
+ });
668
677
  return byId;
669
678
  };
670
- const syntaxLiteral = (syntax) => ({
671
- openLiteral: `<${syntax.elementName}`,
672
- closeTag: `</${syntax.elementName}>`,
673
- idAttr: syntax.idAttributeName
674
- });
675
- const extractNoteElement = (xml, syntax, id) => {
676
- const { openLiteral, closeTag, idAttr } = syntaxLiteral(syntax);
677
- return extractElementByIdAttr(xml, openLiteral, closeTag, idAttr, id);
678
- };
679
- const findNoteElement = (xml, syntax, id) => {
680
- const { openLiteral, closeTag, idAttr } = syntaxLiteral(syntax);
681
- return findElementByIdAttr(xml, openLiteral, closeTag, idAttr, id);
682
- };
679
+ const extractNoteElement = (xml, syntax) => xml.slice(syntax.offsets.start, syntax.offsets.end);
683
680
  const rewriteWordprocessingPrefixes = (xml, { source, target, sourceXmlnsDeclarations }) => {
684
681
  let rewritten = "";
685
682
  let quote = null;
@@ -824,7 +821,7 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
824
821
  const replacementSyntaxEntries = replacementElements.get(id);
825
822
  if (originalSyntaxEntries === void 0) {
826
823
  const replacementSyntax = currentSyntaxEntries.length === 1 && replacementSyntaxEntries?.length === 1 ? replacementSyntaxEntries[0] : void 0;
827
- const replacementNote = replacementSyntax ? extractNoteElement(replacementXml, replacementSyntax, id) : null;
824
+ const replacementNote = replacementSyntax ? extractNoteElement(replacementXml, replacementSyntax) : null;
828
825
  if (!replacementSyntax || !replacementNote) return {
829
826
  type: "refused",
830
827
  reason: "unroutable-paragraph"
@@ -836,24 +833,20 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
836
833
  });
837
834
  continue;
838
835
  }
839
- if (currentSyntaxEntries.length !== 1 || originalSyntaxEntries?.length !== 1 || replacementSyntaxEntries?.length !== 1) return {
836
+ if (currentSyntaxEntries.length !== 1 || !originalSyntaxEntries?.length || replacementSyntaxEntries?.length !== 1) return {
840
837
  type: "refused",
841
838
  reason: "unroutable-paragraph"
842
839
  };
843
840
  const currentSyntax = currentSyntaxEntries[0];
844
- const originalSyntax = originalSyntaxEntries[0];
841
+ const originalSyntax = originalSyntaxEntries.at(0);
845
842
  const replacementSyntax = replacementSyntaxEntries[0];
846
843
  if (!currentSyntax || !originalSyntax || !replacementSyntax) return {
847
844
  type: "refused",
848
845
  reason: "unroutable-paragraph"
849
846
  };
850
- const currentNote = extractNoteElement(serializedXml, currentSyntax, id);
851
- const originalOffsets = findNoteElement(originalXml, originalSyntax, id);
852
- const replacementNote = extractNoteElement(replacementXml, replacementSyntax, id);
853
- if (!currentNote || !originalOffsets || !replacementNote) return {
854
- type: "refused",
855
- reason: "unroutable-paragraph"
856
- };
847
+ const currentNote = extractNoteElement(serializedXml, currentSyntax);
848
+ const originalOffsets = originalSyntax.offsets;
849
+ const replacementNote = extractNoteElement(replacementXml, replacementSyntax);
857
850
  const originalNote = originalXml.slice(originalOffsets.start, originalOffsets.end);
858
851
  const currentParagraphs = paragraphRanges(currentNote, currentSyntax.elementPrefix);
859
852
  const originalParagraphs = paragraphRanges(originalNote, originalSyntax.elementPrefix);
@@ -861,7 +854,7 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
861
854
  const noteChangedParaIds = [...collectParaIds(currentNote).keys()].filter((paraId) => unroutedChangedParaIds.has(paraId));
862
855
  const baselineSyntaxEntries = baselineElements.get(id);
863
856
  const baselineSyntax = baselineSyntaxEntries?.length === 1 ? baselineSyntaxEntries[0] : void 0;
864
- const baselineNote = baselineSyntax ? extractNoteElement(baselineXml, baselineSyntax, id) : null;
857
+ const baselineNote = baselineSyntax ? extractNoteElement(baselineXml, baselineSyntax) : null;
865
858
  const reshaped = baselineSyntax !== void 0 && baselineNote !== null && baselineNote !== currentNote && noteShapeChanged({
866
859
  note: baselineNote,
867
860
  prefix: baselineSyntax.elementPrefix
@@ -910,26 +903,29 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
910
903
  for (const [id, baselineSyntaxEntries] of baselineElements) {
911
904
  if (currentElements.has(id) || baselineSyntaxEntries.length !== 1) continue;
912
905
  const originalSyntaxEntries = originalElements.get(id);
913
- const originalSyntax = originalSyntaxEntries?.length === 1 ? originalSyntaxEntries[0] : void 0;
914
- const originalOffsets = originalSyntax && findNoteElement(originalXml, originalSyntax, id);
915
- if (!originalOffsets) return {
906
+ if (!originalSyntaxEntries?.length) return {
916
907
  type: "refused",
917
908
  reason: "unroutable-paragraph"
918
909
  };
919
- const removal = {
920
- start: originalOffsets.start,
921
- end: originalOffsets.end,
922
- newXml: ""
923
- };
924
- paragraphSplices.push(removal);
925
- noteSplices.push(removal);
910
+ for (const { offsets } of originalSyntaxEntries) {
911
+ const removal = {
912
+ ...offsets,
913
+ newXml: ""
914
+ };
915
+ paragraphSplices.push(removal);
916
+ noteSplices.push(removal);
917
+ }
926
918
  }
927
919
  if (unroutedChangedParaIds.size > 0) return {
928
920
  type: "refused",
929
921
  reason: "unroutable-paragraph"
930
922
  };
931
923
  if (addedNotes.length > 0) {
932
- const appended = appendNotesSplice(originalXml, originalElements, addedNotes, { sourceXmlnsDeclarations: replacementXmlnsDeclarations });
924
+ const appended = appendNotesSplice({
925
+ originalElements,
926
+ addedNotes,
927
+ sourceXmlnsDeclarations: replacementXmlnsDeclarations
928
+ });
933
929
  if (!appended) return {
934
930
  type: "refused",
935
931
  reason: "unroutable-paragraph"
@@ -951,15 +947,11 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
951
947
  xml: wholeNotes
952
948
  };
953
949
  }
954
- /**
955
- * Insert new notes after the part's last note, spelled with that note's
956
- * prefixes. Null when the part holds no note to follow.
957
- */
958
- const appendNotesSplice = (originalXml, originalElements, addedNotes, { sourceXmlnsDeclarations }) => {
950
+ const appendNotesSplice = ({ originalElements, addedNotes, sourceXmlnsDeclarations }) => {
959
951
  let last = null;
960
- for (const [id, entries] of originalElements) for (const syntax of entries) {
961
- const offsets = findNoteElement(originalXml, syntax, id);
962
- if (offsets && (!last || offsets.end > last.end)) last = {
952
+ for (const entries of originalElements.values()) for (const syntax of entries) {
953
+ const offsets = syntax.offsets;
954
+ if (!last || offsets.end > last.end) last = {
963
955
  end: offsets.end,
964
956
  syntax
965
957
  };
@@ -1,6 +1,8 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
2
  import { Comment } from "../../types/content.js";
3
3
  //#region src/docx/serializer/commentSerializer.d.ts
4
+ /** An unrelated body edit must retain unmodelled duplicate comment definitions. */
5
+ declare const canReplayCommentsPart: (document: document_d_exports.Document, sourceXml: string) => boolean;
4
6
  /** An owned empty part survives deletion of its final thread. */
5
7
  declare const hasOwnedCommentsPart: (document: document_d_exports.Document) => boolean;
6
8
  /**
@@ -55,4 +57,4 @@ declare function serializeComments({ order, byId }: CommentPartPlan, sourceBindi
55
57
  */
56
58
  declare function serializeCommentsExtended(plan: CommentPartPlan): string | null;
57
59
  //#endregion
58
- export { CommentPartPlan, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended };
60
+ export { CommentPartPlan, canReplayCommentsPart, hasOwnedCommentsPart, planCommentParts, serializeComments, serializeCommentsExtended };