@stll/folio-core 0.51.0 → 0.53.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 (316) hide show
  1. package/README.md +23 -0
  2. package/dist/ai-edits/apply.d.ts +26 -1
  3. package/dist/ai-edits/apply.js +1519 -105
  4. package/dist/ai-edits/batch-claims.d.ts +130 -0
  5. package/dist/ai-edits/batch-claims.js +244 -0
  6. package/dist/ai-edits/blockRange.js +9 -3
  7. package/dist/ai-edits/character-boundaries.d.ts +43 -0
  8. package/dist/ai-edits/character-boundaries.js +95 -0
  9. package/dist/ai-edits/clean-text.d.ts +33 -3
  10. package/dist/ai-edits/clean-text.js +52 -6
  11. package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
  12. package/dist/ai-edits/comment-lifecycle.js +52 -0
  13. package/dist/ai-edits/headless.d.ts +71 -7
  14. package/dist/ai-edits/headless.js +381 -41
  15. package/dist/ai-edits/index.d.ts +2 -1
  16. package/dist/ai-edits/index.js +2 -1
  17. package/dist/ai-edits/minimal-replacement.d.ts +21 -6
  18. package/dist/ai-edits/minimal-replacement.js +134 -15
  19. package/dist/ai-edits/newListNumbering.d.ts +23 -0
  20. package/dist/ai-edits/newListNumbering.js +94 -0
  21. package/dist/ai-edits/note-references.d.ts +96 -0
  22. package/dist/ai-edits/note-references.js +139 -0
  23. package/dist/ai-edits/pending-suggestions.d.ts +79 -0
  24. package/dist/ai-edits/pending-suggestions.js +306 -0
  25. package/dist/ai-edits/read.d.ts +1 -1
  26. package/dist/ai-edits/read.js +56 -43
  27. package/dist/ai-edits/result-validation.d.ts +29 -0
  28. package/dist/ai-edits/result-validation.js +164 -0
  29. package/dist/ai-edits/snapshot.d.ts +36 -3
  30. package/dist/ai-edits/snapshot.js +177 -23
  31. package/dist/ai-edits/table-cell-mutations.d.ts +1 -1
  32. package/dist/ai-edits/table-cell-mutations.js +4 -1
  33. package/dist/ai-edits/table-geometry.d.ts +1 -1
  34. package/dist/ai-edits/table-row-column-mutations.d.ts +38 -16
  35. package/dist/ai-edits/table-row-column-mutations.js +61 -26
  36. package/dist/ai-edits/types.d.ts +155 -16
  37. package/dist/compare/compare.d.ts +1 -1
  38. package/dist/compare/compare.js +65 -16
  39. package/dist/compare/content-alignment.js +13 -2
  40. package/dist/compare/content-types.d.ts +7 -0
  41. package/dist/compare/content.d.ts +4 -1
  42. package/dist/compare/content.js +6 -1
  43. package/dist/compare/formatting.js +2 -1
  44. package/dist/compare/inline-atoms.d.ts +21 -4
  45. package/dist/compare/inline-atoms.js +138 -16
  46. package/dist/compare/inline-provenance.js +18 -22
  47. package/dist/compare/plan.js +10 -5
  48. package/dist/compare/scenario.js +14 -3
  49. package/dist/compare/section-boundary-properties.js +14 -1
  50. package/dist/compare/types.d.ts +4 -2
  51. package/dist/compare/types.js +2 -1
  52. package/dist/compare/verification.d.ts +1 -1
  53. package/dist/compare/verification.js +15 -3
  54. package/dist/controller/committedLayoutDiff.d.ts +25 -0
  55. package/dist/controller/committedLayoutDiff.js +71 -0
  56. package/dist/controller/contentControlWidgetController.d.ts +1 -1
  57. package/dist/controller/folioEditor.d.ts +1 -1
  58. package/dist/controller/fontReadiness.d.ts +70 -3
  59. package/dist/controller/fontReadiness.js +100 -10
  60. package/dist/controller/headerFooterEditorManager.js +6 -1
  61. package/dist/controller/layoutPipeline.d.ts +12 -10
  62. package/dist/controller/layoutPipeline.js +62 -9
  63. package/dist/controller/layoutRunOptions.d.ts +24 -0
  64. package/dist/controller/layoutRunOptions.js +17 -0
  65. package/dist/controller/layoutScheduler.d.ts +19 -14
  66. package/dist/controller/layoutScheduler.js +15 -12
  67. package/dist/controller/layoutSession.d.ts +26 -2
  68. package/dist/controller/layoutSession.js +1 -1
  69. package/dist/controller/noteEditorManager.js +8 -3
  70. package/dist/document-operations.d.ts +11 -2
  71. package/dist/document-operations.js +230 -21
  72. package/dist/docx/altChunk.d.ts +8 -0
  73. package/dist/docx/altChunk.js +17 -0
  74. package/dist/docx/attributeRemainder.d.ts +1 -1
  75. package/dist/docx/blockContentParser.d.ts +64 -1
  76. package/dist/docx/blockContentParser.js +17 -4
  77. package/dist/docx/blockCustomXmlShell.d.ts +7 -0
  78. package/dist/docx/blockCustomXmlShell.js +14 -0
  79. package/dist/docx/blockPlainText.js +7 -0
  80. package/dist/docx/commentAnchorIndex.js +1 -1
  81. package/dist/docx/commentRangeIntegrity.js +8 -3
  82. package/dist/docx/commentReferenceNormalization.js +1 -1
  83. package/dist/docx/commentReplyMarkers.js +20 -1
  84. package/dist/docx/compatibility.d.ts +1 -1
  85. package/dist/docx/compatibility.js +34 -13
  86. package/dist/docx/containerChildren.gen.d.ts +1 -1
  87. package/dist/docx/containerChildren.gen.js +1 -0
  88. package/dist/docx/documentParser.js +12 -12
  89. package/dist/docx/drawingIdNormalization.d.ts +3 -1
  90. package/dist/docx/drawingIdNormalization.js +6 -1
  91. package/dist/docx/ensureParaIds.js +119 -97
  92. package/dist/docx/footnoteParser.js +8 -4
  93. package/dist/docx/headerFooterParser.js +3 -3
  94. package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
  95. package/dist/docx/headerFooterReferenceNormalization.js +52 -27
  96. package/dist/docx/hyperlinkParser.d.ts +1 -1
  97. package/dist/docx/index.d.ts +1 -1
  98. package/dist/docx/listNumberingInstances.d.ts +59 -0
  99. package/dist/docx/listNumberingInstances.js +249 -0
  100. package/dist/docx/normalizeBaseDirection.js +2 -1
  101. package/dist/docx/opaqueCarrier.d.ts +7 -0
  102. package/dist/docx/opaqueCarrier.js +38 -0
  103. package/dist/docx/packageParts.js +4 -1
  104. package/dist/docx/paragraphParser.js +91 -24
  105. package/dist/docx/paragraphPropertySource.d.ts +4 -2
  106. package/dist/docx/paragraphPropertySource.js +7 -1
  107. package/dist/docx/paragraphTraversal.js +1 -0
  108. package/dist/docx/parseWarningMessage.js +4 -1
  109. package/dist/docx/parser.js +173 -20
  110. package/dist/docx/rezip.js +27 -5
  111. package/dist/docx/sdtProperties.js +1 -1
  112. package/dist/docx/sdtPropertiesPatch.js +3 -0
  113. package/dist/docx/selectiveSave.js +16 -9
  114. package/dist/docx/selectiveXmlPatch.js +106 -37
  115. package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
  116. package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
  117. package/dist/docx/serializer/documentSerializer.js +2 -0
  118. package/dist/docx/serializer/headerFooterSerializer.js +2 -0
  119. package/dist/docx/serializer/noteSerializer.js +3 -1
  120. package/dist/docx/serializer/paragraphSerializer.js +33 -9
  121. package/dist/docx/serializer/tableSerializer.js +25 -38
  122. package/dist/docx/server/createBilingualDocument.js +3 -3
  123. package/dist/docx/tableParser.d.ts +157 -1
  124. package/dist/docx/tableParser.js +89 -10
  125. package/dist/docx/textBoxParser.js +1 -1
  126. package/dist/docx/unzip.d.ts +2 -1
  127. package/dist/docx/unzip.js +1 -1
  128. package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
  129. package/dist/docx/wordprocessingPrefixes.js +286 -0
  130. package/dist/generated/text_shaper.js +26 -0
  131. package/dist/generated/text_shaper_bg.wasm +0 -0
  132. package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
  133. package/dist/i18n/messages/catalogs.gen.js +153 -17
  134. package/dist/i18n/messages/messages.gen.d.ts +8 -0
  135. package/dist/internal/compare/inline-presentation.d.ts +2 -1
  136. package/dist/internal/compare/inline-presentation.js +11 -10
  137. package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
  138. package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
  139. package/dist/internal/indexedPositionMap.d.ts +11 -0
  140. package/dist/internal/indexedPositionMap.js +42 -0
  141. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  142. package/dist/internal/revisionResolutionEdits.d.ts +13 -0
  143. package/dist/internal/revisionResolutionEdits.js +19 -0
  144. package/dist/internal/revisionResolutionInline.d.ts +24 -0
  145. package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
  146. package/dist/internal/revisionResolutionStep.d.ts +34 -0
  147. package/dist/internal/revisionResolutionStep.js +282 -0
  148. package/dist/internal/revisionResolutionTracking.d.ts +11 -0
  149. package/dist/internal/revisionResolutionTracking.js +124 -0
  150. package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
  151. package/dist/internal/wholeStoryRevisionResolution.js +530 -0
  152. package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
  153. package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
  154. package/dist/layout-bridge/convert/tableConversion.js +1 -1
  155. package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
  156. package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
  157. package/dist/layout-engine/layoutInstrumentation.js +4 -1
  158. package/dist/layout-engine/measure/cache.d.ts +18 -1
  159. package/dist/layout-engine/measure/cache.js +31 -1
  160. package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
  161. package/dist/layout-engine/measure/measureContainer.js +7 -47
  162. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
  163. package/dist/layout-engine/measure/measureHelpers.js +22 -2
  164. package/dist/layout-engine/measure/measureWorker.js +2 -3
  165. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
  166. package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
  167. package/dist/layout-engine/types.d.ts +6 -0
  168. package/dist/layout-painter/renderPage.js +1 -0
  169. package/dist/layout-painter/renderParagraph.js +1 -1
  170. package/dist/managers/ContextMenuManager.js +5 -2
  171. package/dist/managers/types.d.ts +3 -0
  172. package/dist/markdown/fromMarkdown.js +2 -1
  173. package/dist/markdown/internals.d.ts +11 -1
  174. package/dist/markdown/internals.js +24 -3
  175. package/dist/markdown/renderBlock.js +39 -9
  176. package/dist/markdown/renderParagraph.d.ts +13 -1
  177. package/dist/markdown/renderParagraph.js +85 -38
  178. package/dist/markdown/renderRuns.js +3 -10
  179. package/dist/markdown/renderTable.js +24 -12
  180. package/dist/markdown/trailers.js +2 -2
  181. package/dist/markdown/types.d.ts +29 -7
  182. package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
  183. package/dist/paged-layout/incrementalMeasure.js +1 -9
  184. package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
  185. package/dist/paged-layout/transactionDirtyRange.js +29 -41
  186. package/dist/panel-layout.d.ts +83 -0
  187. package/dist/panel-layout.js +96 -0
  188. package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
  189. package/dist/prosemirror/anchoredTextBoxes.js +156 -0
  190. package/dist/prosemirror/attrs/index.d.ts +6 -2
  191. package/dist/prosemirror/attrs/index.js +172 -1
  192. package/dist/prosemirror/commands/comments.d.ts +7 -8
  193. package/dist/prosemirror/commands/comments.js +241 -207
  194. package/dist/prosemirror/commands/contentControls.d.ts +1 -1
  195. package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
  196. package/dist/prosemirror/commands/formatting.d.ts +1 -1
  197. package/dist/prosemirror/commands/index.d.ts +2 -2
  198. package/dist/prosemirror/commands/index.js +2 -2
  199. package/dist/prosemirror/commands/paragraph.d.ts +7 -1
  200. package/dist/prosemirror/commands/paragraph.js +9 -1
  201. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
  202. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
  203. package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
  204. package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
  205. package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
  206. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
  207. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
  208. package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
  209. package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
  210. package/dist/prosemirror/commands/sectionBreak.js +2 -1
  211. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
  212. package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
  213. package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
  214. package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
  215. package/dist/prosemirror/conversion/toProseDoc.js +39 -4
  216. package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
  217. package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
  218. package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
  219. package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
  220. package/dist/prosemirror/extensions/StarterKit.js +3 -1
  221. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  222. package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
  223. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
  224. package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
  225. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
  226. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
  227. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
  228. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
  229. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
  230. package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
  231. package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
  232. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
  233. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
  234. package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
  235. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
  236. package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
  237. package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
  238. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
  239. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
  240. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
  241. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
  242. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
  243. package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
  244. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
  245. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  246. package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
  247. package/dist/prosemirror/extensions/types.d.ts +4 -1
  248. package/dist/prosemirror/findReplaceSelection.js +1 -1
  249. package/dist/prosemirror/index.d.ts +3 -2
  250. package/dist/prosemirror/index.js +3 -2
  251. package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
  252. package/dist/prosemirror/indexedNodeLookup.js +103 -0
  253. package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
  254. package/dist/prosemirror/listAutoformatMarkers.js +98 -0
  255. package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
  256. package/dist/prosemirror/listInstanceReferences.js +72 -0
  257. package/dist/prosemirror/listLabels.d.ts +24 -0
  258. package/dist/prosemirror/listLabels.js +111 -0
  259. package/dist/prosemirror/listNumbering.d.ts +110 -0
  260. package/dist/prosemirror/listNumbering.js +345 -0
  261. package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
  262. package/dist/prosemirror/listRenderingAttrs.js +8 -1
  263. package/dist/prosemirror/markupViewProjection.d.ts +95 -0
  264. package/dist/prosemirror/markupViewProjection.js +83 -0
  265. package/dist/prosemirror/numberingAttr.d.ts +9 -1
  266. package/dist/prosemirror/numberingAttr.js +9 -1
  267. package/dist/prosemirror/outlineLevelAttr.js +1 -1
  268. package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
  269. package/dist/prosemirror/paragraphIndentation.js +29 -7
  270. package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
  271. package/dist/prosemirror/paragraphMarkJoin.js +24 -0
  272. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
  273. package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
  274. package/dist/prosemirror/plugins/documentNumbering.js +97 -11
  275. package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
  276. package/dist/prosemirror/plugins/documentStyles.js +14 -1
  277. package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
  278. package/dist/prosemirror/plugins/suggestionMode.js +116 -52
  279. package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
  280. package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
  281. package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
  282. package/dist/prosemirror/positionSweep.d.ts +24 -0
  283. package/dist/prosemirror/positionSweep.js +222 -0
  284. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
  285. package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
  286. package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
  287. package/dist/prosemirror/replacedAnnotations.js +170 -1
  288. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
  289. package/dist/prosemirror/runStyleFormatting.d.ts +7 -1
  290. package/dist/prosemirror/runStyleFormatting.js +13 -7
  291. package/dist/prosemirror/schema/index.d.ts +2 -2
  292. package/dist/prosemirror/schema/index.js +3 -0
  293. package/dist/prosemirror/schema/nodes.d.ts +42 -11
  294. package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
  295. package/dist/prosemirror/sectionMarkEdits.js +59 -0
  296. package/dist/prosemirror/storyListNumbering.d.ts +17 -0
  297. package/dist/prosemirror/storyListNumbering.js +102 -0
  298. package/dist/prosemirror/tableGridMutation.d.ts +48 -2
  299. package/dist/prosemirror/tableGridMutation.js +106 -1
  300. package/dist/prosemirror/validation.js +5 -1
  301. package/dist/redline.js +93 -9
  302. package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
  303. package/dist/render-dom/BodySelectionOverlay.js +3 -2
  304. package/dist/shaping/shaper.d.ts +47 -4
  305. package/dist/shaping/shaper.js +46 -15
  306. package/dist/text-shaping.d.ts +4 -0
  307. package/dist/text-shaping.js +4 -0
  308. package/dist/utils/findReplace.js +2 -2
  309. package/dist/utils/mergeDocumentContent.js +1 -1
  310. package/dist/utils/noteReferenceLabels.d.ts +36 -0
  311. package/dist/utils/noteReferenceLabels.js +32 -0
  312. package/dist/utils/replaceText.js +1 -1
  313. package/dist/version-comparison.d.ts +1 -0
  314. package/dist/version-comparison.js +1 -0
  315. package/package.json +8 -2
  316. package/dist/internal/headlessRevisionResolution.d.ts +0 -23
@@ -1,6 +1,8 @@
1
1
  import { deterministicHexId } from "../utils/hexId.js";
2
2
  import { isXmlNameBoundary, spliceXml } from "./selectiveXmlPatch.js";
3
3
  import { loadDocxArchive } from "./server/boundedArchive.js";
4
+ import { matchCloseTag, matchOpenTag, resolveWordprocessingPrefixes } from "./wordprocessingPrefixes.js";
5
+ import { WORDPROCESSINGML_NAMESPACE_URIS, getChildElements, getLocalName, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
4
6
  import { TaggedError } from "better-result";
5
7
  import JSZip from "jszip";
6
8
  //#region src/docx/ensureParaIds.ts
@@ -48,8 +50,15 @@ import JSZip from "jszip";
48
50
  * declarations and a `mc:Ignorable` listing `w14` when missing — non-Word
49
51
  * producers declare neither, and absent `mc:Ignorable` handling is what
50
52
  * makes pre-2010 consumers choke on the new attributes.
53
+ * - Prefixes are aliases. Each part is scanned under the prefixes its root
54
+ * binds to WordprocessingML (a default namespace included), `w14` and `mc`
55
+ * (see `resolveWordprocessingPrefixes`), and a minted id is written under
56
+ * the part's own `w14` prefix, which is the one `mc:Ignorable` lists. A
57
+ * part whose bindings a string scan cannot follow (a nested element that
58
+ * rebinds one of them) is refused with {@link EnsureParaIdsError}.
51
59
  * - Idempotent: a document that already has full coverage is returned as the
52
- * original bytes, untouched (`alreadyComplete: true`).
60
+ * original bytes, untouched (`alreadyComplete: true`). A body with
61
+ * paragraphs the scan did not see is refused, never reported complete.
53
62
  * - Digitally signed packages are returned untouched when already complete.
54
63
  * When normalization would rewrite the package, it fails unless the caller
55
64
  * explicitly allows signature invalidation after warning the user.
@@ -70,9 +79,6 @@ const TARGET_PART_PATTERNS = [
70
79
  /^word\/footnotes\.xml$/u,
71
80
  /^word\/endnotes\.xml$/u
72
81
  ];
73
- const PARAGRAPH_OPEN = "<w:p";
74
- const FALLBACK_OPEN = "<mc:Fallback";
75
- const FALLBACK_CLOSE = "</mc:Fallback>";
76
82
  const OPAQUE_XML_REGIONS = [
77
83
  {
78
84
  open: "<!--",
@@ -88,15 +94,30 @@ const OPAQUE_XML_REGIONS = [
88
94
  }
89
95
  ];
90
96
  /**
91
- * The parser accepts the `w:` fallback prefix for paraId, and comment parts
92
- * link replies via `w15:paraId` — all three count toward uniqueness.
97
+ * Every prefixed `paraId` counts toward uniqueness: the parser accepts the
98
+ * WordprocessingML fallback, comment parts link replies via `w15:paraId`, and
99
+ * a producer may spell any of them under a prefix of its own. Over-collecting
100
+ * only steers a minted id away from a value; it never keeps one.
93
101
  */
94
- const ANY_PARA_ID_PATTERN = /\bw(?:14|15)?:paraId=(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
95
- const OPEN_TAG_PARA_ID_PATTERN = /\sw(?:14)?:paraId=(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/u;
96
- const OPEN_TAG_TEXT_ID_PATTERN = /\sw(?:14)?:textId=(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/u;
97
- const XMLNS_W14_PATTERN = /\sxmlns:w14=(?<quote>["'])(?<value>[\s\S]*?)\k<quote>/u;
98
- const XMLNS_MC_PATTERN = /\sxmlns:mc=(?<quote>["'])(?<value>[\s\S]*?)\k<quote>/u;
99
- const MC_IGNORABLE_PATTERN = /\smc:Ignorable=(?<quote>["'])(?<value>[\s\S]*?)\k<quote>/u;
102
+ const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId=(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
103
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
104
+ /** An attribute `localName` under any of `prefixes`, with `quote` and `value` groups. */
105
+ const prefixedAttributePattern = (prefixes, localName) => {
106
+ const names = prefixes.filter((prefix) => prefix !== "").map(escapeRegExp);
107
+ return names.length === 0 ? /(?!)/u : new RegExp(`\\s(?:${names.join("|")}):${localName}=(?<quote>["'])(?<value>[\\s\\S]*?)\\k<quote>`, "u");
108
+ };
109
+ const partSpelling = (xml, partPath) => {
110
+ const resolution = resolveWordprocessingPrefixes(xml);
111
+ if (resolution.type === "unsupported") throw createEnsureParaIdsError(`Cannot normalize paragraph ids in ${partPath}: ${resolution.reason}`);
112
+ const { prefixes } = resolution;
113
+ const attributePrefixes = [...prefixes.w14, ...prefixes.main];
114
+ return {
115
+ prefixes,
116
+ paraId: prefixedAttributePattern(attributePrefixes, "paraId"),
117
+ textId: prefixedAttributePattern(attributePrefixes, "textId"),
118
+ w14Prefix: (prefixes.w14Declared ? prefixes.w14[0] : void 0) ?? MC_IGNORABLE_W14
119
+ };
120
+ };
100
121
  /**
101
122
  * Stamp the edits into the part through the splice owner, which refuses a
102
123
  * result that would leave a comment range with only one half. These edits
@@ -141,7 +162,12 @@ const collectExistingParaIds = (xml, into) => {
141
162
  if (id.length > 0) into.add(id.toUpperCase());
142
163
  }
143
164
  };
144
- const collectFallbackParaIds = (xml, partPath, into) => {
165
+ /**
166
+ * Every paragraph open tag of a part in document order, under whichever
167
+ * WordprocessingML prefix the part binds, with whether it sits in
168
+ * `mc:Fallback` (under the part's own `mc` prefix).
169
+ */
170
+ function* paragraphOpenTags(xml, partPath, { prefixes }) {
145
171
  let fallbackDepth = 0;
146
172
  let pos = 0;
147
173
  while (pos < xml.length) {
@@ -152,27 +178,40 @@ const collectFallbackParaIds = (xml, partPath, into) => {
152
178
  pos = opaqueEnd;
153
179
  continue;
154
180
  }
155
- if (xml.startsWith(FALLBACK_OPEN, tagStart) && isXmlNameBoundary(xml[tagStart + 12])) {
181
+ if (matchOpenTag(xml, tagStart, prefixes.mc, "Fallback") !== -1) {
156
182
  const tagEnd = xml.indexOf(">", tagStart);
157
- if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated <mc:Fallback> tag in ${partPath}`);
183
+ if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated mc:Fallback tag in ${partPath}`);
158
184
  if (xml[tagEnd - 1] !== "/") fallbackDepth += 1;
159
185
  pos = tagEnd + 1;
160
186
  continue;
161
187
  }
162
- if (xml.startsWith(FALLBACK_CLOSE, tagStart)) {
188
+ const fallbackClose = matchCloseTag(xml, tagStart, prefixes.mc, "Fallback");
189
+ if (fallbackClose !== -1) {
163
190
  fallbackDepth = Math.max(0, fallbackDepth - 1);
164
- pos = tagStart + 14;
191
+ pos = tagStart + fallbackClose;
165
192
  continue;
166
193
  }
167
- if (fallbackDepth === 0 || !xml.startsWith(PARAGRAPH_OPEN, tagStart) || !isXmlNameBoundary(xml[tagStart + 4])) {
194
+ const nameLength = matchOpenTag(xml, tagStart, prefixes.main, "p");
195
+ if (nameLength === -1) {
168
196
  pos = tagStart + 1;
169
197
  continue;
170
198
  }
171
199
  const tagEnd = xml.indexOf(">", tagStart);
172
- if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated <w:p> tag in ${partPath}`);
173
- const id = OPEN_TAG_PARA_ID_PATTERN.exec(xml.slice(tagStart, tagEnd + 1))?.groups?.["id"];
174
- if (id) into.add(id.toUpperCase());
200
+ if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated paragraph tag in ${partPath}`);
175
201
  pos = tagEnd + 1;
202
+ yield {
203
+ tagStart,
204
+ tagEnd,
205
+ nameEnd: tagStart + nameLength,
206
+ inFallback: fallbackDepth > 0
207
+ };
208
+ }
209
+ }
210
+ const collectFallbackParaIds = (xml, partPath, spelling, into) => {
211
+ for (const { tagStart, tagEnd, inFallback } of paragraphOpenTags(xml, partPath, spelling)) {
212
+ if (!inFallback) continue;
213
+ const id = spelling.paraId.exec(xml.slice(tagStart, tagEnd + 1))?.groups?.["value"];
214
+ if (id) into.add(id.toUpperCase());
176
215
  }
177
216
  };
178
217
  /**
@@ -187,74 +226,45 @@ const mintParaId = (context, partPath, ordinal) => {
187
226
  return id;
188
227
  };
189
228
  /**
190
- * One forward scan over a part: every `<w:p>` open tag outside `mc:Fallback`
191
- * either keeps its paraId (valid, first occurrence) or gets a splice edit
192
- * minting / replacing one. `seen` spans parts so the first-occurrence rule is
193
- * document-wide across the scan order.
229
+ * One forward scan over a part: every paragraph open tag outside
230
+ * `mc:Fallback` either keeps its paraId (valid, first occurrence) or gets a
231
+ * splice edit minting / replacing one. `seen` spans parts so the
232
+ * first-occurrence rule is document-wide across the scan order.
194
233
  */
195
- const scanPart = (xml, partPath, context, seen) => {
234
+ const scanPart = (xml, partPath, spelling, context, seen) => {
196
235
  const edits = [];
236
+ const w14 = spelling.w14Prefix;
197
237
  let assigned = 0;
198
238
  let deduplicated = 0;
199
239
  let ordinal = 0;
200
- let fallbackDepth = 0;
201
- let pos = 0;
202
- while (pos < xml.length) {
203
- const tagStart = xml.indexOf("<", pos);
204
- if (tagStart === -1) break;
205
- const opaqueEnd = skipOpaqueXmlRegion(xml, tagStart, partPath);
206
- if (opaqueEnd !== null) {
207
- pos = opaqueEnd;
208
- continue;
209
- }
210
- if (xml.startsWith(FALLBACK_OPEN, tagStart) && isXmlNameBoundary(xml[tagStart + 12])) {
211
- const tagEnd = xml.indexOf(">", tagStart);
212
- if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated <mc:Fallback> tag in ${partPath}`);
213
- if (xml[tagEnd - 1] !== "/") fallbackDepth += 1;
214
- pos = tagEnd + 1;
215
- continue;
216
- }
217
- if (xml.startsWith(FALLBACK_CLOSE, tagStart)) {
218
- fallbackDepth = Math.max(0, fallbackDepth - 1);
219
- pos = tagStart + 14;
220
- continue;
221
- }
222
- if (!xml.startsWith(PARAGRAPH_OPEN, tagStart) || !isXmlNameBoundary(xml[tagStart + 4])) {
223
- pos = tagStart + 1;
224
- continue;
225
- }
226
- const tagEnd = xml.indexOf(">", tagStart);
227
- if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated <w:p> tag in ${partPath}`);
228
- pos = tagEnd + 1;
229
- if (fallbackDepth > 0) continue;
240
+ for (const { tagStart, tagEnd, nameEnd, inFallback } of paragraphOpenTags(xml, partPath, spelling)) {
241
+ if (inFallback) continue;
230
242
  ordinal += 1;
231
243
  const openTag = xml.slice(tagStart, tagEnd + 1);
232
- const paraIdMatch = OPEN_TAG_PARA_ID_PATTERN.exec(openTag);
244
+ const paraIdMatch = spelling.paraId.exec(openTag);
233
245
  if (!paraIdMatch) {
234
246
  const id = mintParaId(context, partPath, ordinal);
235
- const insertAt = tagStart + 4;
236
- const textIdMatch = OPEN_TAG_TEXT_ID_PATTERN.exec(openTag);
247
+ const textIdMatch = spelling.textId.exec(openTag);
237
248
  if (textIdMatch) {
238
249
  edits.push({
239
- start: insertAt,
240
- end: insertAt,
241
- text: ` w14:paraId="${id}"`
250
+ start: nameEnd,
251
+ end: nameEnd,
252
+ text: ` ${w14}:paraId="${id}"`
242
253
  });
243
- const range = attributeValueRange(textIdMatch, tagStart, "id");
244
254
  edits.push({
245
- ...range,
255
+ ...attributeValueRange(textIdMatch, tagStart, "value"),
246
256
  text: id
247
257
  });
248
258
  } else edits.push({
249
- start: insertAt,
250
- end: insertAt,
251
- text: ` w14:paraId="${id}" w14:textId="${id}"`
259
+ start: nameEnd,
260
+ end: nameEnd,
261
+ text: ` ${w14}:paraId="${id}" ${w14}:textId="${id}"`
252
262
  });
253
263
  assigned += 1;
254
264
  seen.add(id);
255
265
  continue;
256
266
  }
257
- const value = paraIdMatch.groups["id"].toUpperCase();
267
+ const value = paraIdMatch.groups["value"].toUpperCase();
258
268
  const unassigned = RESERVED_ZERO_ID_PATTERN.test(value);
259
269
  if (!unassigned && !seen.has(value)) {
260
270
  seen.add(value);
@@ -262,22 +272,19 @@ const scanPart = (xml, partPath, context, seen) => {
262
272
  }
263
273
  const id = mintParaId(context, partPath, ordinal);
264
274
  edits.push({
265
- ...attributeValueRange(paraIdMatch, tagStart, "id"),
275
+ ...attributeValueRange(paraIdMatch, tagStart, "value"),
266
276
  text: id
267
277
  });
268
- const textIdMatch = OPEN_TAG_TEXT_ID_PATTERN.exec(openTag);
278
+ const textIdMatch = spelling.textId.exec(openTag);
269
279
  if (textIdMatch) edits.push({
270
- ...attributeValueRange(textIdMatch, tagStart, "id"),
280
+ ...attributeValueRange(textIdMatch, tagStart, "value"),
271
281
  text: id
272
282
  });
273
- else {
274
- const insertAt = tagStart + 4;
275
- edits.push({
276
- start: insertAt,
277
- end: insertAt,
278
- text: ` w14:textId="${id}"`
279
- });
280
- }
283
+ else edits.push({
284
+ start: nameEnd,
285
+ end: nameEnd,
286
+ text: ` ${w14}:textId="${id}"`
287
+ });
281
288
  if (unassigned) assigned += 1;
282
289
  else deduplicated += 1;
283
290
  seen.add(id);
@@ -285,7 +292,8 @@ const scanPart = (xml, partPath, context, seen) => {
285
292
  return {
286
293
  edits,
287
294
  assigned,
288
- deduplicated
295
+ deduplicated,
296
+ paragraphs: ordinal
289
297
  };
290
298
  };
291
299
  /**
@@ -293,7 +301,7 @@ const scanPart = (xml, partPath, context, seen) => {
293
301
  * `xmlns:mc` and lists `w14` in `mc:Ignorable`, so consumers that predate the
294
302
  * 2010 extensions skip the new attributes instead of rejecting the part.
295
303
  */
296
- const ensureRootNamespaces = (xml, partPath) => {
304
+ const ensureRootNamespaces = (xml, partPath, { prefixes, w14Prefix }) => {
297
305
  let pos = 0;
298
306
  let rootStart = -1;
299
307
  while (pos < xml.length) {
@@ -317,29 +325,26 @@ const ensureRootNamespaces = (xml, partPath) => {
317
325
  const rootEnd = xml.indexOf(">", rootStart);
318
326
  if (rootEnd === -1) throw createEnsureParaIdsError(`Unterminated root element in ${partPath}`);
319
327
  const rootTag = xml.slice(rootStart, rootEnd + 1);
328
+ if (!prefixes.mcDeclared && prefixes.mc.length === 0) throw createEnsureParaIdsError(`xmlns:mc has an unexpected namespace URI in ${partPath}`);
329
+ if (!prefixes.w14Declared && prefixes.w14.length === 0) throw createEnsureParaIdsError(`xmlns:w14 has an unexpected namespace URI in ${partPath}`);
320
330
  const edits = [];
321
331
  const declarations = [];
322
- const mcNamespace = XMLNS_MC_PATTERN.exec(rootTag);
323
- if (mcNamespace) {
324
- if (mcNamespace.groups["value"] !== MC_NAMESPACE_URI) throw createEnsureParaIdsError(`xmlns:mc has an unexpected namespace URI in ${partPath}`);
325
- } else declarations.push(` xmlns:mc="${MC_NAMESPACE_URI}"`);
326
- const w14Namespace = XMLNS_W14_PATTERN.exec(rootTag);
327
- if (w14Namespace) {
328
- if (w14Namespace.groups["value"] !== W14_NAMESPACE_URI) throw createEnsureParaIdsError(`xmlns:w14 has an unexpected namespace URI in ${partPath}`);
329
- } else declarations.push(` xmlns:w14="${W14_NAMESPACE_URI}"`);
330
- const ignorable = MC_IGNORABLE_PATTERN.exec(rootTag);
332
+ const mcPrefix = (prefixes.mcDeclared ? prefixes.mc[0] : void 0) ?? "mc";
333
+ if (!prefixes.mcDeclared) declarations.push(` xmlns:mc="${MC_NAMESPACE_URI}"`);
334
+ if (!prefixes.w14Declared) declarations.push(` xmlns:w14="${W14_NAMESPACE_URI}"`);
335
+ const ignorable = prefixedAttributePattern(prefixes.mcDeclared ? prefixes.mc : [mcPrefix], "Ignorable").exec(rootTag);
331
336
  if (ignorable) {
332
337
  const tokens = ignorable.groups["value"].split(/\s+/u).filter((token) => token.length > 0);
333
- if (!tokens.includes(MC_IGNORABLE_W14)) {
338
+ if (!tokens.includes(w14Prefix)) {
334
339
  const { end } = attributeValueRange(ignorable, rootStart, "value");
335
- const text = tokens.length === 0 ? MC_IGNORABLE_W14 : ` ${MC_IGNORABLE_W14}`;
340
+ const text = tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`;
336
341
  edits.push({
337
342
  start: end,
338
343
  end,
339
344
  text
340
345
  });
341
346
  }
342
- } else declarations.push(` mc:Ignorable="${MC_IGNORABLE_W14}"`);
347
+ } else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
343
348
  if (declarations.length > 0) {
344
349
  let nameEnd = rootStart + 1;
345
350
  while (nameEnd < xml.length && !isXmlNameBoundary(xml[nameEnd])) nameEnd += 1;
@@ -351,6 +356,17 @@ const ensureRootNamespaces = (xml, partPath) => {
351
356
  }
352
357
  return edits;
353
358
  };
359
+ const hasWordParagraph = (element) => getLocalName(element.name ?? "") === "p" && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "") || getChildElements(element).some(hasWordParagraph);
360
+ /**
361
+ * A body the string scan saw no paragraph in is either genuinely empty or
362
+ * spelled in a way the scan missed. Only the namespace-aware parser can tell
363
+ * which, and it only has to be asked in that rare case: `alreadyComplete`
364
+ * must never describe paragraphs nobody looked at.
365
+ */
366
+ const assertNoUnscannedParagraphs = (xml, partPath) => {
367
+ const root = parseXmlDocument(xml);
368
+ if (root !== null && hasWordParagraph(root)) throw createEnsureParaIdsError(`Cannot normalize paragraph ids in ${partPath}: its paragraphs are not spelled in a way the scan can patch`);
369
+ };
354
370
  /** OPC part names are case-insensitive; compare lowercased. */
355
371
  const isTargetPart = (path) => TARGET_PART_PATTERNS.some((pattern) => pattern.test(path.toLowerCase()));
356
372
  const toUint8Array = (docx) => docx instanceof Uint8Array ? docx : new Uint8Array(docx);
@@ -380,19 +396,25 @@ const ensureParaIdsInternal = async (docx, options) => {
380
396
  docKey: deterministicHexId(partTexts.get(documentPartName))
381
397
  };
382
398
  const targetParts = [documentPartName, ...xmlPartNames.filter((name) => name !== documentPartName && isTargetPart(name)).sort((a, b) => a.localeCompare(b))];
399
+ const spellings = /* @__PURE__ */ new Map();
383
400
  const seen = /* @__PURE__ */ new Set();
384
- for (const [partPath, xml] of partTexts) if (isTargetPart(partPath)) collectFallbackParaIds(xml, partPath, seen);
385
- else collectExistingParaIds(xml, seen);
401
+ for (const [partPath, xml] of partTexts) if (isTargetPart(partPath)) {
402
+ const spelling = partSpelling(xml, partPath);
403
+ spellings.set(partPath, spelling);
404
+ collectFallbackParaIds(xml, partPath, spelling, seen);
405
+ } else collectExistingParaIds(xml, seen);
386
406
  const updates = /* @__PURE__ */ new Map();
387
407
  let assigned = 0;
388
408
  let deduplicated = 0;
389
409
  for (const partPath of targetParts) {
390
410
  const xml = partTexts.get(partPath);
391
- const scan = scanPart(xml, partPath, context, seen);
411
+ const spelling = spellings.get(partPath);
412
+ const scan = scanPart(xml, partPath, spelling, context, seen);
413
+ if (partPath === documentPartName && scan.paragraphs === 0) assertNoUnscannedParagraphs(xml, partPath);
392
414
  if (scan.edits.length === 0) continue;
393
415
  assigned += scan.assigned;
394
416
  deduplicated += scan.deduplicated;
395
- updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(xml, partPath)], partPath));
417
+ updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(xml, partPath, spelling)], partPath));
396
418
  }
397
419
  if (updates.size === 0) return {
398
420
  docx: toUint8Array(docx),
@@ -4,7 +4,8 @@ import { parseParagraph } from "./paragraphParser.js";
4
4
  import { standalonePreviewLedger } from "./previewBudget.js";
5
5
  import { captureSdtSiblingMarkers, parseSdtProperties } from "./sdtProperties.js";
6
6
  import { parseTable } from "./tableParser.js";
7
- import { findChildren, findWordprocessingChild, getAttributes, getChildElements, getLocalName, parseXml } from "./xmlParser.js";
7
+ import { captureVerbatimXml } from "./verbatimCapture.js";
8
+ import { WORDPROCESSINGML_NAMESPACE_URIS, findChildren, findWordprocessingChild, getAttributes, getChildElements, getLocalName, parseXml } from "./xmlParser.js";
8
9
  import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
9
10
  //#region src/docx/footnoteParser.ts
10
11
  /**
@@ -49,7 +50,10 @@ function parseNoteBlockContent(element, styles, theme, numbering, rels, media, p
49
50
  properties,
50
51
  content: sdtContent ? parseNoteBlockContent(sdtContent, styles, theme, numbering, rels, media, previews) : []
51
52
  });
52
- }
53
+ } else if (localName === "altChunk" && WORDPROCESSINGML_NAMESPACE_URIS.has(child.namespaceUri ?? "")) blocks.push({
54
+ type: "preservedBlock",
55
+ xml: captureVerbatimXml(child)
56
+ });
53
57
  }
54
58
  return blocks;
55
59
  }
@@ -79,7 +83,7 @@ function parseFootnotes(footnotesXml, styles = null, theme = null, numbering = n
79
83
  const byId = /* @__PURE__ */ new Map();
80
84
  const footnotes = [];
81
85
  if (!footnotesXml) return createFootnoteMap(byId, footnotes);
82
- const rootElement = parseXml(footnotesXml).elements?.find((el) => el.type === "element" && (el.name === "w:footnotes" || el.name?.endsWith(":footnotes")));
86
+ const rootElement = parseXml(footnotesXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "footnotes");
83
87
  if (!rootElement) return createFootnoteMap(byId, footnotes);
84
88
  const footnoteElements = findChildren(rootElement, "w", "footnote");
85
89
  for (const fnEl of footnoteElements) {
@@ -148,7 +152,7 @@ function parseEndnotes(endnotesXml, styles = null, theme = null, numbering = nul
148
152
  const byId = /* @__PURE__ */ new Map();
149
153
  const endnotes = [];
150
154
  if (!endnotesXml) return createEndnoteMap(byId, endnotes);
151
- const rootElement = parseXml(endnotesXml).elements?.find((el) => el.type === "element" && (el.name === "w:endnotes" || el.name?.endsWith(":endnotes")));
155
+ const rootElement = parseXml(endnotesXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "endnotes");
152
156
  if (!rootElement) return createEndnoteMap(byId, endnotes);
153
157
  const endnoteElements = findChildren(rootElement, "w", "endnote");
154
158
  for (const enEl of endnoteElements) {
@@ -5,7 +5,7 @@ import { assignHeaderFooterVerbatimXml } from "./headerFooterVerbatim.js";
5
5
  import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
6
6
  import { standalonePreviewLedger } from "./previewBudget.js";
7
7
  import { parseWatermark } from "./watermarkParser.js";
8
- import { collectXmlnsDeclarations, parseXml } from "./xmlParser.js";
8
+ import { collectXmlnsDeclarations, getLocalName, parseXml } from "./xmlParser.js";
9
9
  //#region src/docx/headerFooterParser.ts
10
10
  /**
11
11
  * Parse a header XML file (word/header*.xml)
@@ -28,7 +28,7 @@ function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = n
28
28
  content: []
29
29
  };
30
30
  if (!headerXml) return result;
31
- const rootElement = parseXml(headerXml).elements?.find((el) => el.type === "element" && (el.name === "w:hdr" || el.name?.endsWith(":hdr")));
31
+ const rootElement = parseXml(headerXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "hdr");
32
32
  if (!rootElement) return result;
33
33
  const watermarkResult = parseWatermark(rootElement);
34
34
  if (watermarkResult) {
@@ -72,7 +72,7 @@ function parseFooter(footerXml, hdrFtrType = "default", styles = null, theme = n
72
72
  content: []
73
73
  };
74
74
  if (!footerXml) return result;
75
- const rootElement = parseXml(footerXml).elements?.find((el) => el.type === "element" && (el.name === "w:ftr" || el.name?.endsWith(":ftr")));
75
+ const rootElement = parseXml(footerXml).elements?.find((el) => el.type === "element" && getLocalName(el.name ?? "") === "ftr");
76
76
  if (!rootElement) return result;
77
77
  result.content = parseBlockContent(rootElement, styles, theme, numbering, rels, media, {
78
78
  inHeaderFooter: true,
@@ -13,5 +13,24 @@ type NormalizeHeaderFooterReferencesResult = {
13
13
  removedDanglingFooterReferences: number;
14
14
  };
15
15
  declare const normalizeHeaderFooterReferences: ({ documentBody, headers, footers }: NormalizeHeaderFooterReferencesInput) => NormalizeHeaderFooterReferencesResult;
16
+ type AssignHeaderFooterRolesInput = {
17
+ documentBody: document_d_exports.DocumentBody;
18
+ headers?: Map<string, document_d_exports.HeaderFooter>;
19
+ footers?: Map<string, document_d_exports.HeaderFooter>;
20
+ };
21
+ /**
22
+ * Give each header and footer part the role a section reference states for
23
+ * it.
24
+ *
25
+ * A part says nothing about which pages it serves: the `w:type` of the
26
+ * `w:headerReference` / `w:footerReference` naming it does (ECMA-376 Part 1
27
+ * §17.10.5, `ST_HdrFtr`). The parts are read from the document's
28
+ * relationships, before any section is consulted, so each is read as a
29
+ * default part and takes its role here, from the first section that
30
+ * references it. A part no section references stays default; one several
31
+ * sections reference in different roles keeps the first, and a reader that
32
+ * needs every role reads the section references themselves.
33
+ */
34
+ declare const assignHeaderFooterRoles: ({ documentBody, headers, footers }: AssignHeaderFooterRolesInput) => void;
16
35
  //#endregion
17
- export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, normalizeHeaderFooterReferences };
36
+ export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, assignHeaderFooterRoles, normalizeHeaderFooterReferences };
@@ -4,12 +4,9 @@ import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
4
4
  const DANGLING_HEADER_REFERENCE_WARNING = PARSE_WARNING_CODES.danglingHeaderReference;
5
5
  const DANGLING_FOOTER_REFERENCE_WARNING = PARSE_WARNING_CODES.danglingFooterReference;
6
6
  const normalizeHeaderFooterReferences = ({ documentBody, headers, footers }) => {
7
- const seenSectionProperties = /* @__PURE__ */ new Set();
8
7
  let removedDanglingHeaderReferences = 0;
9
8
  let removedDanglingFooterReferences = 0;
10
9
  const normalizeSectionProperties = (sectionProperties) => {
11
- if (!sectionProperties || seenSectionProperties.has(sectionProperties)) return;
12
- seenSectionProperties.add(sectionProperties);
13
10
  const headerResult = removeDanglingReferences(sectionProperties.headerReferences, headers);
14
11
  if (headerResult.changed) {
15
12
  removedDanglingHeaderReferences += headerResult.removed;
@@ -23,34 +20,62 @@ const normalizeHeaderFooterReferences = ({ documentBody, headers, footers }) =>
23
20
  else delete sectionProperties.footerReferences;
24
21
  }
25
22
  };
26
- const normalizeParagraph = (paragraph) => {
27
- normalizeSectionProperties(paragraph.sectionProperties);
23
+ forEachSectionProperties(documentBody, normalizeSectionProperties);
24
+ return {
25
+ removedDanglingHeaderReferences,
26
+ removedDanglingFooterReferences
28
27
  };
29
- const normalizeTable = (table) => {
30
- for (const row of table.rows) for (const cell of row.cells) normalizeBlocks(cell.content);
28
+ };
29
+ /**
30
+ * Visit every distinct section record once, in section order: the paragraph
31
+ * carriers in document order, then the body's final `w:sectPr`.
32
+ */
33
+ const forEachSectionProperties = (documentBody, visit) => {
34
+ const seen = /* @__PURE__ */ new Set();
35
+ const visitOnce = (sectionProperties) => {
36
+ if (!sectionProperties || seen.has(sectionProperties)) return;
37
+ seen.add(sectionProperties);
38
+ visit(sectionProperties);
31
39
  };
32
- const normalizeBlock = (block) => {
33
- if (block.type === "paragraph") {
34
- normalizeParagraph(block);
35
- return;
36
- }
37
- if (block.type === "table") {
38
- normalizeTable(block);
39
- return;
40
- }
41
- if (block.type !== "blockSdt") return;
42
- normalizeBlocks(block.content);
40
+ const visitBlocks = (blocks) => {
41
+ for (const block of blocks) if (block.type === "paragraph") visitOnce(block.sectionProperties);
42
+ else if (block.type === "table") visitTable(block);
43
+ else if (block.type === "blockSdt" || block.type === "blockCustomXml") visitBlocks(block.content);
43
44
  };
44
- const normalizeBlocks = (blocks) => {
45
- for (const block of blocks) normalizeBlock(block);
45
+ const visitTable = (table) => {
46
+ for (const row of table.rows) for (const cell of row.cells) visitBlocks(cell.content);
46
47
  };
47
- normalizeBlocks(documentBody.content);
48
- normalizeSectionProperties(documentBody.finalSectionProperties);
49
- for (const section of documentBody.sections ?? []) normalizeSectionProperties(section.properties);
50
- return {
51
- removedDanglingHeaderReferences,
52
- removedDanglingFooterReferences
48
+ visitBlocks(documentBody.content);
49
+ visitOnce(documentBody.finalSectionProperties);
50
+ for (const section of documentBody.sections ?? []) visitOnce(section.properties);
51
+ };
52
+ /**
53
+ * Give each header and footer part the role a section reference states for
54
+ * it.
55
+ *
56
+ * A part says nothing about which pages it serves: the `w:type` of the
57
+ * `w:headerReference` / `w:footerReference` naming it does (ECMA-376 Part 1
58
+ * §17.10.5, `ST_HdrFtr`). The parts are read from the document's
59
+ * relationships, before any section is consulted, so each is read as a
60
+ * default part and takes its role here, from the first section that
61
+ * references it. A part no section references stays default; one several
62
+ * sections reference in different roles keeps the first, and a reader that
63
+ * needs every role reads the section references themselves.
64
+ */
65
+ const assignHeaderFooterRoles = ({ documentBody, headers, footers }) => {
66
+ const assigned = /* @__PURE__ */ new Set();
67
+ const assign = (references, parts) => {
68
+ for (const { type, rId } of references ?? []) {
69
+ const part = parts?.get(rId);
70
+ if (!part || assigned.has(part)) continue;
71
+ assigned.add(part);
72
+ part.hdrFtrType = type;
73
+ }
53
74
  };
75
+ forEachSectionProperties(documentBody, (sectionProperties) => {
76
+ assign(sectionProperties.headerReferences, headers);
77
+ assign(sectionProperties.footerReferences, footers);
78
+ });
54
79
  };
55
80
  const removeDanglingReferences = (references, validParts) => {
56
81
  if (!references || !validParts) return {
@@ -66,4 +91,4 @@ const removeDanglingReferences = (references, validParts) => {
66
91
  };
67
92
  };
68
93
  //#endregion
69
- export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, normalizeHeaderFooterReferences };
94
+ export { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, assignHeaderFooterRoles, normalizeHeaderFooterReferences };
@@ -1,8 +1,8 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { XmlElement } from "./xmlParser.js";
3
+ import { ChildReader } from "./containerChildren.js";
3
4
  import { PreviewLedger } from "./previewBudget.js";
4
5
  import { StyleMap } from "./styleParser.js";
5
- import { ChildReader } from "./containerChildren.js";
6
6
  //#region src/docx/hyperlinkParser.d.ts
7
7
  /**
8
8
  * Parse a hyperlink element (w:hyperlink)
@@ -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";
2
3
  import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError, DocxEncryptionErrorCode, isDocxEncryptionError } from "./encryption/errors.js";
3
4
  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 };
@@ -0,0 +1,59 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/listNumberingInstances.d.ts
3
+ /** The two kinds of list a command can create. */
4
+ declare const LIST_KINDS: readonly ["bullet", "numbered"];
5
+ type ListKind = (typeof LIST_KINDS)[number];
6
+ /** What a numbered list's first level counts in and how its marker reads. */
7
+ type ListLevelFormat = {
8
+ numFmt: document_d_exports.NumberFormat;
9
+ /** `w:lvlText` for level 0, e.g. `%1.` or `%1)`. */
10
+ lvlText: string;
11
+ };
12
+ /** The first-level appearance and start a new list is minted with. */
13
+ type NewListDefinition = {
14
+ kind: ListKind;
15
+ /** Level-0 format of a numbered list; the template's `%1.` decimal otherwise. */
16
+ format?: ListLevelFormat | undefined;
17
+ /** The value the first item shows, stated as a level-0 `w:startOverride`. */
18
+ start?: number | undefined;
19
+ };
20
+ type MintedListInstance = {
21
+ definitions: document_d_exports.NumberingDefinitions;
22
+ numId: number;
23
+ };
24
+ /**
25
+ * Define a new list: a new `w:abstractNum` of the requested kind and the
26
+ * `w:num` naming it. `definitions` must already hold every instance the
27
+ * document references (see {@link completeListNumbering}), so the new ids
28
+ * collide with none of them.
29
+ */
30
+ declare const mintListInstance: (definitions: document_d_exports.NumberingDefinitions | null | undefined, { kind, format, start }: NewListDefinition) => MintedListInstance;
31
+ type RestartListInstanceOptions = {
32
+ abstractNumId: number;
33
+ ilvl: number;
34
+ start: number;
35
+ };
36
+ /**
37
+ * A new `w:num` over an existing `w:abstractNum`, with a `w:startOverride` at
38
+ * `ilvl`: *Restart Numbering* and *Set Numbering Value*. The override is what
39
+ * makes a consumer count the instance on its own rather than continue the
40
+ * other instances of the same definition.
41
+ */
42
+ declare const restartListInstance: (definitions: document_d_exports.NumberingDefinitions | null | undefined, { abstractNumId, ilvl, start }: RestartListInstanceOptions) => MintedListInstance;
43
+ /** One paragraph's numbering reference and the rendering it resolved to. */
44
+ type ListInstanceReference = {
45
+ numId: number;
46
+ ilvl: number;
47
+ rendering: document_d_exports.ListRendering;
48
+ };
49
+ /**
50
+ * Define every instance `references` name that `definitions` does not.
51
+ *
52
+ * A reference the editor resolved carries its rendering, and a rendering is
53
+ * what a minted instance leaves behind; a reference without one (a dangling
54
+ * id read from a file) has no rendering and so is never defined here. Returns
55
+ * `definitions` itself when nothing is missing.
56
+ */
57
+ declare const completeListNumbering: (definitions: document_d_exports.NumberingDefinitions | undefined, references: Iterable<ListInstanceReference>) => document_d_exports.NumberingDefinitions | undefined;
58
+ //#endregion
59
+ export { LIST_KINDS, ListInstanceReference, ListKind, ListLevelFormat, completeListNumbering, mintListInstance, restartListInstance };