@stll/folio-core 0.52.0 → 0.54.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 (389) 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 +1779 -141
  4. package/dist/ai-edits/batch-claims.d.ts +145 -0
  5. package/dist/ai-edits/batch-claims.js +275 -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 +91 -7
  14. package/dist/ai-edits/headless.js +520 -51
  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 +6 -4
  26. package/dist/ai-edits/read.js +113 -50
  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/revisionStretches.d.ts +17 -0
  30. package/dist/ai-edits/revisionStretches.js +80 -0
  31. package/dist/ai-edits/snapshot.d.ts +36 -3
  32. package/dist/ai-edits/snapshot.js +180 -25
  33. package/dist/ai-edits/table-cell-mutations.d.ts +4 -2
  34. package/dist/ai-edits/table-cell-mutations.js +6 -2
  35. package/dist/ai-edits/table-geometry.d.ts +1 -1
  36. package/dist/ai-edits/table-row-column-mutations.d.ts +44 -17
  37. package/dist/ai-edits/table-row-column-mutations.js +86 -34
  38. package/dist/ai-edits/types.d.ts +155 -16
  39. package/dist/compare/compare.d.ts +1 -1
  40. package/dist/compare/compare.js +65 -16
  41. package/dist/compare/content-alignment.d.ts +5 -1
  42. package/dist/compare/content-alignment.js +52 -34
  43. package/dist/compare/content-types.d.ts +7 -0
  44. package/dist/compare/content.d.ts +4 -1
  45. package/dist/compare/content.js +6 -1
  46. package/dist/compare/formatting.js +2 -1
  47. package/dist/compare/inline-atoms.d.ts +21 -4
  48. package/dist/compare/inline-atoms.js +138 -16
  49. package/dist/compare/inline-provenance.js +1 -1
  50. package/dist/compare/plan.js +10 -5
  51. package/dist/compare/scenario.js +14 -3
  52. package/dist/compare/section-boundary-properties.js +1 -1
  53. package/dist/compare/types.d.ts +4 -2
  54. package/dist/compare/types.js +2 -1
  55. package/dist/compare/verification.d.ts +1 -1
  56. package/dist/compare/verification.js +15 -3
  57. package/dist/controller/committedLayoutDiff.d.ts +25 -0
  58. package/dist/controller/committedLayoutDiff.js +71 -0
  59. package/dist/controller/contentControlWidgetController.d.ts +1 -1
  60. package/dist/controller/folioEditor.d.ts +1 -1
  61. package/dist/controller/fontReadiness.d.ts +70 -3
  62. package/dist/controller/fontReadiness.js +100 -10
  63. package/dist/controller/headerFooterEditorManager.js +6 -1
  64. package/dist/controller/layoutPipeline.d.ts +12 -10
  65. package/dist/controller/layoutPipeline.js +65 -11
  66. package/dist/controller/layoutRunOptions.d.ts +24 -0
  67. package/dist/controller/layoutRunOptions.js +17 -0
  68. package/dist/controller/layoutScheduler.d.ts +19 -14
  69. package/dist/controller/layoutScheduler.js +15 -12
  70. package/dist/controller/layoutSession.d.ts +26 -2
  71. package/dist/controller/layoutSession.js +1 -1
  72. package/dist/controller/noteEditorManager.js +8 -3
  73. package/dist/document-operations.d.ts +11 -2
  74. package/dist/document-operations.js +230 -21
  75. package/dist/docx/altChunk.d.ts +8 -0
  76. package/dist/docx/altChunk.js +17 -0
  77. package/dist/docx/archiveInflation.d.ts +127 -0
  78. package/dist/docx/archiveInflation.js +181 -0
  79. package/dist/docx/attributeRemainder.d.ts +1 -1
  80. package/dist/docx/blockContentParser.d.ts +64 -1
  81. package/dist/docx/blockContentParser.js +17 -4
  82. package/dist/docx/blockCustomXmlShell.d.ts +7 -0
  83. package/dist/docx/blockCustomXmlShell.js +14 -0
  84. package/dist/docx/blockPlainText.js +7 -0
  85. package/dist/docx/commentAnchorIndex.js +1 -1
  86. package/dist/docx/commentRangeIntegrity.js +8 -3
  87. package/dist/docx/commentReferenceNormalization.js +1 -1
  88. package/dist/docx/commentReplyMarkers.js +20 -1
  89. package/dist/docx/compatibility.d.ts +1 -1
  90. package/dist/docx/compatibility.js +34 -13
  91. package/dist/docx/containerChildren.gen.d.ts +1 -1
  92. package/dist/docx/containerChildren.gen.js +1 -0
  93. package/dist/docx/documentParser.js +12 -12
  94. package/dist/docx/drawingIdNormalization.d.ts +3 -1
  95. package/dist/docx/drawingIdNormalization.js +6 -1
  96. package/dist/docx/ensureParaIds.js +119 -97
  97. package/dist/docx/footnoteParser.js +8 -4
  98. package/dist/docx/headerFooterParser.js +3 -3
  99. package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
  100. package/dist/docx/headerFooterReferenceNormalization.js +52 -27
  101. package/dist/docx/hyperlinkParser.d.ts +1 -1
  102. package/dist/docx/index.d.ts +1 -1
  103. package/dist/docx/listNumberingInstances.d.ts +59 -0
  104. package/dist/docx/listNumberingInstances.js +249 -0
  105. package/dist/docx/metadataPrivacy.js +24 -1
  106. package/dist/docx/normalizeBaseDirection.js +2 -1
  107. package/dist/docx/noteReferenceMark.d.ts +15 -0
  108. package/dist/docx/noteReferenceMark.js +48 -0
  109. package/dist/docx/opaqueCarrier.d.ts +7 -0
  110. package/dist/docx/opaqueCarrier.js +38 -0
  111. package/dist/docx/packageParts.js +4 -1
  112. package/dist/docx/paragraphParser.js +96 -27
  113. package/dist/docx/paragraphPropertySource.d.ts +4 -2
  114. package/dist/docx/paragraphPropertySource.js +10 -2
  115. package/dist/docx/paragraphTraversal.js +1 -0
  116. package/dist/docx/parseWarningMessage.js +4 -1
  117. package/dist/docx/parser.js +175 -22
  118. package/dist/docx/rezip.js +31 -7
  119. package/dist/docx/sdtProperties.js +1 -1
  120. package/dist/docx/sdtPropertiesPatch.js +3 -0
  121. package/dist/docx/selectiveSave.js +16 -9
  122. package/dist/docx/selectiveXmlPatch.d.ts +3 -1
  123. package/dist/docx/selectiveXmlPatch.js +186 -39
  124. package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
  125. package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
  126. package/dist/docx/serializer/documentSerializer.js +2 -0
  127. package/dist/docx/serializer/headerFooterSerializer.js +2 -0
  128. package/dist/docx/serializer/noteSerializer.js +3 -1
  129. package/dist/docx/serializer/paragraphSerializer.js +33 -9
  130. package/dist/docx/serializer/tableSerializer.js +25 -38
  131. package/dist/docx/server/boundedArchive.d.ts +8 -13
  132. package/dist/docx/server/boundedArchive.js +70 -59
  133. package/dist/docx/server/createBilingualDocument.js +3 -3
  134. package/dist/docx/server/extractDocxText.d.ts +8 -2
  135. package/dist/docx/server/extractDocxText.js +2 -2
  136. package/dist/docx/server/validateDocxConformance.d.ts +1 -1
  137. package/dist/docx/server/validateDocxConformance.js +1 -0
  138. package/dist/docx/tableParser.d.ts +157 -1
  139. package/dist/docx/tableParser.js +89 -10
  140. package/dist/docx/textBoxParser.js +1 -1
  141. package/dist/docx/unzip.d.ts +20 -2
  142. package/dist/docx/unzip.js +136 -25
  143. package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
  144. package/dist/docx/wordprocessingPrefixes.js +286 -0
  145. package/dist/fonts/embeddedFonts.js +1 -1
  146. package/dist/i18n/messages/catalogs.gen.d.ts +170 -34
  147. package/dist/i18n/messages/catalogs.gen.js +187 -51
  148. package/dist/i18n/messages/messages.gen.d.ts +10 -2
  149. package/dist/internal/compare/inline-presentation.d.ts +2 -1
  150. package/dist/internal/compare/inline-presentation.js +11 -10
  151. package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
  152. package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
  153. package/dist/internal/indexedPositionMap.d.ts +11 -0
  154. package/dist/internal/indexedPositionMap.js +42 -0
  155. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  156. package/dist/internal/revisionResolutionEdits.d.ts +13 -0
  157. package/dist/internal/revisionResolutionEdits.js +19 -0
  158. package/dist/internal/revisionResolutionInline.d.ts +24 -0
  159. package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
  160. package/dist/internal/revisionResolutionStep.d.ts +34 -0
  161. package/dist/internal/revisionResolutionStep.js +282 -0
  162. package/dist/internal/revisionResolutionTracking.d.ts +11 -0
  163. package/dist/internal/revisionResolutionTracking.js +124 -0
  164. package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
  165. package/dist/internal/wholeStoryRevisionResolution.js +600 -0
  166. package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
  167. package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
  168. package/dist/layout-bridge/convert/tableConversion.js +1 -1
  169. package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
  170. package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
  171. package/dist/layout-engine/layoutInstrumentation.js +4 -1
  172. package/dist/layout-engine/measure/cache.d.ts +18 -1
  173. package/dist/layout-engine/measure/cache.js +31 -1
  174. package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
  175. package/dist/layout-engine/measure/measureContainer.js +7 -47
  176. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
  177. package/dist/layout-engine/measure/measureHelpers.js +22 -2
  178. package/dist/layout-engine/measure/measureParagraph.js +2 -2
  179. package/dist/layout-engine/measure/measureWorker.js +2 -3
  180. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
  181. package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
  182. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +12 -1
  183. package/dist/layout-engine/measure/paragraphMeasureShared.js +14 -1
  184. package/dist/layout-engine/types.d.ts +6 -0
  185. package/dist/layout-painter/renderPage.js +1 -0
  186. package/dist/layout-painter/renderParagraph.js +1 -1
  187. package/dist/managers/ContextMenuManager.js +5 -2
  188. package/dist/managers/types.d.ts +3 -0
  189. package/dist/markdown/escape.d.ts +2 -2
  190. package/dist/markdown/escape.js +4 -5
  191. package/dist/markdown/fromMarkdown.js +2 -1
  192. package/dist/markdown/internals.d.ts +11 -1
  193. package/dist/markdown/internals.js +24 -3
  194. package/dist/markdown/renderBlock.js +121 -25
  195. package/dist/markdown/renderParagraph.d.ts +13 -1
  196. package/dist/markdown/renderParagraph.js +85 -38
  197. package/dist/markdown/renderRuns.d.ts +10 -1
  198. package/dist/markdown/renderRuns.js +323 -69
  199. package/dist/markdown/renderTable.js +24 -12
  200. package/dist/markdown/trailers.js +2 -2
  201. package/dist/markdown/types.d.ts +29 -7
  202. package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
  203. package/dist/paged-layout/incrementalMeasure.js +1 -9
  204. package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
  205. package/dist/paged-layout/transactionDirtyRange.js +29 -41
  206. package/dist/panel-layout.d.ts +83 -0
  207. package/dist/panel-layout.js +96 -0
  208. package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
  209. package/dist/prosemirror/anchoredTextBoxes.js +156 -0
  210. package/dist/prosemirror/attrs/index.d.ts +6 -2
  211. package/dist/prosemirror/attrs/index.js +172 -1
  212. package/dist/prosemirror/clearRunColor.d.ts +6 -0
  213. package/dist/prosemirror/clearRunColor.js +46 -0
  214. package/dist/prosemirror/commands/comments.d.ts +7 -8
  215. package/dist/prosemirror/commands/comments.js +281 -212
  216. package/dist/prosemirror/commands/contentControls.d.ts +1 -1
  217. package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
  218. package/dist/prosemirror/commands/formatPainter.js +1 -1
  219. package/dist/prosemirror/commands/formatting.d.ts +1 -1
  220. package/dist/prosemirror/commands/hyperlink.js +2 -1
  221. package/dist/prosemirror/commands/index.d.ts +2 -2
  222. package/dist/prosemirror/commands/index.js +2 -2
  223. package/dist/prosemirror/commands/paragraph.d.ts +7 -1
  224. package/dist/prosemirror/commands/paragraph.js +9 -1
  225. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
  226. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
  227. package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
  228. package/dist/prosemirror/commands/propertyChangeScope.d.ts +3 -2
  229. package/dist/prosemirror/commands/propertyChangeScope.js +24 -3
  230. package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
  231. package/dist/prosemirror/commands/resolveAllTableChanges.js +620 -0
  232. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
  233. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
  234. package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
  235. package/dist/prosemirror/commands/resolveParagraphProperties.js +73 -0
  236. package/dist/prosemirror/commands/sectionBreak.js +2 -1
  237. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +15 -2
  238. package/dist/prosemirror/commands/tableCellMergeResolution.js +23 -7
  239. package/dist/prosemirror/containerFinalParagraph.d.ts +11 -5
  240. package/dist/prosemirror/containerFinalParagraph.js +7 -6
  241. package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
  242. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  243. package/dist/prosemirror/conversion/fromProseDoc.js +177 -605
  244. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -11
  245. package/dist/prosemirror/conversion/toProseDoc.js +70 -154
  246. package/dist/prosemirror/documentSchema.d.ts +8 -0
  247. package/dist/prosemirror/documentSchema.js +18 -0
  248. package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
  249. package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
  250. package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
  251. package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
  252. package/dist/prosemirror/extensions/StarterKit.js +5 -1
  253. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  254. package/dist/prosemirror/extensions/core/ParagraphExtension.js +116 -63
  255. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +4 -1
  256. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +24 -3
  257. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.d.ts +20 -0
  258. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +43 -0
  259. package/dist/prosemirror/extensions/features/ListExtension.js +167 -128
  260. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
  261. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
  262. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
  263. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
  264. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
  265. package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
  266. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +1 -1
  267. package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
  268. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +124 -0
  269. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +33 -11
  270. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +8 -4
  271. package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
  272. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
  273. package/dist/prosemirror/extensions/marks/StrikeExtension.js +2 -2
  274. package/dist/prosemirror/extensions/marks/SubscriptExtension.js +2 -2
  275. package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +2 -2
  276. package/dist/prosemirror/extensions/marks/TextColorExtension.js +4 -3
  277. package/dist/prosemirror/extensions/marks/markUtils.d.ts +15 -8
  278. package/dist/prosemirror/extensions/marks/markUtils.js +25 -10
  279. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +12 -0
  280. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +46 -0
  281. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
  282. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
  283. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +2 -2
  284. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
  285. package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
  286. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -3
  287. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
  288. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  289. package/dist/prosemirror/extensions/nodes/TableExtension.js +121 -226
  290. package/dist/prosemirror/extensions/types.d.ts +11 -1
  291. package/dist/prosemirror/findReplaceSelection.js +1 -1
  292. package/dist/prosemirror/hyperlinkRemoval.d.ts +9 -0
  293. package/dist/prosemirror/hyperlinkRemoval.js +129 -0
  294. package/dist/prosemirror/index.d.ts +5 -3
  295. package/dist/prosemirror/index.js +5 -3
  296. package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
  297. package/dist/prosemirror/indexedNodeLookup.js +103 -0
  298. package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
  299. package/dist/prosemirror/listAutoformatMarkers.js +98 -0
  300. package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
  301. package/dist/prosemirror/listInstanceReferences.js +72 -0
  302. package/dist/prosemirror/listLabels.d.ts +24 -0
  303. package/dist/prosemirror/listLabels.js +111 -0
  304. package/dist/prosemirror/listNumbering.d.ts +92 -0
  305. package/dist/prosemirror/listNumbering.js +330 -0
  306. package/dist/prosemirror/listRendering.d.ts +27 -0
  307. package/dist/prosemirror/listRendering.js +27 -0
  308. package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
  309. package/dist/prosemirror/listRenderingAttrs.js +8 -1
  310. package/dist/prosemirror/markupViewNotes.d.ts +8 -0
  311. package/dist/prosemirror/markupViewNotes.js +49 -0
  312. package/dist/prosemirror/markupViewProjection.d.ts +97 -0
  313. package/dist/prosemirror/markupViewProjection.js +88 -0
  314. package/dist/prosemirror/noteReferenceReview.d.ts +77 -0
  315. package/dist/prosemirror/noteReferenceReview.js +332 -0
  316. package/dist/prosemirror/numberingAttr.d.ts +9 -1
  317. package/dist/prosemirror/numberingAttr.js +9 -1
  318. package/dist/prosemirror/outlineLevelAttr.js +1 -1
  319. package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
  320. package/dist/prosemirror/paragraphIndentation.js +32 -7
  321. package/dist/prosemirror/paragraphMarkJoin.d.ts +21 -0
  322. package/dist/prosemirror/paragraphMarkJoin.js +26 -0
  323. package/dist/prosemirror/paragraphPropertyCarry.d.ts +64 -0
  324. package/dist/prosemirror/paragraphPropertyCarry.js +158 -0
  325. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
  326. package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
  327. package/dist/prosemirror/plugins/documentNumbering.js +97 -11
  328. package/dist/prosemirror/plugins/documentStyleState.d.ts +26 -0
  329. package/dist/prosemirror/plugins/documentStyleState.js +32 -0
  330. package/dist/prosemirror/plugins/documentStyles.d.ts +3 -13
  331. package/dist/prosemirror/plugins/documentStyles.js +67 -17
  332. package/dist/prosemirror/plugins/index.d.ts +2 -1
  333. package/dist/prosemirror/plugins/index.js +2 -1
  334. package/dist/prosemirror/plugins/paragraphStyleResolution.d.ts +15 -0
  335. package/dist/prosemirror/plugins/paragraphStyleResolution.js +230 -0
  336. package/dist/prosemirror/plugins/suggestionMode.d.ts +27 -3
  337. package/dist/prosemirror/plugins/suggestionMode.js +490 -63
  338. package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
  339. package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
  340. package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
  341. package/dist/prosemirror/positionSweep.d.ts +24 -0
  342. package/dist/prosemirror/positionSweep.js +222 -0
  343. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +23 -3
  344. package/dist/prosemirror/rebaseParagraphRunFormatting.js +39 -13
  345. package/dist/prosemirror/rebaseParagraphRuns.d.ts +36 -0
  346. package/dist/prosemirror/rebaseParagraphRuns.js +99 -0
  347. package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
  348. package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
  349. package/dist/prosemirror/replacedAnnotations.js +170 -1
  350. package/dist/prosemirror/runFormattingFromMarks.d.ts +18 -0
  351. package/dist/prosemirror/runFormattingFromMarks.js +567 -0
  352. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
  353. package/dist/prosemirror/runFormattingReconciliation.js +10 -9
  354. package/dist/prosemirror/schema/index.d.ts +2 -2
  355. package/dist/prosemirror/schema/index.js +3 -0
  356. package/dist/prosemirror/schema/nodes.d.ts +44 -11
  357. package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
  358. package/dist/prosemirror/sectionMarkEdits.js +59 -0
  359. package/dist/prosemirror/storyListNumbering.d.ts +17 -0
  360. package/dist/prosemirror/storyListNumbering.js +102 -0
  361. package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +59 -0
  362. package/dist/prosemirror/styles/paragraphStyleCascade.js +145 -0
  363. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +21 -2
  364. package/dist/prosemirror/styles/resolvedStyleAttrs.js +38 -1
  365. package/dist/prosemirror/styles/tableStyleRegions.d.ts +31 -0
  366. package/dist/prosemirror/styles/tableStyleRegions.js +55 -0
  367. package/dist/prosemirror/tableCellPaste.d.ts +74 -0
  368. package/dist/prosemirror/tableCellPaste.js +525 -0
  369. package/dist/prosemirror/tableGridMutation.d.ts +87 -2
  370. package/dist/prosemirror/tableGridMutation.js +255 -2
  371. package/dist/prosemirror/tableRunIn.d.ts +51 -0
  372. package/dist/prosemirror/tableRunIn.js +165 -0
  373. package/dist/prosemirror/textInput.js +11 -2
  374. package/dist/prosemirror/trackedRevisionPath.d.ts +17 -0
  375. package/dist/prosemirror/trackedRevisionPath.js +32 -0
  376. package/dist/prosemirror/validation.js +5 -1
  377. package/dist/redline.js +93 -9
  378. package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
  379. package/dist/render-dom/BodySelectionOverlay.js +3 -2
  380. package/dist/server.d.ts +2 -2
  381. package/dist/utils/findReplace.js +2 -2
  382. package/dist/utils/mergeDocumentContent.js +1 -1
  383. package/dist/utils/noteReferenceLabels.d.ts +36 -0
  384. package/dist/utils/noteReferenceLabels.js +32 -0
  385. package/dist/utils/replaceText.js +1 -1
  386. package/dist/version-comparison.d.ts +1 -0
  387. package/dist/version-comparison.js +1 -0
  388. package/package.json +6 -5
  389. package/dist/internal/headlessRevisionResolution.d.ts +0 -23
@@ -1,4 +1,5 @@
1
1
  import { bytesToDataUrl } from "../utils/base64.js";
2
+ import { compressionRatioLimitFor, countCentralDirectoryRecords, createInflationBudget, exceedsCompressionRatio, getZipEntrySizes, inflateEntryWithinLimits, isStoredZipEntry } from "./archiveInflation.js";
2
3
  import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./encryption/containerFormat.js";
3
4
  import { openDocxBuffer } from "./encryption/openEncryptedDocx.js";
4
5
  import { decodeXmlBytes } from "./xmlEncoding.js";
@@ -60,6 +61,7 @@ const DEFAULT_UNZIP_LIMITS = {
60
61
  maxMediaBytes: 25 * MEBIBYTE,
61
62
  maxFontBytes: 10 * MEBIBYTE,
62
63
  maxTotalUncompressedBytes: 250 * MEBIBYTE,
64
+ maxCompressionRatio: 200,
63
65
  maxXmlElementsPerPart: FOLIO_XML_RESOURCE_LIMITS.maxElementsPerPart,
64
66
  maxXmlAttributesPerPart: FOLIO_XML_RESOURCE_LIMITS.maxAttributesPerPart,
65
67
  maxXmlElementsPerPackage: FOLIO_XML_RESOURCE_LIMITS.maxElementsPerPackage,
@@ -101,19 +103,23 @@ const ZIP_CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE = 33639248;
101
103
  const ZIP_END_OF_CENTRAL_DIRECTORY_SIZE = 22;
102
104
  const ZIP_END_OF_CENTRAL_DIRECTORY_WITH_COUNTS_SIZE = 12;
103
105
  const ZIP_CENTRAL_DIRECTORY_FILE_HEADER_SIZE = 46;
106
+ /** Entries inflated at once; the rest wait for a slot. */
107
+ const EXTRACTION_CONCURRENCY = 6;
104
108
  /**
105
109
  * Extract all content from a DOCX file
106
110
  *
107
111
  * @param buffer - DOCX file as ArrayBuffer
108
112
  * @returns Promise resolving to extracted content
109
113
  */
110
- async function unzipDocx(buffer, options = {}) {
114
+ async function unzipDocx(buffer, options = {}, { verifyUnreadEntries = true } = {}) {
111
115
  const limits = createUnzipLimits(options);
112
116
  if (buffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
113
117
  const containerType = detectDocxContainerType(buffer);
114
118
  const zipBuffer = containerType === DOCX_CONTAINER_TYPES.ZIP ? buffer : await openDocxBuffer(buffer, { password: options.password });
115
119
  if (zipBuffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
116
120
  const wasEncrypted = containerType === DOCX_CONTAINER_TYPES.CFB;
121
+ const maxRecords = limits.maxFiles * 2;
122
+ if (countCentralDirectoryRecords(new Uint8Array(zipBuffer), maxRecords) > maxRecords) throw new DocxSecurityError("DOCX file contains too many entries");
117
123
  const loaded = await loadDocxZip(zipBuffer, limits.maxFiles);
118
124
  if (loaded.buffer.byteLength > limits.maxInputBytes) throw new DocxSecurityError("DOCX file exceeds the maximum allowed size");
119
125
  const { zip } = loaded;
@@ -149,40 +155,78 @@ async function unzipDocx(buffer, options = {}) {
149
155
  wasEncrypted
150
156
  };
151
157
  let totalUncompressedBytes = 0;
158
+ const inflationBudget = createInflationBudget(limits.maxTotalUncompressedBytes);
152
159
  const extractionTasks = [];
160
+ const verifyEntry = (file, path) => {
161
+ if (!verifyUnreadEntries || isStoredZipEntry(file)) return;
162
+ extractionTasks.push(async () => {
163
+ await verifyEntryWithinLimits({
164
+ file,
165
+ path,
166
+ limits,
167
+ budget: inflationBudget
168
+ });
169
+ return null;
170
+ });
171
+ };
172
+ const inflate = async (file, maxEntryBytes) => await inflateEntryWithinLimits({
173
+ entry: file,
174
+ maxEntryBytes,
175
+ maxCompressionRatio: compressionRatioLimitFor(file.name, limits.maxCompressionRatio),
176
+ budget: inflationBudget
177
+ });
153
178
  for (const [path, file] of entries) {
154
179
  if (!isSafeDocxPath(path)) throw new DocxSecurityError("DOCX file contains an unsafe entry path");
155
- const declaredSize = getEntryUncompressedSize(file);
180
+ const { compressedBytes, uncompressedBytes: declaredSize } = getZipEntrySizes(file);
156
181
  if (declaredSize !== null) {
157
182
  totalUncompressedBytes += declaredSize;
158
183
  if (totalUncompressedBytes > limits.maxTotalUncompressedBytes) throw new DocxSecurityError("DOCX file expands beyond the maximum allowed size");
184
+ if (exceedsCompressionRatio({
185
+ inflatedBytes: declaredSize,
186
+ compressedBytes,
187
+ maxRatio: compressionRatioLimitFor(path, limits.maxCompressionRatio)
188
+ })) throw new DocxSecurityError(`DOCX entry exceeds the maximum compression ratio: ${path}`);
189
+ }
190
+ if (!isParsedDocxEntry(path)) {
191
+ verifyEntry(file, path);
192
+ continue;
159
193
  }
160
- if (!isParsedDocxEntry(path)) continue;
161
194
  const lowerPath = path.toLowerCase();
162
195
  if (lowerPath.endsWith(".xml") || lowerPath.endsWith(".rels")) {
163
196
  assertEntrySize(path, declaredSize, limits.maxXmlBytes);
164
- if (options.extractAllXml === false && !shouldExtractXmlPart(lowerPath)) continue;
165
- extractionTasks.push(() => file.async("uint8array").then((xmlBytes) => {
166
- assertExtractedSize(path, xmlBytes.byteLength, limits.maxXmlBytes);
197
+ if (options.extractAllXml === false && !shouldExtractXmlPart(lowerPath)) {
198
+ verifyEntry(file, path);
199
+ continue;
200
+ }
201
+ extractionTasks.push(async () => {
202
+ const result = await inflate(file, limits.maxXmlBytes);
203
+ if (!result.ok) throw inflationLimitError(result.limit, path);
167
204
  return {
168
205
  type: "xml",
169
206
  path,
170
207
  lowerPath,
171
- content: decodeXmlBytes(xmlBytes)
208
+ content: decodeXmlBytes(result.bytes)
172
209
  };
173
- }));
210
+ });
174
211
  } else if (lowerPath.startsWith("word/media/")) {
175
212
  const mimeType = getMediaMimeType(path);
176
- if (!limits.allowedMediaMimeTypes.has(mimeType)) continue;
213
+ if (!limits.allowedMediaMimeTypes.has(mimeType)) {
214
+ verifyEntry(file, path);
215
+ continue;
216
+ }
177
217
  if (isEntryTooLarge(declaredSize, limits.maxMediaBytes)) {
178
218
  content.warnings.push(`Skipped oversized media file: ${path}; original entry preserved for round-trip.`);
219
+ verifyEntry(file, path);
179
220
  continue;
180
221
  }
181
- extractionTasks.push(() => file.async("arraybuffer").then((binaryContent) => {
182
- if (binaryContent.byteLength > limits.maxMediaBytes) {
222
+ extractionTasks.push(async () => {
223
+ const result = await inflate(file, limits.maxMediaBytes);
224
+ if (!result.ok) {
225
+ if (result.limit !== "entry") throw inflationLimitError(result.limit, path);
183
226
  content.warnings.push(`Skipped oversized media file: ${path}; original entry preserved for round-trip.`);
184
227
  return null;
185
228
  }
229
+ const binaryContent = result.bytes.buffer;
186
230
  if (!isMediaContentAllowed(binaryContent, mimeType)) return null;
187
231
  return {
188
232
  type: "media",
@@ -190,21 +234,33 @@ async function unzipDocx(buffer, options = {}) {
190
234
  mimeType,
191
235
  content: binaryContent
192
236
  };
193
- }));
237
+ });
194
238
  } else if (lowerPath.startsWith("word/fonts/")) {
195
- if (isEntryTooLarge(declaredSize, limits.maxFontBytes)) continue;
196
- extractionTasks.push(() => file.async("arraybuffer").then((binaryContent) => {
197
- if (binaryContent.byteLength > limits.maxFontBytes) return null;
239
+ if (isEntryTooLarge(declaredSize, limits.maxFontBytes)) {
240
+ verifyEntry(file, path);
241
+ continue;
242
+ }
243
+ extractionTasks.push(async () => {
244
+ const result = await inflate(file, limits.maxFontBytes);
245
+ if (!result.ok) {
246
+ if (result.limit !== "entry") throw inflationLimitError(result.limit, path);
247
+ return null;
248
+ }
198
249
  return {
199
250
  type: "font",
200
251
  path,
201
- content: binaryContent
252
+ content: result.bytes.buffer
202
253
  };
203
- }));
204
- }
254
+ });
255
+ } else verifyEntry(file, path);
205
256
  }
257
+ if (exceedsCompressionRatio({
258
+ inflatedBytes: totalUncompressedBytes,
259
+ compressedBytes: loaded.buffer.byteLength,
260
+ maxRatio: limits.maxCompressionRatio
261
+ })) throw new DocxSecurityError("DOCX file exceeds the maximum compression ratio");
206
262
  const xmlBudget = createXmlPackageBudget();
207
- for (const extracted of await Promise.all(extractionTasks.map((extract) => extract()))) {
263
+ for (const extracted of await runExtractionTasks(extractionTasks, inflationBudget)) {
208
264
  if (!extracted) continue;
209
265
  if (extracted.type === "xml") {
210
266
  assignXmlContent(content, extracted, limits, xmlBudget);
@@ -262,6 +318,62 @@ function assignXmlContent(content, { path, lowerPath, content: xmlContent }, lim
262
318
  content.footers.set(filename, xmlContent);
263
319
  }
264
320
  }
321
+ function inflationLimitError(limit, path) {
322
+ switch (limit) {
323
+ case "declared-size": return new DocxSecurityError(`DOCX entry inflates past its declared size: ${path}`);
324
+ case "compression-ratio": return new DocxSecurityError(`DOCX entry exceeds the maximum compression ratio: ${path}`);
325
+ case "entry": return new DocxSecurityError(`DOCX entry exceeds maximum size: ${path}`);
326
+ case "total": return new DocxSecurityError("DOCX file expands beyond the maximum allowed size");
327
+ case "aborted": return new DocxSecurityError(`DOCX entry was not read after another entry failed: ${path}`);
328
+ }
329
+ }
330
+ /**
331
+ * Run extraction tasks a few at a time, in order, keeping their results in
332
+ * task order. The first failure marks the shared budget aborted, which stops
333
+ * the streams still running at their next chunk and leaves the queued tasks
334
+ * unstarted.
335
+ */
336
+ async function runExtractionTasks(tasks, budget) {
337
+ const results = Array.from({ length: tasks.length }, () => null);
338
+ let next = 0;
339
+ const worker = async () => {
340
+ while (next < tasks.length && !budget.aborted) {
341
+ const index = next;
342
+ next += 1;
343
+ const task = tasks[index];
344
+ if (!task) return;
345
+ try {
346
+ results[index] = await task();
347
+ } catch (error) {
348
+ budget.aborted = true;
349
+ throw error;
350
+ }
351
+ }
352
+ };
353
+ await Promise.all(Array.from({ length: Math.min(EXTRACTION_CONCURRENCY, tasks.length) }, async () => await worker()));
354
+ return results;
355
+ }
356
+ /**
357
+ * Inflate an entry without keeping it, to bound what a later read of it can
358
+ * produce. A limit refuses the package. A corrupt entry does not: it was never
359
+ * going to be read here, a later read stops at the same byte, and refusing
360
+ * the whole document over a part folio does not model would be a regression.
361
+ */
362
+ async function verifyEntryWithinLimits({ file, path, limits, budget }) {
363
+ let result;
364
+ try {
365
+ result = await inflateEntryWithinLimits({
366
+ entry: file,
367
+ maxEntryBytes: Number.POSITIVE_INFINITY,
368
+ maxCompressionRatio: compressionRatioLimitFor(path, limits.maxCompressionRatio),
369
+ budget,
370
+ retain: false
371
+ });
372
+ } catch {
373
+ return;
374
+ }
375
+ if (!result.ok) throw inflationLimitError(result.limit, path);
376
+ }
265
377
  async function loadDocxZip(buffer, maxFiles) {
266
378
  try {
267
379
  return {
@@ -336,9 +448,12 @@ function findLastSignature(view, byteLength, signature) {
336
448
  return -1;
337
449
  }
338
450
  function createUnzipLimits(options) {
451
+ const { maxCompressionRatio } = options;
452
+ if (maxCompressionRatio !== void 0 && (!Number.isSafeInteger(maxCompressionRatio) || maxCompressionRatio < 0)) throw new RangeError("DOCX compression ratio limit must be a non-negative safe integer");
339
453
  return {
340
454
  ...DEFAULT_UNZIP_LIMITS,
341
455
  ...options,
456
+ maxCompressionRatio: maxCompressionRatio ?? DEFAULT_UNZIP_LIMITS.maxCompressionRatio,
342
457
  allowedMediaMimeTypes: options.allowedMediaMimeTypes ? new Set(options.allowedMediaMimeTypes) : DEFAULT_UNZIP_LIMITS.allowedMediaMimeTypes
343
458
  };
344
459
  }
@@ -375,8 +490,7 @@ function isParsedDocxEntry(path) {
375
490
  return lowerPath === "[content_types].xml" || lowerPath.startsWith("_rels/") || lowerPath.startsWith("docprops/") || lowerPath.startsWith("word/") || lowerPath.startsWith("customxml/");
376
491
  }
377
492
  function getEntryUncompressedSize(file) {
378
- const metadata = file._data;
379
- return typeof metadata?.uncompressedSize === "number" ? metadata.uncompressedSize : null;
493
+ return getZipEntrySizes(file).uncompressedBytes;
380
494
  }
381
495
  function assertEntrySize(path, declaredSize, maxBytes) {
382
496
  if (isEntryTooLarge(declaredSize, maxBytes)) throw new DocxSecurityError(`DOCX entry exceeds maximum size: ${path}`);
@@ -384,9 +498,6 @@ function assertEntrySize(path, declaredSize, maxBytes) {
384
498
  function isEntryTooLarge(declaredSize, maxBytes) {
385
499
  return declaredSize !== null && declaredSize > maxBytes;
386
500
  }
387
- function assertExtractedSize(path, byteLength, maxBytes) {
388
- if (byteLength > maxBytes) throw new DocxSecurityError(`DOCX entry exceeds maximum size: ${path}`);
389
- }
390
501
  function isMediaContentAllowed(data, mimeType) {
391
502
  const bytes = new Uint8Array(data);
392
503
  switch (mimeType) {
@@ -494,4 +605,4 @@ function getContentSummary(content) {
494
605
  };
495
606
  }
496
607
  //#endregion
497
- export { DocxSecurityError, extractFile, getContentSummary, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, unzipDocx };
608
+ export { DocxSecurityError, extractFile, getContentSummary, getEntryUncompressedSize, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, unzipDocx };
@@ -0,0 +1,78 @@
1
+ //#region src/docx/wordprocessingPrefixes.d.ts
2
+ /**
3
+ * Which prefixes a WordprocessingML part spells its namespaces with.
4
+ *
5
+ * A prefix is an alias: `<w:p>`, `<x:p>` and an unprefixed `<p>` under a
6
+ * default namespace are the same paragraph when the prefix (or the default)
7
+ * is bound to the WordprocessingML URI. The parser resolves names by URI, so
8
+ * every such package opens. The patchers that work on part XML as a string
9
+ * (`ensureParaIds`, the selective-save splices) find elements by their
10
+ * literal tags instead, and a literal `<w:p` finds nothing in a part that
11
+ * spells it `<x:p`: the patch reports success having touched nothing.
12
+ *
13
+ * {@link resolveWordprocessingPrefixes} is the one place those patchers ask
14
+ * how a part spells WordprocessingML, the `w14` extensions and
15
+ * markup compatibility (`mc`). A patcher either scans with every prefix the
16
+ * part binds, or checks the names its literal scan needs in their namespace
17
+ * scope. A prefix conflict on one of those names is refused.
18
+ */
19
+ /** How one part spells the namespaces a string-level patcher scans for. */
20
+ type WordprocessingPrefixes = {
21
+ /**
22
+ * Prefixes bound to WordprocessingML (Transitional or Strict), `""` for the
23
+ * default namespace. Never empty: an undeclared part reads as `w`.
24
+ */
25
+ main: readonly string[];
26
+ /** Prefixes bound to the `w14` extensions namespace. */
27
+ w14: readonly string[];
28
+ /** Whether a `w14` prefix is declared on the root (else `w14` is assumed). */
29
+ w14Declared: boolean;
30
+ /** Prefixes bound to markup compatibility (`mc`). */
31
+ mc: readonly string[];
32
+ /** Whether an `mc` prefix is declared on the root (else `mc` is assumed). */
33
+ mcDeclared: boolean;
34
+ /**
35
+ * The spelling folio's serializer writes: WordprocessingML only as `w`,
36
+ * `w14` and `mc` under their conventional prefixes. Splicing serializer
37
+ * output into a part is only sound when this holds.
38
+ */
39
+ canonical: boolean;
40
+ };
41
+ type WordprocessingPrefixResolution = {
42
+ type: "resolved";
43
+ prefixes: WordprocessingPrefixes;
44
+ } | {
45
+ type: "unsupported";
46
+ reason: string;
47
+ };
48
+ declare const resolveWordprocessingPrefixes: (xml: string) => WordprocessingPrefixResolution;
49
+ /** Root spelling for a selective scan whose relevant nested names are checked separately. */
50
+ declare const resolveSelectiveScanPrefixes: (xml: string) => WordprocessingPrefixResolution;
51
+ /**
52
+ * Whether a patcher that reads `<w:…>` literally sees every element it looks
53
+ * for in `xml`, and may splice the serializer's `w:` markup into it.
54
+ *
55
+ * True for the canonical spelling. Also true when the part binds
56
+ * WordprocessingML under an extra prefix besides `w` but never spells the
57
+ * names the patcher scans under that alias, or binds markup compatibility
58
+ * under another prefix. Relevant nested names are checked in their own scope.
59
+ */
60
+ declare const splicesAsCanonical: (xml: string, localNames: readonly string[]) => boolean;
61
+ /** The names the paragraph splices scan for: paragraphs, their containers and ids. */
62
+ declare const PARAGRAPH_SCAN_NAMES: readonly ["p", "tc", "txbxContent", "paraId", "textId"];
63
+ /** Whether `xml` spells WordprocessingML the way folio's serializer does. */
64
+ declare const hasCanonicalWordprocessingPrefixes: (xml: string) => boolean;
65
+ /** `<p`, `<w:p`, … — the open-tag literal of `localName` under `prefix`. */
66
+ declare const openTagLiteral: (prefix: string, localName: string) => string;
67
+ /** `</p>`, `</w:p>`, … — the close-tag literal of `localName` under `prefix`. */
68
+ declare const closeTagLiteral: (prefix: string, localName: string) => string;
69
+ /**
70
+ * Whether `xml` opens `localName` under one of `prefixes` at `start`, and the
71
+ * length of the literal that matched (`-1` when none did). The character after
72
+ * the name must end it, so `<w:pPr` is not `<w:p`.
73
+ */
74
+ declare const matchOpenTag: (xml: string, start: number, prefixes: readonly string[], localName: string) => number;
75
+ /** The length of the close tag of `localName` under one of `prefixes` at `start`, or -1. */
76
+ declare const matchCloseTag: (xml: string, start: number, prefixes: readonly string[], localName: string) => number;
77
+ //#endregion
78
+ export { PARAGRAPH_SCAN_NAMES, WordprocessingPrefixResolution, WordprocessingPrefixes, closeTagLiteral, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, openTagLiteral, resolveSelectiveScanPrefixes, resolveWordprocessingPrefixes, splicesAsCanonical };
@@ -0,0 +1,286 @@
1
+ import { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, getLocalName, getNamespacePrefix, getNamespaceUri, parseXmlDocument, resolveAttributeNamespaceUri } from "./xmlParser.js";
2
+ //#region src/docx/wordprocessingPrefixes.ts
3
+ /**
4
+ * Which prefixes a WordprocessingML part spells its namespaces with.
5
+ *
6
+ * A prefix is an alias: `<w:p>`, `<x:p>` and an unprefixed `<p>` under a
7
+ * default namespace are the same paragraph when the prefix (or the default)
8
+ * is bound to the WordprocessingML URI. The parser resolves names by URI, so
9
+ * every such package opens. The patchers that work on part XML as a string
10
+ * (`ensureParaIds`, the selective-save splices) find elements by their
11
+ * literal tags instead, and a literal `<w:p` finds nothing in a part that
12
+ * spells it `<x:p`: the patch reports success having touched nothing.
13
+ *
14
+ * {@link resolveWordprocessingPrefixes} is the one place those patchers ask
15
+ * how a part spells WordprocessingML, the `w14` extensions and
16
+ * markup compatibility (`mc`). A patcher either scans with every prefix the
17
+ * part binds, or checks the names its literal scan needs in their namespace
18
+ * scope. A prefix conflict on one of those names is refused.
19
+ */
20
+ const XMLNS = "xmlns";
21
+ const isWhitespace = (character) => character === " " || character === " " || character === "\n" || character === "\r";
22
+ /** End offset (exclusive) of the tag opening at `start`, skipping quoted values. */
23
+ const tagEnd = (xml, start) => {
24
+ let quote = null;
25
+ for (let index = start + 1; index < xml.length; index += 1) {
26
+ const character = xml[index];
27
+ if (quote !== null) {
28
+ if (character === quote) quote = null;
29
+ continue;
30
+ }
31
+ if (character === "\"" || character === "'") quote = character;
32
+ else if (character === ">") return index + 1;
33
+ }
34
+ return -1;
35
+ };
36
+ /** The `xmlns` / `xmlns:*` declarations written in one start tag. */
37
+ const declarationsIn = (tag) => {
38
+ const declarations = [];
39
+ for (const match of tag.matchAll(/\sxmlns(?::(?<prefix>[^\s=/>]+))?\s*=\s*(?<quote>["'])(?<uri>[\s\S]*?)\k<quote>/gu)) declarations.push({
40
+ prefix: match.groups?.["prefix"] ?? "",
41
+ uri: match.groups?.["uri"] ?? ""
42
+ });
43
+ return declarations;
44
+ };
45
+ const OPAQUE_REGIONS = [
46
+ {
47
+ open: "<!--",
48
+ close: "-->"
49
+ },
50
+ {
51
+ open: "<![CDATA[",
52
+ close: "]]>"
53
+ },
54
+ {
55
+ open: "<?",
56
+ close: "?>"
57
+ }
58
+ ];
59
+ /** Visit every start tag of `xml` that declares a namespace, root first. */
60
+ const visitDeclaringStartTags = (xml, visit) => {
61
+ let seenRoot = false;
62
+ let pos = 0;
63
+ scan: while (pos < xml.length) {
64
+ const start = xml.indexOf("<", pos);
65
+ if (start === -1) break;
66
+ for (const { open, close } of OPAQUE_REGIONS) if (xml.startsWith(open, start)) {
67
+ const closeStart = xml.indexOf(close, start + open.length);
68
+ if (closeStart === -1) return false;
69
+ pos = closeStart + close.length;
70
+ continue scan;
71
+ }
72
+ const next = xml[start + 1];
73
+ if (next === "!" || next === "/") {
74
+ const end = xml.indexOf(">", start);
75
+ if (end === -1) return false;
76
+ pos = end + 1;
77
+ continue;
78
+ }
79
+ const end = tagEnd(xml, start);
80
+ if (end === -1) return false;
81
+ const isRoot = !seenRoot;
82
+ seenRoot = true;
83
+ const tag = xml.slice(start, end);
84
+ if (isRoot || tag.includes(XMLNS)) visit(tag, isRoot);
85
+ pos = end;
86
+ }
87
+ return seenRoot;
88
+ };
89
+ const MAIN_URIS = WORDPROCESSINGML_NAMESPACE_URIS;
90
+ const W14_URI = NAMESPACES.w14;
91
+ const MC_URI = NAMESPACES.mc;
92
+ const NESTED_BINDINGS = {
93
+ strict: "strict",
94
+ scoped: "scoped"
95
+ };
96
+ const slotOf = (uri) => {
97
+ if (MAIN_URIS.has(uri)) return "main";
98
+ if (uri === W14_URI) return "w14";
99
+ if (uri === MC_URI) return "mc";
100
+ return null;
101
+ };
102
+ const CONVENTIONAL_PREFIX = {
103
+ main: "w",
104
+ w14: "w14",
105
+ mc: "mc"
106
+ };
107
+ /**
108
+ * Resolve the prefixes `xml` binds to WordprocessingML, `w14` and `mc`.
109
+ *
110
+ * Root declarations decide. A namespace the root never declares is read under
111
+ * its conventional prefix, the tolerant reading of a malformed part. A declaration on
112
+ * a nested element is accepted only when it repeats a binding the scan already
113
+ * uses; anything else changes what a literal tag means partway through the
114
+ * part and is reported as unsupported.
115
+ */
116
+ const resolvePrefixes = (xml, nestedBindings) => {
117
+ const bound = {
118
+ main: [],
119
+ w14: [],
120
+ mc: []
121
+ };
122
+ const rootPrefixes = /* @__PURE__ */ new Map();
123
+ const nested = [];
124
+ if (!visitDeclaringStartTags(xml, (tag, isRoot) => {
125
+ for (const declaration of declarationsIn(tag)) {
126
+ if (!isRoot) {
127
+ nested.push(declaration);
128
+ continue;
129
+ }
130
+ rootPrefixes.set(declaration.prefix, declaration.uri);
131
+ const slot = slotOf(declaration.uri);
132
+ if (slot !== null) bound[slot].push(declaration.prefix);
133
+ }
134
+ })) return {
135
+ type: "unsupported",
136
+ reason: "no root element or unterminated markup"
137
+ };
138
+ const declared = {
139
+ main: bound.main.length > 0,
140
+ w14: bound.w14.length > 0,
141
+ mc: bound.mc.length > 0
142
+ };
143
+ for (const slot of [
144
+ "main",
145
+ "w14",
146
+ "mc"
147
+ ]) {
148
+ if (declared[slot]) {
149
+ if (slot !== "main" && bound[slot].includes("")) return {
150
+ type: "unsupported",
151
+ reason: `${slot} is bound only as the default namespace`
152
+ };
153
+ continue;
154
+ }
155
+ const conventional = CONVENTIONAL_PREFIX[slot];
156
+ if (rootPrefixes.has(conventional)) {
157
+ if (slot === "main") return {
158
+ type: "unsupported",
159
+ reason: `no WordprocessingML binding; ${conventional} is bound to another namespace`
160
+ };
161
+ continue;
162
+ }
163
+ bound[slot].push(conventional);
164
+ }
165
+ const inUse = /* @__PURE__ */ new Map();
166
+ for (const slot of [
167
+ "main",
168
+ "w14",
169
+ "mc"
170
+ ]) for (const prefix of bound[slot]) inUse.set(prefix, slot);
171
+ for (const { prefix, uri } of nestedBindings === NESTED_BINDINGS.strict ? nested : []) {
172
+ const slot = slotOf(uri);
173
+ const spelling = prefix === "" ? "the default namespace" : `prefix ${prefix}`;
174
+ if (slot !== null) {
175
+ if (!bound[slot].includes(prefix)) return {
176
+ type: "unsupported",
177
+ reason: `a nested element binds ${slot} under ${spelling}`
178
+ };
179
+ continue;
180
+ }
181
+ if (inUse.has(prefix)) return {
182
+ type: "unsupported",
183
+ reason: `a nested element rebinds ${spelling} to ${uri === "" ? "no namespace" : uri}`
184
+ };
185
+ }
186
+ const only = (prefixes, prefix) => prefixes.length === 1 && prefixes[0] === prefix;
187
+ const canonical = only(bound.main, "w") && only(bound.w14, "w14") && only(bound.mc, "mc");
188
+ return {
189
+ type: "resolved",
190
+ prefixes: {
191
+ main: bound.main,
192
+ w14: bound.w14,
193
+ w14Declared: declared.w14,
194
+ mc: bound.mc,
195
+ mcDeclared: declared.mc,
196
+ canonical
197
+ }
198
+ };
199
+ };
200
+ const resolveWordprocessingPrefixes = (xml) => resolvePrefixes(xml, NESTED_BINDINGS.strict);
201
+ /** Root spelling for a selective scan whose relevant nested names are checked separately. */
202
+ const resolveSelectiveScanPrefixes = (xml) => resolvePrefixes(xml, NESTED_BINDINGS.scoped);
203
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
204
+ /**
205
+ * Whether a patcher that reads `<w:…>` literally sees every element it looks
206
+ * for in `xml`, and may splice the serializer's `w:` markup into it.
207
+ *
208
+ * True for the canonical spelling. Also true when the part binds
209
+ * WordprocessingML under an extra prefix besides `w` but never spells the
210
+ * names the patcher scans under that alias, or binds markup compatibility
211
+ * under another prefix. Relevant nested names are checked in their own scope.
212
+ */
213
+ const splicesAsCanonical = (xml, localNames) => {
214
+ const resolution = resolveWordprocessingPrefixes(xml);
215
+ if (resolution.type === "resolved" && resolution.prefixes.canonical) return true;
216
+ const relaxed = resolveSelectiveScanPrefixes(xml);
217
+ if (relaxed.type !== "resolved") return false;
218
+ const { prefixes } = relaxed;
219
+ const only = (list, prefix) => list.length === 1 && list[0] === prefix;
220
+ const aliases = prefixes.main.filter((prefix) => prefix !== "w");
221
+ if (!prefixes.main.includes("w") || aliases.includes("") || !only(prefixes.w14, "w14") || prefixes.mc.length === 0) return false;
222
+ const names = localNames.map(escapeRegExp).join("|");
223
+ if (!aliases.every((alias) => !new RegExp(`[<\\s/]${escapeRegExp(alias)}:(?:${names})[\\s/>=]`, "u").test(xml))) return false;
224
+ const root = parseXmlDocument(xml);
225
+ if (!root) return false;
226
+ const mainNames = new Set(localNames.filter((name) => name !== "paraId" && name !== "textId"));
227
+ const idNames = /* @__PURE__ */ new Set(["paraId", "textId"]);
228
+ const mcNames = /* @__PURE__ */ new Set(["Fallback", "AlternateContent"]);
229
+ const visit = (element) => {
230
+ if (element.type !== "element") return true;
231
+ const name = element.name ?? "";
232
+ const local = getLocalName(name);
233
+ const prefix = getNamespacePrefix(name) ?? "";
234
+ const uri = getNamespaceUri(element) ?? "";
235
+ if (prefix === "mc" && uri !== MC_URI || mainNames.has(local) && WORDPROCESSINGML_NAMESPACE_URIS.has(uri) !== (prefix === "w") || mcNames.has(local) && uri === MC_URI !== prefixes.mc.includes(prefix)) return false;
236
+ for (const attribute of Object.keys(element.attributes ?? {})) {
237
+ const attributePrefix = getNamespacePrefix(attribute);
238
+ if (attributePrefix === "mc" && resolveAttributeNamespaceUri(element, attribute) !== MC_URI) return false;
239
+ if (!idNames.has(getLocalName(attribute))) continue;
240
+ if (resolveAttributeNamespaceUri(element, attribute) === W14_URI !== (attributePrefix === "w14")) return false;
241
+ }
242
+ return (element.elements ?? []).every(visit);
243
+ };
244
+ return visit(root);
245
+ };
246
+ /** The names the paragraph splices scan for: paragraphs, their containers and ids. */
247
+ const PARAGRAPH_SCAN_NAMES = [
248
+ "p",
249
+ "tc",
250
+ "txbxContent",
251
+ "paraId",
252
+ "textId"
253
+ ];
254
+ /** Whether `xml` spells WordprocessingML the way folio's serializer does. */
255
+ const hasCanonicalWordprocessingPrefixes = (xml) => {
256
+ const resolution = resolveWordprocessingPrefixes(xml);
257
+ return resolution.type === "resolved" && resolution.prefixes.canonical;
258
+ };
259
+ /** `<p`, `<w:p`, … — the open-tag literal of `localName` under `prefix`. */
260
+ const openTagLiteral = (prefix, localName) => prefix === "" ? `<${localName}` : `<${prefix}:${localName}`;
261
+ /** `</p>`, `</w:p>`, … — the close-tag literal of `localName` under `prefix`. */
262
+ const closeTagLiteral = (prefix, localName) => prefix === "" ? `</${localName}>` : `</${prefix}:${localName}>`;
263
+ /**
264
+ * Whether `xml` opens `localName` under one of `prefixes` at `start`, and the
265
+ * length of the literal that matched (`-1` when none did). The character after
266
+ * the name must end it, so `<w:pPr` is not `<w:p`.
267
+ */
268
+ const matchOpenTag = (xml, start, prefixes, localName) => {
269
+ for (const prefix of prefixes) {
270
+ const literal = openTagLiteral(prefix, localName);
271
+ if (!xml.startsWith(literal, start)) continue;
272
+ const after = xml[start + literal.length];
273
+ if (after === ">" || after === "/" || isWhitespace(after)) return literal.length;
274
+ }
275
+ return -1;
276
+ };
277
+ /** The length of the close tag of `localName` under one of `prefixes` at `start`, or -1. */
278
+ const matchCloseTag = (xml, start, prefixes, localName) => {
279
+ for (const prefix of prefixes) {
280
+ const literal = closeTagLiteral(prefix, localName);
281
+ if (xml.startsWith(literal, start)) return literal.length;
282
+ }
283
+ return -1;
284
+ };
285
+ //#endregion
286
+ export { PARAGRAPH_SCAN_NAMES, closeTagLiteral, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, openTagLiteral, resolveSelectiveScanPrefixes, resolveWordprocessingPrefixes, splicesAsCanonical };
@@ -130,7 +130,7 @@ function findFontTableRels(allXml) {
130
130
  * embedded fonts. See {@link getEmbeddedFontFaces} for `docNonce`.
131
131
  */
132
132
  async function extractEmbeddedFonts(buffer, docNonce = generateHexId()) {
133
- const raw = await unzipDocx(buffer);
133
+ const raw = await unzipDocx(buffer, {}, { verifyUnreadEntries: false });
134
134
  return getEmbeddedFontFaces({
135
135
  fontTableXml: raw.fontTableXml,
136
136
  fontTableRelsXml: findFontTableRels(raw.allXml),