@stll/folio-core 0.44.0 → 0.46.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 (263) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.js +2 -2
  2. package/dist/ai-edits/apply.js +59 -19
  3. package/dist/ai-edits/headless.js +2 -1
  4. package/dist/compare/compare.js +2 -5
  5. package/dist/compare/content-alignment.js +78 -53
  6. package/dist/compare/inline-atoms.js +34 -20
  7. package/dist/compare/scenario.js +2 -5
  8. package/dist/compare/types.d.ts +9 -1
  9. package/dist/compare/types.js +12 -1
  10. package/dist/compat/eigenpal.d.ts +2 -1
  11. package/dist/compat/eigenpal.js +2 -1
  12. package/dist/content-controls/checkboxDisplay.d.ts +11 -0
  13. package/dist/content-controls/checkboxDisplay.js +40 -0
  14. package/dist/content-controls/findContentControls.js +3 -1
  15. package/dist/content-controls/mutateContentControls.js +21 -4
  16. package/dist/controller/contentControlWidgetController.d.ts +1 -0
  17. package/dist/controller/contentControlWidgetController.js +3 -0
  18. package/dist/controller/hiddenEditorApi.js +3 -1
  19. package/dist/controller/hiddenEditorManager.d.ts +1 -1
  20. package/dist/controller/hiddenEditorManager.js +6 -3
  21. package/dist/display-list/build/buildDisplayList.js +20 -1
  22. package/dist/display-list/build/imagePrimitives.d.ts +1 -1
  23. package/dist/display-list/build/imagePrimitives.js +3 -1
  24. package/dist/display-list/build/paragraphPrimitives.js +21 -2
  25. package/dist/display-list/dom/renderDisplayListToDom.js +18 -10
  26. package/dist/display-list/types.d.ts +17 -0
  27. package/dist/document-operations.js +14 -3
  28. package/dist/docx/attributeRemainder.d.ts +43 -0
  29. package/dist/docx/attributeRemainder.js +65 -0
  30. package/dist/docx/blockContentParser.d.ts +1 -1
  31. package/dist/docx/blockContentParser.js +87 -63
  32. package/dist/docx/blockPlainText.d.ts +3 -4
  33. package/dist/docx/blockPlainText.js +1 -0
  34. package/dist/docx/bookmarkPlacement.js +2 -0
  35. package/dist/docx/borderParser.js +5 -5
  36. package/dist/docx/commentAnchorIndex.d.ts +30 -0
  37. package/dist/docx/commentAnchorIndex.js +50 -0
  38. package/dist/docx/commentParser.d.ts +1 -1
  39. package/dist/docx/commentParser.js +78 -50
  40. package/dist/docx/commentRangeIntegrity.d.ts +16 -1
  41. package/dist/docx/commentRangeIntegrity.js +42 -1
  42. package/dist/docx/commentRangeJoin.d.ts +9 -0
  43. package/dist/docx/commentRangeJoin.js +33 -0
  44. package/dist/docx/commentReferenceCompletion.d.ts +9 -0
  45. package/dist/docx/commentReferenceCompletion.js +47 -0
  46. package/dist/docx/commentReferenceNormalization.js +1 -0
  47. package/dist/docx/commentReplyMarkers.js +11 -19
  48. package/dist/docx/commentThreadKey.d.ts +18 -0
  49. package/dist/docx/commentThreadKey.js +22 -0
  50. package/dist/docx/compatibility.js +1 -0
  51. package/dist/docx/containerChildren.d.ts +111 -0
  52. package/dist/docx/containerChildren.gen.d.ts +28 -0
  53. package/dist/docx/containerChildren.gen.js +246 -0
  54. package/dist/docx/containerChildren.js +103 -0
  55. package/dist/docx/diagramPreview.js +87 -27
  56. package/dist/docx/documentParser.d.ts +1 -1
  57. package/dist/docx/documentParser.js +3 -3
  58. package/dist/docx/ensureParaIds.js +17 -7
  59. package/dist/docx/fieldParser.d.ts +1 -1
  60. package/dist/docx/fieldParser.js +4 -4
  61. package/dist/docx/fieldState.d.ts +26 -0
  62. package/dist/docx/fieldState.js +53 -0
  63. package/dist/docx/footnoteParser.d.ts +1 -1
  64. package/dist/docx/graphicFrameLocks.d.ts +16 -4
  65. package/dist/docx/graphicFrameLocks.js +19 -6
  66. package/dist/docx/groupDrawingParser.js +3 -3
  67. package/dist/docx/headerFooterParser.js +1 -1
  68. package/dist/docx/headerFooterReferenceNormalization.js +1 -0
  69. package/dist/docx/hyperlinkParser.d.ts +27 -8
  70. package/dist/docx/hyperlinkParser.js +78 -20
  71. package/dist/docx/imageParser.d.ts +9 -1
  72. package/dist/docx/imageParser.js +91 -20
  73. package/dist/docx/imageRawXml.d.ts +14 -1
  74. package/dist/docx/imageRawXml.js +30 -6
  75. package/dist/docx/inlineWrapperContent.d.ts +85 -0
  76. package/dist/docx/inlineWrapperContent.js +84 -0
  77. package/dist/docx/mathToMathml.js +12 -14
  78. package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
  79. package/dist/docx/nonVisualDrawingProps.js +46 -0
  80. package/dist/docx/normalizeBaseDirection.js +10 -1
  81. package/dist/docx/paraIdAttribute.d.ts +21 -0
  82. package/dist/docx/paraIdAttribute.js +64 -0
  83. package/dist/docx/paragraphParser.d.ts +1 -1
  84. package/dist/docx/paragraphParser.js +277 -168
  85. package/dist/docx/paragraphPropertySource.js +5 -1
  86. package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
  87. package/dist/docx/paragraphTextBoxEnrichment.js +9 -52
  88. package/dist/docx/paragraphTraversal.js +7 -4
  89. package/dist/docx/parseWarningMessage.js +2 -1
  90. package/dist/docx/parser.js +2 -2
  91. package/dist/docx/preservedRunContent.d.ts +32 -0
  92. package/dist/docx/preservedRunContent.js +86 -0
  93. package/dist/docx/previewBudget.d.ts +64 -0
  94. package/dist/docx/previewBudget.js +88 -0
  95. package/dist/docx/renderedPageBreakNormalization.js +2 -4
  96. package/dist/docx/revisionIdNormalization.js +17 -5
  97. package/dist/docx/rezip.js +91 -36
  98. package/dist/docx/runParser.d.ts +1 -1
  99. package/dist/docx/runParser.js +16 -12
  100. package/dist/docx/sdtPropertiesPatch.js +24 -18
  101. package/dist/docx/sectionParser.js +8 -1
  102. package/dist/docx/sectionReferenceHistory.js +2 -2
  103. package/dist/docx/selectiveSave.js +6 -6
  104. package/dist/docx/selectiveXmlPatch.d.ts +46 -2
  105. package/dist/docx/selectiveXmlPatch.js +86 -39
  106. package/dist/docx/serializer/blockSdtSerializer.js +38 -26
  107. package/dist/docx/serializer/borderSerializer.d.ts +1 -1
  108. package/dist/docx/serializer/borderSerializer.js +15 -14
  109. package/dist/docx/serializer/commentSerializer.d.ts +41 -16
  110. package/dist/docx/serializer/commentSerializer.js +87 -92
  111. package/dist/docx/serializer/documentSerializer.js +8 -11
  112. package/dist/docx/serializer/fontTableSerializer.js +6 -6
  113. package/dist/docx/serializer/headerFooterSerializer.js +12 -14
  114. package/dist/docx/serializer/markupRangeAttributes.js +2 -2
  115. package/dist/docx/serializer/noteSerializer.js +8 -8
  116. package/dist/docx/serializer/numberingSerializer.js +7 -6
  117. package/dist/docx/serializer/paragraphSerializer.js +93 -72
  118. package/dist/docx/serializer/partNamespaces.js +2 -2
  119. package/dist/docx/serializer/runSerializer.js +73 -44
  120. package/dist/docx/serializer/sectionPropertiesSerializer.js +19 -14
  121. package/dist/docx/serializer/settingsSerializer.js +4 -3
  122. package/dist/docx/serializer/stylesSerializer.js +6 -6
  123. package/dist/docx/serializer/tableSerializer.js +28 -17
  124. package/dist/docx/serializer/textFormattingSerializer.js +29 -28
  125. package/dist/docx/serializer/themeSerializer.js +6 -6
  126. package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
  127. package/dist/docx/serializer/xmlUtils.d.ts +1 -2
  128. package/dist/docx/serializer/xmlUtils.js +1 -13
  129. package/dist/docx/server/boundedArchive.d.ts +12 -0
  130. package/dist/docx/server/boundedArchive.js +20 -1
  131. package/dist/docx/server/createBilingualDocument.js +3 -1
  132. package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
  133. package/dist/docx/server/materializeYjsDocx.js +9 -1
  134. package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
  135. package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
  136. package/dist/docx/server/validateDocxConformance.js +22 -1
  137. package/dist/docx/shapeParser.js +12 -10
  138. package/dist/docx/tableParser.d.ts +1 -1
  139. package/dist/docx/tableParser.js +206 -85
  140. package/dist/docx/textBoxParser.d.ts +25 -2
  141. package/dist/docx/textBoxParser.js +59 -4
  142. package/dist/docx/unzip.d.ts +23 -0
  143. package/dist/docx/unzip.js +37 -26
  144. package/dist/docx/verbatimCapture.js +1 -1
  145. package/dist/docx/vmlImageParser.d.ts +17 -1
  146. package/dist/docx/vmlImageParser.js +20 -3
  147. package/dist/docx/vmlPreview.d.ts +1 -3
  148. package/dist/docx/vmlPreview.js +2 -30
  149. package/dist/docx/xmlEncoding.d.ts +5 -0
  150. package/dist/docx/xmlEncoding.js +12 -0
  151. package/dist/docx/xmlParser.d.ts +26 -2
  152. package/dist/docx/xmlParser.js +70 -27
  153. package/dist/docx/xmlResourceLimits.d.ts +89 -9
  154. package/dist/docx/xmlResourceLimits.js +105 -24
  155. package/dist/headless-layout.js +10 -1
  156. package/dist/index.d.ts +2 -1
  157. package/dist/index.js +2 -1
  158. package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
  159. package/dist/internal/paragraphFormattingSerialization.js +3 -2
  160. package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
  161. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  162. package/dist/layout-engine/measure/tableCellFloating.js +2 -0
  163. package/dist/layout-engine/types.d.ts +23 -1
  164. package/dist/layout-painter/renderImage.d.ts +5 -1
  165. package/dist/layout-painter/renderImage.js +14 -6
  166. package/dist/layout-painter/renderPage.d.ts +2 -0
  167. package/dist/layout-painter/renderPage.js +4 -0
  168. package/dist/layout-painter/renderParagraph.d.ts +13 -1
  169. package/dist/layout-painter/renderParagraph.js +26 -6
  170. package/dist/managers/autoSaveCodec.d.ts +9 -1
  171. package/dist/managers/autoSaveCodec.js +14 -12
  172. package/dist/markdown/images.js +1 -4
  173. package/dist/markdown/renderBlock.js +1 -0
  174. package/dist/markdown/renderRuns.d.ts +1 -1
  175. package/dist/markdown/renderRuns.js +28 -5
  176. package/dist/markdown/renderTable.js +22 -5
  177. package/dist/markdown/trailers.js +4 -1
  178. package/dist/pdf/images.js +142 -6
  179. package/dist/prosemirror/attrs/index.d.ts +14 -3
  180. package/dist/prosemirror/attrs/index.js +224 -2
  181. package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
  182. package/dist/prosemirror/authoredTransformAttrs.js +64 -0
  183. package/dist/prosemirror/commands/contentControls.js +10 -2
  184. package/dist/prosemirror/commands/image.d.ts +9 -2
  185. package/dist/prosemirror/commands/image.js +43 -28
  186. package/dist/prosemirror/commands/pageBreak.js +1 -1
  187. package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
  188. package/dist/prosemirror/commentReferenceAttrs.js +33 -0
  189. package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
  190. package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
  191. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  192. package/dist/prosemirror/conversion/fromProseDoc.js +338 -99
  193. package/dist/prosemirror/conversion/index.d.ts +2 -2
  194. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
  195. package/dist/prosemirror/conversion/toProseDoc.js +551 -327
  196. package/dist/prosemirror/extensions/StarterKit.js +8 -58
  197. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  198. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
  199. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
  200. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
  201. package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
  202. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
  203. package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
  204. package/dist/prosemirror/extensions/markRegistry.js +75 -0
  205. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
  206. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
  207. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
  208. package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
  209. package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
  210. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
  211. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
  212. package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
  213. package/dist/prosemirror/extensions/nodes/ImageExtension.js +20 -0
  214. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
  215. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
  216. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
  217. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
  218. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +10 -2
  219. package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
  220. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +13 -5
  221. package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
  222. package/dist/prosemirror/inlineWrapperStack.js +75 -0
  223. package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
  224. package/dist/prosemirror/pageBreakRunProjection.js +19 -9
  225. package/dist/prosemirror/paragraphFormattingProvenance.d.ts +6 -3
  226. package/dist/prosemirror/paragraphFormattingProvenance.js +12 -3
  227. package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
  228. package/dist/prosemirror/replacedAnnotations.js +165 -0
  229. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
  230. package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
  231. package/dist/prosemirror/schema/index.d.ts +3 -3
  232. package/dist/prosemirror/schema/marks.d.ts +25 -1
  233. package/dist/prosemirror/schema/nodes.d.ts +139 -3
  234. package/dist/prosemirror/schema/nodes.js +16 -1
  235. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
  236. package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
  237. package/dist/prosemirror/validation.js +16 -2
  238. package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
  239. package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
  240. package/dist/prosemirror/zeroWidthAnchors.js +2 -0
  241. package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
  242. package/dist/render-dom/commentAnchorAttributes.js +54 -0
  243. package/dist/server.d.ts +3 -1
  244. package/dist/server.js +3 -1
  245. package/dist/types/content.d.ts +2 -2
  246. package/dist/utils/base64.d.ts +36 -0
  247. package/dist/utils/base64.js +40 -0
  248. package/dist/utils/clipboard.d.ts +9 -1
  249. package/dist/utils/clipboard.js +29 -2
  250. package/dist/utils/findReplace.js +1 -1
  251. package/dist/utils/imageLuminance.d.ts +15 -0
  252. package/dist/utils/imageLuminance.js +32 -0
  253. package/dist/utils/mergeDocumentContent.js +7 -17
  254. package/dist/utils/replaceText.js +1 -0
  255. package/dist/utils/units.d.ts +10 -1
  256. package/dist/utils/units.js +12 -1
  257. package/dist/utils/urlSecurity.d.ts +8 -2
  258. package/dist/utils/urlSecurity.js +21 -3
  259. package/package.json +2 -2
  260. package/dist/docx/blockRangeMarkers.d.ts +0 -36
  261. package/dist/docx/blockRangeMarkers.js +0 -59
  262. package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
  263. package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
@@ -1,27 +1,107 @@
1
1
  //#region src/docx/xmlResourceLimits.d.ts
2
- /** Shared bounds for XML parts parsed by Folio. */
2
+ /**
3
+ * Shared bounds for XML parts parsed by Folio.
4
+ *
5
+ * `maxBytes` and the package expansion ceiling bound the *markup*; they do not
6
+ * bound what parsing that markup allocates. A parsed element retains far more
7
+ * than the bytes it was written as, so a part that satisfies a byte bound can
8
+ * still cost multiples of it in tree. Measured on this repository's corpus
9
+ * generators (Bun 1.4, arm64, heap retained after a forced GC):
10
+ *
11
+ * shape bytes/element tree heap / part bytes
12
+ * `<w:r/>` 49.5 B 8.3x
13
+ * `<w:r a=".." x4/>` 105-124 B 2.8-3.3x
14
+ * `<w:r><w:t>x</w:t></w:r>` 162-171 B 14.1-14.8x
15
+ *
16
+ * An element is therefore the unit a memory bound has to count, because the
17
+ * adversarial shape is the cheap one: `<w:r/>` is 7 bytes of markup and 49.5
18
+ * bytes of tree, so 128 MiB of markup buys ~19M elements and ~950 MB of tree
19
+ * inside a byte budget that never trips.
20
+ *
21
+ * Corpus distribution (5,314 readable packages, every XML and .rels part):
22
+ *
23
+ * metric p50 p90 p99 p99.9 max
24
+ * elements / part 16 227 1,590 24,719 596,668
25
+ * elements / package 917 3,230 29,106 71,975 602,212
26
+ * attributes / part 27 577 1,993 21,037 630,374
27
+ * attributes / package 1,783 4,711 25,896 91,946 639,110
28
+ * depth / part 3 9 15 22 15,005
29
+ * elements per byte 0.012 0.027 0.036 0.043 0.111
30
+ *
31
+ * The defaults below reject no corpus package that today's bounds accept. The
32
+ * package budget is the one that matters: it caps a whole package at ~2.5M
33
+ * elements and ~3M attributes, which is at most ~425 MB of tree at the densest
34
+ * measured shape and ~124 MB at the cheapest. Before it existed, only
35
+ * `word/document.xml`, `word/styles.xml` and `word/numbering.xml` were counted
36
+ * at all, so a package of many merely-large parts could reach the expansion
37
+ * ceiling of 250 MiB, ~37M elements and well past 1.8 GB of tree while passing
38
+ * every bound. Lower these to trade format reach for a smaller ceiling.
39
+ */
3
40
  declare const FOLIO_XML_RESOURCE_LIMITS: {
4
41
  readonly maxBytes: number;
5
42
  readonly maxDepth: 100;
6
- readonly maxNodes: 1000000;
43
+ /** 1.68x the corpus maximum (596,668). Unchanged; a shipped bound is not loosened. */
44
+ readonly maxElementsPerPart: 1000000;
45
+ /** 3.97x the corpus maximum (630,374). */
46
+ readonly maxAttributesPerPart: 2500000;
47
+ /** 4.15x the corpus maximum (602,212). */
48
+ readonly maxElementsPerPackage: 2500000;
49
+ /** 4.69x the corpus maximum (639,110). */
50
+ readonly maxAttributesPerPackage: 3000000;
7
51
  };
8
52
  type XmlResourceLimits = {
9
53
  maxBytes: number;
10
54
  maxDepth: number;
11
- maxNodes: number;
55
+ maxElementsPerPart: number;
56
+ maxAttributesPerPart: number;
57
+ maxElementsPerPackage: number;
58
+ maxAttributesPerPackage: number;
12
59
  };
13
- type XmlResourceLimitKind = "bytes" | "depth" | "nodes" | "syntax";
60
+ type XmlResourceLimitKind = "bytes" | "depth" | "elements" | "attributes" | "package-elements" | "package-attributes" | "syntax";
14
61
  declare const XmlResourceLimitError_base: import("better-result").TaggedErrorClass<"XmlResourceLimitError">;
15
62
  /** XML input exceeded a parser resource bound or could not be scanned safely. */
16
63
  declare class XmlResourceLimitError extends XmlResourceLimitError_base<{
17
64
  message: string;
18
65
  limit: XmlResourceLimitKind;
66
+ /** The package part being scanned, when the caller named one. */
67
+ partPath?: string;
68
+ /** The count reached at the point of refusal, not the count of the whole input. */
69
+ observed: number;
70
+ allowed: number;
19
71
  }> {}
20
72
  /**
21
- * Bound XML bytes, element count, and nesting before building an object tree.
22
- * The lexical scan is iterative, so deeply nested input cannot consume the JS
23
- * call stack before the depth limit is enforced.
73
+ * What a package has spent so far, shared by every part in one package.
74
+ *
75
+ * Per-part bounds alone do not bound a package: a package may hold hundreds of
76
+ * parts, each individually modest. The budget is the accumulator the readers
77
+ * carry across parts so the ceiling is the package's, not each part's.
78
+ */
79
+ type XmlPackageBudget = {
80
+ elements: number;
81
+ attributes: number;
82
+ };
83
+ declare const createXmlPackageBudget: () => XmlPackageBudget;
84
+ type XmlResourceScanOptions = {
85
+ xml: string;
86
+ limits?: XmlResourceLimits;
87
+ /** The package path scanned, carried on any refusal so a host can name it. */
88
+ partPath?: string;
89
+ /** Charged as the scan proceeds; omit for a part with no package context. */
90
+ budget?: XmlPackageBudget;
91
+ };
92
+ /** What the preflight counted, for callers that reconcile it against the tree. */
93
+ type XmlResourceScanResult = {
94
+ elements: number;
95
+ attributes: number;
96
+ maxDepth: number;
97
+ };
98
+ /**
99
+ * Bound XML bytes, element count, attribute count and nesting before building
100
+ * an object tree. The lexical scan is iterative, so deeply nested input cannot
101
+ * consume the JS call stack before the depth limit is enforced, and every
102
+ * bound is checked at the point it is crossed, so the work done before a
103
+ * refusal is proportional to the limit rather than to the input.
24
104
  */
25
- declare const assertXmlResourceLimits: (xml: string, limits?: XmlResourceLimits) => void;
105
+ declare const assertXmlResourceLimits: ({ xml, limits, partPath, budget }: XmlResourceScanOptions) => XmlResourceScanResult;
26
106
  //#endregion
27
- export { FOLIO_XML_RESOURCE_LIMITS, XmlResourceLimitError, assertXmlResourceLimits };
107
+ export { FOLIO_XML_RESOURCE_LIMITS, XmlPackageBudget, XmlResourceLimitError, XmlResourceLimits, XmlResourceScanOptions, XmlResourceScanResult, assertXmlResourceLimits, createXmlPackageBudget };
@@ -1,12 +1,60 @@
1
1
  import { TaggedError } from "better-result";
2
- /** Shared bounds for XML parts parsed by Folio. */
2
+ /**
3
+ * Shared bounds for XML parts parsed by Folio.
4
+ *
5
+ * `maxBytes` and the package expansion ceiling bound the *markup*; they do not
6
+ * bound what parsing that markup allocates. A parsed element retains far more
7
+ * than the bytes it was written as, so a part that satisfies a byte bound can
8
+ * still cost multiples of it in tree. Measured on this repository's corpus
9
+ * generators (Bun 1.4, arm64, heap retained after a forced GC):
10
+ *
11
+ * shape bytes/element tree heap / part bytes
12
+ * `<w:r/>` 49.5 B 8.3x
13
+ * `<w:r a=".." x4/>` 105-124 B 2.8-3.3x
14
+ * `<w:r><w:t>x</w:t></w:r>` 162-171 B 14.1-14.8x
15
+ *
16
+ * An element is therefore the unit a memory bound has to count, because the
17
+ * adversarial shape is the cheap one: `<w:r/>` is 7 bytes of markup and 49.5
18
+ * bytes of tree, so 128 MiB of markup buys ~19M elements and ~950 MB of tree
19
+ * inside a byte budget that never trips.
20
+ *
21
+ * Corpus distribution (5,314 readable packages, every XML and .rels part):
22
+ *
23
+ * metric p50 p90 p99 p99.9 max
24
+ * elements / part 16 227 1,590 24,719 596,668
25
+ * elements / package 917 3,230 29,106 71,975 602,212
26
+ * attributes / part 27 577 1,993 21,037 630,374
27
+ * attributes / package 1,783 4,711 25,896 91,946 639,110
28
+ * depth / part 3 9 15 22 15,005
29
+ * elements per byte 0.012 0.027 0.036 0.043 0.111
30
+ *
31
+ * The defaults below reject no corpus package that today's bounds accept. The
32
+ * package budget is the one that matters: it caps a whole package at ~2.5M
33
+ * elements and ~3M attributes, which is at most ~425 MB of tree at the densest
34
+ * measured shape and ~124 MB at the cheapest. Before it existed, only
35
+ * `word/document.xml`, `word/styles.xml` and `word/numbering.xml` were counted
36
+ * at all, so a package of many merely-large parts could reach the expansion
37
+ * ceiling of 250 MiB, ~37M elements and well past 1.8 GB of tree while passing
38
+ * every bound. Lower these to trade format reach for a smaller ceiling.
39
+ */
3
40
  const FOLIO_XML_RESOURCE_LIMITS = {
4
41
  maxBytes: 128 * (1024 * 1024),
5
42
  maxDepth: 100,
6
- maxNodes: 1e6
43
+ /** 1.68x the corpus maximum (596,668). Unchanged; a shipped bound is not loosened. */
44
+ maxElementsPerPart: 1e6,
45
+ /** 3.97x the corpus maximum (630,374). */
46
+ maxAttributesPerPart: 25e5,
47
+ /** 4.15x the corpus maximum (602,212). */
48
+ maxElementsPerPackage: 25e5,
49
+ /** 4.69x the corpus maximum (639,110). */
50
+ maxAttributesPerPackage: 3e6
7
51
  };
8
52
  /** XML input exceeded a parser resource bound or could not be scanned safely. */
9
53
  var XmlResourceLimitError = class extends TaggedError("XmlResourceLimitError") {};
54
+ const createXmlPackageBudget = () => ({
55
+ elements: 0,
56
+ attributes: 0
57
+ });
10
58
  const exceedsUtf8ByteLimit = (value, maxBytes) => {
11
59
  let bytes = 0;
12
60
  for (let index = 0; index < value.length; index += 1) {
@@ -22,8 +70,9 @@ const exceedsUtf8ByteLimit = (value, maxBytes) => {
22
70
  return false;
23
71
  };
24
72
  const isXmlWhitespace = (code) => code === 9 || code === 10 || code === 13 || code === 32;
25
- const findTagClose = (xml, start) => {
73
+ const scanTag = (xml, start) => {
26
74
  let quote = 0;
75
+ let attributes = 0;
27
76
  for (let cursor = start; cursor < xml.length; cursor += 1) {
28
77
  const code = xml.charCodeAt(cursor);
29
78
  if (quote !== 0) {
@@ -34,29 +83,53 @@ const findTagClose = (xml, start) => {
34
83
  quote = code;
35
84
  continue;
36
85
  }
37
- if (code === 62) return cursor;
86
+ if (code === 61) {
87
+ attributes += 1;
88
+ continue;
89
+ }
90
+ if (code === 62) return {
91
+ close: cursor,
92
+ attributes
93
+ };
38
94
  }
39
- return -1;
95
+ return {
96
+ close: -1,
97
+ attributes
98
+ };
40
99
  };
41
100
  const throwSyntaxLimit = () => {
42
101
  throw new XmlResourceLimitError({
43
102
  message: "XML resource preflight could not safely scan malformed markup",
44
- limit: "syntax"
103
+ limit: "syntax",
104
+ observed: 0,
105
+ allowed: 0
45
106
  });
46
107
  };
47
108
  /**
48
- * Bound XML bytes, element count, and nesting before building an object tree.
49
- * The lexical scan is iterative, so deeply nested input cannot consume the JS
50
- * call stack before the depth limit is enforced.
109
+ * Bound XML bytes, element count, attribute count and nesting before building
110
+ * an object tree. The lexical scan is iterative, so deeply nested input cannot
111
+ * consume the JS call stack before the depth limit is enforced, and every
112
+ * bound is checked at the point it is crossed, so the work done before a
113
+ * refusal is proportional to the limit rather than to the input.
51
114
  */
52
- const assertXmlResourceLimits = (xml, limits = FOLIO_XML_RESOURCE_LIMITS) => {
53
- if (exceedsUtf8ByteLimit(xml, limits.maxBytes)) throw new XmlResourceLimitError({
54
- message: `XML part exceeds ${String(limits.maxBytes)} bytes`,
55
- limit: "bytes"
56
- });
115
+ const assertXmlResourceLimits = ({ xml, limits = FOLIO_XML_RESOURCE_LIMITS, partPath, budget }) => {
116
+ const refuse = (limit, message, observed, allowed) => {
117
+ throw new XmlResourceLimitError({
118
+ message: partPath === void 0 ? message : `${message} (part ${partPath})`,
119
+ limit,
120
+ ...partPath === void 0 ? {} : { partPath },
121
+ observed,
122
+ allowed
123
+ });
124
+ };
125
+ if (exceedsUtf8ByteLimit(xml, limits.maxBytes)) refuse("bytes", `XML part exceeds ${String(limits.maxBytes)} bytes`, limits.maxBytes + 1, limits.maxBytes);
126
+ const budgetElements = budget?.elements ?? 0;
127
+ const budgetAttributes = budget?.attributes ?? 0;
57
128
  let cursor = 0;
58
129
  let depth = 0;
130
+ let maxDepth = 0;
59
131
  let nodes = 0;
132
+ let attributes = 0;
60
133
  while (cursor < xml.length) {
61
134
  const open = xml.indexOf("<", cursor);
62
135
  if (open === -1) break;
@@ -79,7 +152,7 @@ const assertXmlResourceLimits = (xml, limits = FOLIO_XML_RESOURCE_LIMITS) => {
79
152
  continue;
80
153
  }
81
154
  if (xml.startsWith("<!", open)) throwSyntaxLimit();
82
- const close = findTagClose(xml, open + 1);
155
+ const { close, attributes: tagAttributes } = scanTag(xml, open + 1);
83
156
  if (close === -1) throwSyntaxLimit();
84
157
  if (xml.charCodeAt(open + 1) === 47) {
85
158
  if (depth === 0) throwSyntaxLimit();
@@ -88,21 +161,29 @@ const assertXmlResourceLimits = (xml, limits = FOLIO_XML_RESOURCE_LIMITS) => {
88
161
  continue;
89
162
  }
90
163
  nodes += 1;
91
- if (nodes > limits.maxNodes) throw new XmlResourceLimitError({
92
- message: `XML part contains more than ${String(limits.maxNodes)} elements`,
93
- limit: "nodes"
94
- });
164
+ if (nodes > limits.maxElementsPerPart) refuse("elements", `XML part contains more than ${String(limits.maxElementsPerPart)} elements`, nodes, limits.maxElementsPerPart);
165
+ if (budgetElements + nodes > limits.maxElementsPerPackage) refuse("package-elements", `DOCX package contains more than ${String(limits.maxElementsPerPackage)} XML elements`, budgetElements + nodes, limits.maxElementsPerPackage);
166
+ attributes += tagAttributes;
167
+ if (attributes > limits.maxAttributesPerPart) refuse("attributes", `XML part contains more than ${String(limits.maxAttributesPerPart)} attributes`, attributes, limits.maxAttributesPerPart);
168
+ if (budgetAttributes + attributes > limits.maxAttributesPerPackage) refuse("package-attributes", `DOCX package contains more than ${String(limits.maxAttributesPerPackage)} XML attributes`, budgetAttributes + attributes, limits.maxAttributesPerPackage);
95
169
  let lastContent = close - 1;
96
170
  while (lastContent > open && isXmlWhitespace(xml.charCodeAt(lastContent))) lastContent -= 1;
97
171
  const elementDepth = depth + 1;
98
- if (elementDepth > limits.maxDepth) throw new XmlResourceLimitError({
99
- message: `XML part is nested deeper than ${String(limits.maxDepth)} elements`,
100
- limit: "depth"
101
- });
172
+ if (elementDepth > limits.maxDepth) refuse("depth", `XML part is nested deeper than ${String(limits.maxDepth)} elements`, elementDepth, limits.maxDepth);
173
+ if (elementDepth > maxDepth) maxDepth = elementDepth;
102
174
  if (xml.charCodeAt(lastContent) !== 47) depth = elementDepth;
103
175
  cursor = close + 1;
104
176
  }
105
177
  if (depth !== 0) throwSyntaxLimit();
178
+ if (budget) {
179
+ budget.elements += nodes;
180
+ budget.attributes += attributes;
181
+ }
182
+ return {
183
+ elements: nodes,
184
+ attributes,
185
+ maxDepth
186
+ };
106
187
  };
107
188
  //#endregion
108
- export { FOLIO_XML_RESOURCE_LIMITS, XmlResourceLimitError, assertXmlResourceLimits };
189
+ export { FOLIO_XML_RESOURCE_LIMITS, XmlResourceLimitError, assertXmlResourceLimits, createXmlPackageBudget };
@@ -1,5 +1,7 @@
1
1
  import { resolveDocumentGridLinePitch } from "./docx/documentGrid.js";
2
2
  import { formatOoxmlCounter } from "./docx/ooxmlCounterFormatter.js";
3
+ import { createParseWarningCollector } from "./docx/parseContext.js";
4
+ import { formatParseWarnings } from "./docx/parseWarningMessage.js";
3
5
  import { parseDocx } from "./docx/parser.js";
4
6
  import { buildHeaderFooterFieldValues } from "./fields/resolveFieldValues.js";
5
7
  import { extractEmbeddedFonts } from "./fonts/embeddedFonts.js";
@@ -209,14 +211,21 @@ const layoutDocxHeadless = async (input, options = {}) => {
209
211
  });
210
212
  if (parsed.isErr()) return Result.err(parsed.error);
211
213
  const document = parsed.value;
214
+ const { context: projectionContext, warnings: projectionWarnings } = createParseWarningCollector("word/document.xml");
212
215
  const projected = Result.try(() => toProseDoc(document, {
213
216
  ...document.package.styles ? { styles: document.package.styles } : {},
214
- ...document.package.theme ? { theme: document.package.theme } : {}
217
+ ...document.package.theme ? { theme: document.package.theme } : {},
218
+ warn: projectionContext.warn
215
219
  }));
216
220
  if (projected.isErr()) return Result.err(new HeadlessLayoutError({
217
221
  message: "The document could not be projected to the editable model.",
218
222
  cause: projected.error
219
223
  }));
224
+ const recorded = projectionWarnings();
225
+ if (recorded.length > 0) {
226
+ document.parseWarnings = [...document.parseWarnings ?? [], ...recorded];
227
+ document.warnings = [...document.warnings ?? [], ...formatParseWarnings(recorded)];
228
+ }
220
229
  if (packageNeedsShaping(document.package)) {
221
230
  const loaded = await Result.tryPromise({
222
231
  try: () => getShaper(),
package/dist/index.d.ts CHANGED
@@ -38,7 +38,8 @@ import { fromMarkdown } from "./markdown/fromMarkdown.js";
38
38
  import { toMarkdown, toMarkdownResult } from "./markdown/index.js";
39
39
  import { EmbeddedFont, EmbeddedFontParts, buildEmbeddedFontFamilyMap, extractEmbeddedFonts, getEmbeddedFontFaces, scopeEmbeddedFontFamily } from "./fonts/embeddedFonts.js";
40
40
  import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "./utils/fontResolver.js";
41
+ import { FOLIO_YJS_ATTR_SCHEMA_VERSION, FolioYjsAttrSchemaVersion, FolioYjsAttrSchemaVersionError } from "./prosemirror/yjsDocumentMetadata.js";
41
42
  import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
42
43
  type Document = document_d_exports.Document;
43
44
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
44
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareCompatibility, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFolioRequirement, type CompareResult, type CompareRevisionFormat, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentPropertiesOptions, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type DocxDrawingClassification, type DrawingSafetyClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineComparisonResult, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, FolioContentInlinePresentationProjectionError, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewHiddenRange, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
45
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareCompatibility, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFolioRequirement, type CompareResult, type CompareRevisionFormat, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentPropertiesOptions, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type DocxDrawingClassification, type DrawingSafetyClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineComparisonResult, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, FolioContentInlinePresentationProjectionError, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type FolioYjsAttrSchemaVersion, FolioYjsAttrSchemaVersionError, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewHiddenRange, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ import { DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acc
25
25
  import { getTemplateDirectives, scanDirectives } from "./prosemirror/plugins/templateDirectives.js";
26
26
  import { setTemplatePreviewValues } from "./prosemirror/plugins/templatePreviewValues.js";
27
27
  import { clearTemplateSlashMenu, consumeTemplateSlashQuery, getTemplateSlashMenu, resetTemplateSlashQuery, templateSlashMenuKey } from "./prosemirror/plugins/templateSlashMenu.js";
28
+ import { FOLIO_YJS_ATTR_SCHEMA_VERSION, FolioYjsAttrSchemaVersionError } from "./prosemirror/yjsDocumentMetadata.js";
28
29
  import { extractDocumentStyleSet, extractDocumentStyleSetFromDocx, inspectDocumentStyles, inspectDocumentStylesFromDocx } from "./style-sets/extract.js";
29
30
  import { STELLA_STYLE_SET_NAME, createStellaStyleDocumentPreset, createStellaStyleSet } from "./style-sets/stellaStyle.js";
30
31
  import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION } from "./style-sets/types.js";
@@ -33,4 +34,4 @@ import { createEmptyDocument } from "./utils/createDocument.js";
33
34
  import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "./utils/fontResolver.js";
34
35
  import { mergeDocumentContent } from "./utils/mergeDocumentContent.js";
35
36
  import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
36
- export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioContentComparisonLimitError, FolioContentInlinePresentationProjectionError, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
37
+ export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, FolioContentComparisonLimitError, FolioContentInlinePresentationProjectionError, FolioYjsAttrSchemaVersionError, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -37,6 +37,7 @@ const buildPageBreakRunSourceDescendantIndex = (rootContent) => {
37
37
  case "footnoteRef":
38
38
  case "instrText":
39
39
  case "noBreakHyphen":
40
+ case "preservedXml":
40
41
  case "renderedPageBreak":
41
42
  case "softHyphen":
42
43
  case "symbol":
@@ -60,7 +61,7 @@ const buildPageBreakRunSourceDescendantIndex = (rootContent) => {
60
61
  case "moveFrom":
61
62
  case "moveTo":
62
63
  case "inlineSdt":
63
- case "bidiWrapper": return inspectMany(content.content, inspectParagraphContent);
64
+ case "inlineWrapper": return inspectMany(content.content, inspectParagraphContent);
64
65
  case "simpleField": {
65
66
  const fieldResultFlags = inspectMany(content.content, inspectParagraphContent);
66
67
  if (hasFlag(fieldResultFlags, SOURCE_FLAGS.paragraphPageBreak) && hasFlag(fieldResultFlags, SOURCE_FLAGS.paragraphTextBoxShape)) return fieldResultFlags | SOURCE_FLAGS.pageBreakSharesTextBoxShape;
@@ -82,7 +83,8 @@ const buildPageBreakRunSourceDescendantIndex = (rootContent) => {
82
83
  case "moveFromRangeEnd":
83
84
  case "moveToRangeStart":
84
85
  case "moveToRangeEnd":
85
- case "mathEquation": return 0;
86
+ case "mathEquation":
87
+ case "preservedInline": return 0;
86
88
  default: return content;
87
89
  }
88
90
  });
@@ -94,6 +96,7 @@ const buildPageBreakRunSourceDescendantIndex = (rootContent) => {
94
96
  case "paragraph": return inspectMany(block.content, inspectParagraphContent);
95
97
  case "table": return inspectMany(block.rows, inspectTableRow);
96
98
  case "blockSdt": return inspectBlocks(block.content);
99
+ case "preservedBlock": return 0;
97
100
  default: return block;
98
101
  }
99
102
  });
@@ -1,8 +1,9 @@
1
1
  import { serializeBorder } from "../docx/serializer/borderSerializer.js";
2
2
  import { serializeShading, serializeTextFormatting } from "../docx/serializer/textFormattingSerializer.js";
3
- import { escapeXml, intAttr } from "../docx/serializer/xmlUtils.js";
3
+ import { intAttr } from "../docx/serializer/xmlUtils.js";
4
4
  import { sanitizeCapturedXmlElement } from "../docx/verbatimCapture.js";
5
5
  import { NAMESPACES, OOXML_NAMESPACE_SCOPE } from "../docx/xmlParser.js";
6
+ import { escapeXmlAttribute } from "@stll/docx-core";
6
7
  //#region src/internal/paragraphFormattingSerialization.ts
7
8
  const modelParagraphNumberingReference = (reference) => {
8
9
  if (!reference) return null;
@@ -140,7 +141,7 @@ const modelParagraphFormattingEmission = (input) => {
140
141
  const { alignment, bidi, kinsoku, overflowPunctuation, spaceBefore, spaceAfter, lineSpacing, lineSpacingRule, snapToGrid, beforeAutospacing, afterAutospacing, spacingExplicit, indentLeft, indentRight, indentFirstLine, hangingIndent, borders, shading, tabs, keepNext, keepLines, widowControl, pageBreakBefore, contextualSpacing, numPr, numPrFromStyle, numberingChangeXml, outlineLevel, styleId, frame, suppressLineNumbers, suppressAutoHyphens, runProperties, runInWithNext } = input;
141
142
  modelSpacingProvenance(spacingExplicit);
142
143
  const propertiesXml = [
143
- styleId ? `<w:pStyle w:val="${escapeXml(styleId)}"/>` : "",
144
+ styleId ? `<w:pStyle w:val="${escapeXmlAttribute(styleId)}"/>` : "",
144
145
  serializeToggle("keepNext", keepNext),
145
146
  serializeToggle("keepLines", keepLines),
146
147
  serializeToggle("pageBreakBefore", pageBreakBefore),
@@ -8,12 +8,11 @@ import { setParagraphFrame } from "../../layout-engine/paragraphFrame.js";
8
8
  import { setTextBoxGroupId } from "../../layout-engine/textBoxGroup.js";
9
9
  import { DEFAULT_TEXTBOX_MARGINS } from "../../layout-engine/types.js";
10
10
  import { getPageNumbering } from "../../paged-layout/sectionGeometry.js";
11
- import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectSymbolAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../../prosemirror/attrs/index.js";
11
+ import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectInlineWrapperMarkAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectPreservedXmlAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectSymbolAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../../prosemirror/attrs/index.js";
12
12
  import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
13
13
  import { runShadingAttrsToShading } from "../../prosemirror/conversion/runShadingMark.js";
14
14
  import { advanceListMarker, advanceVisibleListMarker, cloneListCounterState, formatCounter, resolveListTemplate } from "../../prosemirror/listMarker.js";
15
15
  import { resolveNumberedRefFields } from "../../prosemirror/numberedRefFields.js";
16
- import { pageBreakRunParagraphProjectionDisposition } from "../../prosemirror/pageBreakRunProjection.js";
17
16
  import { directionToBidi } from "../../prosemirror/paragraphDirection.js";
18
17
  import { resolveEffectiveRunStyleFormatting } from "../../prosemirror/runStyleFormatting.js";
19
18
  import { expectTextBoxAnchorAttrs } from "../../prosemirror/textBoxAnchorAttrs.js";
@@ -102,6 +101,74 @@ function resetBlockIdCounter() {
102
101
  blockIdCounter = 0;
103
102
  }
104
103
  /**
104
+ * What the painter does with each mark the schema declares.
105
+ *
106
+ * The switch below used to end in `default: break`, so a mark that reached it
107
+ * was silently not painted whether that was the decision or an omission. The
108
+ * table is total over {@link SchemaMarkName}: a mark added to the schema
109
+ * without a paint decision does not compile, and a mark this table says is
110
+ * painted but the switch does not handle panics rather than disappearing.
111
+ */
112
+ const MARK_PAINT_DISPOSITIONS = {
113
+ bold: "runFormatting",
114
+ italic: "runFormatting",
115
+ underline: "runFormatting",
116
+ strike: "runFormatting",
117
+ textColor: "runFormatting",
118
+ runShading: "runFormatting",
119
+ highlight: "runFormatting",
120
+ fontSize: "runFormatting",
121
+ fontFamily: "runFormatting",
122
+ language: "runFormatting",
123
+ superscript: "runFormatting",
124
+ subscript: "runFormatting",
125
+ hyperlink: "runFormatting",
126
+ allCaps: "runFormatting",
127
+ smallCaps: "runFormatting",
128
+ footnoteRef: "runFormatting",
129
+ characterSpacing: "runFormatting",
130
+ emboss: "runFormatting",
131
+ imprint: "runFormatting",
132
+ hidden: "runFormatting",
133
+ textShadow: "runFormatting",
134
+ emphasisMark: "runFormatting",
135
+ textOutline: "runFormatting",
136
+ rtl: "runFormatting",
137
+ textEffect: "runFormatting",
138
+ runFormattingOverride: "runFormatting",
139
+ comment: "runFormatting",
140
+ insertion: "runFormatting",
141
+ deletion: "runFormatting",
142
+ inlineWrapper: "runFormatting",
143
+ characterStyle: "resolvedElsewhere",
144
+ pageBreakRunOwner: "notPainted",
145
+ runPropertyChange: "notPainted"
146
+ };
147
+ const markPaintDisposition = (name) => {
148
+ return MARK_PAINT_DISPOSITIONS[name];
149
+ };
150
+ /**
151
+ * The direction one bidirectional wrapper lays its content out in, as the
152
+ * innermost layer of a stack states it.
153
+ *
154
+ * Only the innermost is read: a wrapper inside another is the one whose
155
+ * direction the text is laid out in, and the outer layers are what the save
156
+ * leg rebuilds the nesting from.
157
+ */
158
+ const innermostBidiWrapper = (stack) => {
159
+ const layer = stack.at(-1);
160
+ if (layer === void 0) return;
161
+ switch (layer.kind) {
162
+ case "bidi": return layer.direction === void 0 ? { control: layer.control } : {
163
+ control: layer.control,
164
+ direction: layer.direction
165
+ };
166
+ default:
167
+ layer.kind;
168
+ return;
169
+ }
170
+ };
171
+ /**
105
172
  * Extract run formatting from ProseMirror marks.
106
173
  */
107
174
  function extractRunFormatting(marks, theme, fontAlternates) {
@@ -282,7 +349,14 @@ function extractRunFormatting(marks, theme, fontAlternates) {
282
349
  }
283
350
  break;
284
351
  }
285
- default: break;
352
+ case "inlineWrapper": {
353
+ const bidiWrapper = innermostBidiWrapper(expectInlineWrapperMarkAttrs(mark).stack);
354
+ if (bidiWrapper) formatting.bidiWrapper = bidiWrapper;
355
+ break;
356
+ }
357
+ default:
358
+ if (markPaintDisposition(mark.type.name) === "runFormatting") panic(`Mark ${mark.type.name} is painted as run formatting but has no branch here`);
359
+ break;
286
360
  }
287
361
  if (runFormattingOverride) applyRunFormattingOverrides(formatting, runFormattingOverride);
288
362
  if (hasNoteRef && formatting.subscript) delete formatting.superscript;
@@ -530,6 +604,8 @@ function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange) {
530
604
  if (attrs.alt !== void 0) run.alt = attrs.alt;
531
605
  if (attrs.transform !== void 0) run.transform = attrs.transform;
532
606
  if (attrs.opacity != null) run.opacity = attrs.opacity;
607
+ if (attrs.brightness != null) run.brightness = attrs.brightness;
608
+ if (attrs.contrast != null) run.contrast = attrs.contrast;
533
609
  if (attrs.wrapType !== void 0) run.wrapType = attrs.wrapType;
534
610
  if (attrs.displayMode !== void 0) run.displayMode = attrs.displayMode;
535
611
  if (attrs.cssFloat !== void 0) run.cssFloat = attrs.cssFloat;
@@ -684,6 +760,25 @@ function paragraphToRuns(node, startPos, _options, pageBreaks) {
684
760
  });
685
761
  return;
686
762
  }
763
+ if (child.type.name === "preservedXml") {
764
+ const { text } = expectPreservedXmlAttrs(child);
765
+ if (text === "") return;
766
+ const formatting = extractRunFormatting(effectiveMarks, theme, fontAlternates);
767
+ applyCharacterStyleToggleFormatting({
768
+ formatting,
769
+ marks: effectiveMarks,
770
+ paragraphFormatting: pmAttrs.defaultTextFormatting,
771
+ styleResolver: _options.styleResolver
772
+ });
773
+ runs.push({
774
+ kind: "text",
775
+ text,
776
+ ...mergeRunFormatting(paraDefaults, formatting),
777
+ pmStart: childPos,
778
+ pmEnd: childPos + child.nodeSize
779
+ });
780
+ return;
781
+ }
687
782
  if (child.type.name === "hardBreak") {
688
783
  runs.push({
689
784
  kind: "lineBreak",
@@ -1357,8 +1452,7 @@ function convertTableCell(node, startPos, options, tableCellMargins) {
1357
1452
  const blocks = [];
1358
1453
  let offset = startPos + 1;
1359
1454
  const authoredPageBreakPosition = options.firstPageBreakRunPosition(node);
1360
- const leadingParagraph = node.firstChild?.type.name === "paragraph" ? node.firstChild : void 0;
1361
- if (authoredPageBreakPosition !== void 0 && (leadingParagraph === void 0 || countPageBreakRuns(node) !== 1 || options.firstPageBreakRunPosition(leadingParagraph) !== authoredPageBreakPosition)) panic(`An explicit page-break run at ${String(authoredPageBreakPosition)} cannot be projected inside a table cell`);
1455
+ const leadingParagraph = node.firstChild?.type.name === "paragraph" && countPageBreakRuns(node) === 1 && options.firstPageBreakRunPosition(node.firstChild) === authoredPageBreakPosition ? node.firstChild : void 0;
1362
1456
  const pageBreaks = [];
1363
1457
  node.forEach((child) => {
1364
1458
  if (child.type.name === "paragraph") {
@@ -1406,7 +1500,7 @@ function convertTableCell(node, startPos, options, tableCellMargins) {
1406
1500
  if (attrs.noWrap) cell.noWrap = true;
1407
1501
  if (authoredPageBreakPosition === void 0 || pageBreaks.length === 0) return { cell };
1408
1502
  const paragraph = blocks.at(0);
1409
- if (paragraph?.kind !== "paragraph" || !hasSingleLeadingProjectedPageBreak(paragraph.runs, pageBreaks)) panic(`An explicit page-break run at ${String(authoredPageBreakPosition)} cannot be projected inside a table cell`);
1503
+ if (paragraph?.kind !== "paragraph" || !hasSingleLeadingProjectedPageBreak(paragraph.runs, pageBreaks)) return { cell };
1410
1504
  return {
1411
1505
  cell,
1412
1506
  breakBefore: "page"
@@ -1523,6 +1617,8 @@ function convertImage(node, startPos, pageContentHeight) {
1523
1617
  if (attrs.alt) imgBlock.alt = attrs.alt;
1524
1618
  if (attrs.transform) imgBlock.transform = attrs.transform;
1525
1619
  if (attrs.opacity != null) imgBlock.opacity = attrs.opacity;
1620
+ if (attrs.brightness != null) imgBlock.brightness = attrs.brightness;
1621
+ if (attrs.contrast != null) imgBlock.contrast = attrs.contrast;
1526
1622
  if (shouldAnchor) {
1527
1623
  const anchor = {
1528
1624
  isAnchored: true,
@@ -1544,8 +1640,6 @@ function convertImage(node, startPos, pageContentHeight) {
1544
1640
  }
1545
1641
  /** Convert a textBox PM node to a TextBoxBlock. */
1546
1642
  function convertTextBoxNode(node, startPos, opts) {
1547
- const pageBreakPosition = opts.firstPageBreakRunPosition(node);
1548
- if (pageBreakPosition !== void 0) panic(`An explicit page-break run at ${String(pageBreakPosition)} cannot be projected inside a text box`);
1549
1643
  const attrs = expectTextBoxAttrs(node);
1550
1644
  const contentBlocks = [];
1551
1645
  node.forEach((child, offset) => {
@@ -1746,10 +1840,6 @@ function toFlowBlocks(doc, options = {}) {
1746
1840
  const pushParagraphProjection = (node, pos, stripLeadingLineBreak = false) => {
1747
1841
  const pageBreaks = [];
1748
1842
  const paragraph = convertParagraph(node, pos, opts, pageBreaks);
1749
- if (pageBreaks.length > 0) {
1750
- const disposition = pageBreakRunParagraphProjectionDisposition(node);
1751
- if (disposition.status === "unsupported" && (disposition.reason === "textBoxAnchor" || !hasSingleLeadingProjectedPageBreak(paragraph.runs, pageBreaks))) panic(disposition.message);
1752
- }
1753
1843
  if (stripLeadingLineBreak && paragraph.runs.at(0)?.kind === "lineBreak") paragraph.runs.shift();
1754
1844
  for (const block of splitParagraphAtPageBreaks({
1755
1845
  pageBreaks,