@stll/folio-core 0.54.2 → 0.56.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 (267) hide show
  1. package/dist/ai-edits/apply.js +185 -33
  2. package/dist/ai-edits/headless.js +3 -2
  3. package/dist/ai-edits/types.d.ts +7 -1
  4. package/dist/compare/compare.js +1 -0
  5. package/dist/compare/content-types.d.ts +3 -15
  6. package/dist/compare/reproducible-package.d.ts +0 -17
  7. package/dist/compare/reproducible-package.js +12 -18
  8. package/dist/controller/canonicalClipboard.d.ts +23 -0
  9. package/dist/controller/canonicalClipboard.js +476 -0
  10. package/dist/controller/canonicalClipboardResources.d.ts +21 -0
  11. package/dist/controller/canonicalClipboardResources.js +382 -0
  12. package/dist/controller/canonicalComposition.d.ts +53 -0
  13. package/dist/controller/canonicalComposition.js +296 -0
  14. package/dist/controller/canonicalInlineProjection.d.ts +113 -0
  15. package/dist/controller/canonicalInlineProjection.js +393 -0
  16. package/dist/controller/canonicalInput.d.ts +54 -0
  17. package/dist/controller/canonicalInput.js +377 -0
  18. package/dist/controller/canonicalInputTimer.d.ts +7 -0
  19. package/dist/controller/canonicalInputTimer.js +11 -0
  20. package/dist/controller/canonicalOperations.d.ts +22 -0
  21. package/dist/controller/canonicalOperations.js +85 -0
  22. package/dist/controller/canonicalPublicOperations.d.ts +48 -0
  23. package/dist/controller/canonicalPublicOperations.js +556 -0
  24. package/dist/controller/canonicalReview.d.ts +12 -0
  25. package/dist/controller/canonicalReview.js +19 -0
  26. package/dist/controller/canonicalSession.d.ts +167 -0
  27. package/dist/controller/canonicalSession.js +1186 -0
  28. package/dist/controller/canonicalStoryEditor.d.ts +36 -0
  29. package/dist/controller/canonicalStoryEditor.js +87 -0
  30. package/dist/controller/canonicalStructure.d.ts +16 -0
  31. package/dist/controller/canonicalStructure.js +322 -0
  32. package/dist/controller/canonicalTocSelection.d.ts +11 -0
  33. package/dist/controller/canonicalTocSelection.js +43 -0
  34. package/dist/controller/folioEditor.d.ts +3 -10
  35. package/dist/controller/folioEditor.js +14 -4
  36. package/dist/controller/headerFooterEditorManager.d.ts +5 -0
  37. package/dist/controller/headerFooterEditorManager.js +72 -3
  38. package/dist/controller/hiddenEditorApi.d.ts +45 -0
  39. package/dist/controller/hiddenEditorApi.js +29 -8
  40. package/dist/controller/hiddenEditorManager.d.ts +13 -2
  41. package/dist/controller/hiddenEditorManager.js +639 -31
  42. package/dist/controller/layoutPipeline.d.ts +1 -1
  43. package/dist/controller/layoutPipeline.js +2 -0
  44. package/dist/controller/noteEditorManager.d.ts +5 -0
  45. package/dist/controller/noteEditorManager.js +114 -15
  46. package/dist/document-operations.d.ts +8 -1
  47. package/dist/document-operations.js +6 -4
  48. package/dist/docx/attributeRemainder.js +1 -0
  49. package/dist/docx/blockPlainText.d.ts +1 -1
  50. package/dist/docx/blockPlainText.js +11 -3
  51. package/dist/docx/canonicalResourceSave.d.ts +16 -0
  52. package/dist/docx/canonicalResourceSave.js +27 -0
  53. package/dist/docx/canonicalSave.d.ts +29 -0
  54. package/dist/docx/canonicalSave.js +101 -0
  55. package/dist/docx/canonicalSessionInput.d.ts +11 -0
  56. package/dist/docx/canonicalSessionInput.js +27 -0
  57. package/dist/docx/canonicalStoryRepack.d.ts +10 -0
  58. package/dist/docx/canonicalStoryRepack.js +74 -0
  59. package/dist/docx/contentTypeRegistry.d.ts +8 -0
  60. package/dist/docx/contentTypeRegistry.js +64 -0
  61. package/dist/docx/documentParser.d.ts +1 -13
  62. package/dist/docx/documentParser.js +1 -47
  63. package/dist/docx/ensureParaIds.d.ts +7 -1
  64. package/dist/docx/ensureParaIds.js +173 -215
  65. package/dist/docx/foldedListNumberFields.d.ts +85 -0
  66. package/dist/docx/foldedListNumberFields.js +267 -0
  67. package/dist/docx/footnoteParser.d.ts +1 -11
  68. package/dist/docx/footnoteParser.js +1 -16
  69. package/dist/docx/headerFooterParser.d.ts +1 -9
  70. package/dist/docx/headerFooterParser.js +1 -12
  71. package/dist/docx/headerFooterVerbatim.d.ts +39 -1
  72. package/dist/docx/headerFooterVerbatim.js +173 -2
  73. package/dist/docx/imageParser.d.ts +13 -1
  74. package/dist/docx/imageParser.js +20 -11
  75. package/dist/docx/metadataPrivacy.js +6 -1
  76. package/dist/docx/noteIds.d.ts +5 -0
  77. package/dist/docx/noteIds.js +6 -2
  78. package/dist/docx/noteReferenceMark.d.ts +3 -1
  79. package/dist/docx/noteReferenceMark.js +15 -14
  80. package/dist/docx/numberingParser.d.ts +3 -1
  81. package/dist/docx/numberingParser.js +17 -3
  82. package/dist/docx/packageParts.d.ts +3 -1
  83. package/dist/docx/packageParts.js +74 -19
  84. package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
  85. package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
  86. package/dist/docx/paragraphParser.js +101 -53
  87. package/dist/docx/paragraphPropertySource.d.ts +15 -1
  88. package/dist/docx/paragraphPropertySource.js +77 -22
  89. package/dist/docx/parser.js +3 -1
  90. package/dist/docx/removeHeaderFooterParts.js +17 -6
  91. package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
  92. package/dist/docx/reviewHistoryNamespace.js +4 -0
  93. package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
  94. package/dist/docx/reviewResolutionProvenance.js +153 -0
  95. package/dist/docx/rezip.d.ts +54 -6
  96. package/dist/docx/rezip.js +835 -257
  97. package/dist/docx/runConsolidator.d.ts +2 -58
  98. package/dist/docx/runConsolidator.js +3 -175
  99. package/dist/docx/runParser.d.ts +2 -1
  100. package/dist/docx/runParser.js +156 -116
  101. package/dist/docx/saveDiagnostics.d.ts +22 -0
  102. package/dist/docx/saveDiagnostics.js +0 -0
  103. package/dist/docx/selectiveSave.d.ts +3 -1
  104. package/dist/docx/selectiveSave.js +36 -4
  105. package/dist/docx/selectiveXmlPatch.d.ts +16 -8
  106. package/dist/docx/selectiveXmlPatch.js +81 -7
  107. package/dist/docx/serializer/documentSerializer.d.ts +9 -2
  108. package/dist/docx/serializer/documentSerializer.js +29 -10
  109. package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
  110. package/dist/docx/serializer/headerFooterSerializer.js +19 -11
  111. package/dist/docx/serializer/noteSerializer.d.ts +2 -2
  112. package/dist/docx/serializer/noteSerializer.js +24 -8
  113. package/dist/docx/serializer/paragraphSerializer.js +41 -4
  114. package/dist/docx/serializer/partNamespaces.js +2 -1
  115. package/dist/docx/serializer/runSerializer.js +3 -1
  116. package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
  117. package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
  118. package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
  119. package/dist/docx/settingsParser.d.ts +1 -2
  120. package/dist/docx/settingsParser.js +5 -6
  121. package/dist/docx/storyBlockReplay.d.ts +31 -0
  122. package/dist/docx/storyBlockReplay.js +471 -0
  123. package/dist/docx/storyPlainText.d.ts +20 -0
  124. package/dist/docx/storyPlainText.js +23 -0
  125. package/dist/docx/streamingXmlParser.d.ts +22 -1
  126. package/dist/docx/streamingXmlParser.js +49 -3
  127. package/dist/docx/structuralXmlPatch.js +18 -23
  128. package/dist/docx/styleParser.d.ts +5 -1
  129. package/dist/docx/styleParser.js +1 -1
  130. package/dist/docx/tableParser.d.ts +1 -8
  131. package/dist/docx/tableParser.js +1 -28
  132. package/dist/docx/textBoxParser.d.ts +1 -5
  133. package/dist/docx/textBoxParser.js +1 -20
  134. package/dist/docx/unzip.js +6 -1
  135. package/dist/docx/verbatimCapture.d.ts +3 -1
  136. package/dist/docx/verbatimCapture.js +49 -6
  137. package/dist/i18n/messages/ar.gen.d.ts +5 -0
  138. package/dist/i18n/messages/ar.gen.js +656 -0
  139. package/dist/i18n/messages/cs.gen.d.ts +5 -0
  140. package/dist/i18n/messages/cs.gen.js +656 -0
  141. package/dist/i18n/messages/de.gen.d.ts +5 -0
  142. package/dist/i18n/messages/de.gen.js +656 -0
  143. package/dist/i18n/messages/en.gen.d.ts +5 -0
  144. package/dist/i18n/messages/en.gen.js +656 -0
  145. package/dist/i18n/messages/es.gen.d.ts +5 -0
  146. package/dist/i18n/messages/es.gen.js +656 -0
  147. package/dist/i18n/messages/et.gen.d.ts +5 -0
  148. package/dist/i18n/messages/et.gen.js +656 -0
  149. package/dist/i18n/messages/fr.gen.d.ts +5 -0
  150. package/dist/i18n/messages/fr.gen.js +656 -0
  151. package/dist/i18n/messages/he.gen.d.ts +5 -0
  152. package/dist/i18n/messages/he.gen.js +656 -0
  153. package/dist/i18n/messages/hi.gen.d.ts +5 -0
  154. package/dist/i18n/messages/hi.gen.js +656 -0
  155. package/dist/i18n/messages/hu.gen.d.ts +5 -0
  156. package/dist/i18n/messages/hu.gen.js +656 -0
  157. package/dist/i18n/messages/locales.d.ts +5 -0
  158. package/dist/i18n/messages/locales.js +22 -0
  159. package/dist/i18n/messages/lt.gen.d.ts +5 -0
  160. package/dist/i18n/messages/lt.gen.js +656 -0
  161. package/dist/i18n/messages/lv.gen.d.ts +5 -0
  162. package/dist/i18n/messages/lv.gen.js +656 -0
  163. package/dist/i18n/messages/pl.gen.d.ts +5 -0
  164. package/dist/i18n/messages/pl.gen.js +656 -0
  165. package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
  166. package/dist/i18n/messages/pt-BR.gen.js +656 -0
  167. package/dist/i18n/messages/sk.gen.d.ts +5 -0
  168. package/dist/i18n/messages/sk.gen.js +656 -0
  169. package/dist/i18n/messages/tr.gen.d.ts +5 -0
  170. package/dist/i18n/messages/tr.gen.js +656 -0
  171. package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
  172. package/dist/i18n/messages/zh-CN.gen.js +656 -0
  173. package/dist/i18n/messages.d.ts +2 -3
  174. package/dist/i18n/messages.js +1 -19
  175. package/dist/internal/acceptedBlockProjection.d.ts +12 -0
  176. package/dist/internal/acceptedBlockProjection.js +45 -0
  177. package/dist/internal/noteMarkerAttrs.d.ts +8 -0
  178. package/dist/internal/noteMarkerAttrs.js +16 -0
  179. package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
  180. package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
  181. package/dist/internal/prosemirrorAttrBoundary.js +50 -0
  182. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  183. package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
  184. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  185. package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
  186. package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
  187. package/dist/layout-bridge/convert/noteReferences.js +1 -6
  188. package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
  189. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
  190. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
  191. package/dist/managers/DocumentLoaderManager.d.ts +1 -0
  192. package/dist/managers/DocumentLoaderManager.js +9 -1
  193. package/dist/markdown/escape.d.ts +3 -1
  194. package/dist/markdown/escape.js +9 -2
  195. package/dist/markdown/renderBlock.js +20 -121
  196. package/dist/markdown/renderParagraph.js +13 -15
  197. package/dist/markdown/renderRuns.d.ts +7 -1
  198. package/dist/markdown/renderRuns.js +35 -15
  199. package/dist/markdown/renderTable.js +2 -0
  200. package/dist/markdown/types.d.ts +3 -9
  201. package/dist/prosemirror/attrs/index.d.ts +2 -12
  202. package/dist/prosemirror/attrs/index.js +22 -48
  203. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
  204. package/dist/prosemirror/canonicalCommands.d.ts +121 -0
  205. package/dist/prosemirror/canonicalCommands.js +35 -0
  206. package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
  207. package/dist/prosemirror/canonicalSelectionRange.js +15 -0
  208. package/dist/prosemirror/clipboardIntent.d.ts +7 -0
  209. package/dist/prosemirror/clipboardIntent.js +9 -0
  210. package/dist/prosemirror/commands/pageBreak.js +15 -1
  211. package/dist/prosemirror/commands/pastePlainText.js +2 -1
  212. package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
  213. package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
  214. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
  215. package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
  216. package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
  217. package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
  218. package/dist/prosemirror/conversion/toProseDoc.d.ts +11 -4
  219. package/dist/prosemirror/conversion/toProseDoc.js +55 -14
  220. package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
  221. package/dist/prosemirror/executeEditorCommand.js +26 -0
  222. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  223. package/dist/prosemirror/extensions/core/ParagraphExtension.js +88 -48
  224. package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
  225. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
  226. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
  227. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +56 -28
  228. package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
  229. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  230. package/dist/prosemirror/extensions/marks/markUtils.js +72 -8
  231. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
  232. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
  233. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
  234. package/dist/prosemirror/insertOperations.js +2 -1
  235. package/dist/prosemirror/listMarker.d.ts +1 -0
  236. package/dist/prosemirror/listMarker.js +16 -7
  237. package/dist/prosemirror/listNumbering.js +26 -5
  238. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  239. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  240. package/dist/prosemirror/markupViewNotes.js +3 -2
  241. package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
  242. package/dist/prosemirror/noteReferenceReview.js +5 -3
  243. package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
  244. package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
  245. package/dist/prosemirror/plugins/suggestionMode.js +57 -7
  246. package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
  247. package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
  248. package/dist/prosemirror/runFormattingFromMarks.js +15 -1
  249. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
  250. package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
  251. package/dist/prosemirror/schema/marks.d.ts +2 -0
  252. package/dist/prosemirror/schema/nodes.d.ts +10 -2
  253. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
  254. package/dist/prosemirror/textInput.js +5 -1
  255. package/dist/prosemirror/trackedRevisionPath.js +2 -1
  256. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
  257. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
  258. package/dist/prosemirror/validation.js +4 -0
  259. package/dist/types/canonicalCapabilities.d.ts +215 -0
  260. package/dist/types/canonicalCapabilities.js +212 -0
  261. package/dist/types/canonicalSave.d.ts +11 -0
  262. package/dist/types/canonicalSave.js +0 -0
  263. package/dist/types/content.d.ts +2 -2
  264. package/dist/types/docxSerialization.d.ts +14 -0
  265. package/dist/types/docxSerialization.js +7 -0
  266. package/dist/utils/createDocument.js +2 -2
  267. package/package.json +16 -4
@@ -0,0 +1,20 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/storyPlainText.d.ts
3
+ /** Read the document body with pending revisions accepted. */
4
+ declare const getDocumentText: (body: document_d_exports.DocumentBody) => string;
5
+ /** Approximate word count of the accepted body text. */
6
+ declare const getWordCount: (body: document_d_exports.DocumentBody) => number;
7
+ /** Character count of the accepted body text. */
8
+ declare const getCharacterCount: (body: document_d_exports.DocumentBody) => number;
9
+ /** Read an accepted table, including nested blocks and tracked structure. */
10
+ declare const getTableText: (table: document_d_exports.Table) => string;
11
+ /** Read accepted text-box content for search and indexing. */
12
+ declare const getTextBoxText: (textBox: document_d_exports.TextBox) => string;
13
+ /** Read an accepted header or footer through the same walk as body and notes. */
14
+ declare const getHeaderFooterText: (headerFooter: document_d_exports.HeaderFooter) => string;
15
+ /** Read accepted footnote content, including nested blocks. */
16
+ declare const getFootnoteText: (footnote: document_d_exports.Footnote) => string;
17
+ /** Read accepted endnote content, including nested blocks. */
18
+ declare const getEndnoteText: (endnote: document_d_exports.Endnote) => string;
19
+ //#endregion
20
+ export { getCharacterCount, getDocumentText, getEndnoteText, getFootnoteText, getHeaderFooterText, getTableText, getTextBoxText, getWordCount };
@@ -0,0 +1,23 @@
1
+ import { blockPlainText } from "./blockPlainText.js";
2
+ //#region src/docx/storyPlainText.ts
3
+ /** Read the document body with pending revisions accepted. */
4
+ const getDocumentText = (body) => blockPlainText(body.content);
5
+ /** Approximate word count of the accepted body text. */
6
+ const getWordCount = (body) => {
7
+ const words = getDocumentText(body).trim().split(/\s+/u);
8
+ return words.length > 0 && words[0] !== "" ? words.length : 0;
9
+ };
10
+ /** Character count of the accepted body text. */
11
+ const getCharacterCount = (body) => getDocumentText(body).length;
12
+ /** Read an accepted table, including nested blocks and tracked structure. */
13
+ const getTableText = (table) => blockPlainText([table]);
14
+ /** Read accepted text-box content for search and indexing. */
15
+ const getTextBoxText = (textBox) => blockPlainText(textBox.content);
16
+ /** Read an accepted header or footer through the same walk as body and notes. */
17
+ const getHeaderFooterText = (headerFooter) => blockPlainText(headerFooter.content);
18
+ /** Read accepted footnote content, including nested blocks. */
19
+ const getFootnoteText = (footnote) => blockPlainText(footnote.content);
20
+ /** Read accepted endnote content, including nested blocks. */
21
+ const getEndnoteText = (endnote) => blockPlainText(endnote.content);
22
+ //#endregion
23
+ export { getCharacterCount, getDocumentText, getEndnoteText, getFootnoteText, getHeaderFooterText, getTableText, getTextBoxText, getWordCount };
@@ -6,6 +6,12 @@ type ParseXmlResult = {
6
6
  } | {
7
7
  status: "unsupported";
8
8
  };
9
+ type XmlSourceRange = {
10
+ readonly start: number;
11
+ readonly end: number;
12
+ };
13
+ /** Exact element source ranges, available only from the opt-in source-range reader. */
14
+ declare const getXmlSourceRange: (element: XmlElement) => XmlSourceRange | undefined;
9
15
  /**
10
16
  * Parse ordinary OOXML into Folio's existing element representation in one
11
17
  * pass. Unsupported or malformed constructs return a sentinel so callers can
@@ -17,11 +23,20 @@ type AttributeValueSpan = {
17
23
  };
18
24
  type OpenTagVisitor = (element: XmlElement, attributeValueSpans: ReadonlyMap<string, AttributeValueSpan>) => ReadonlyMap<string, string> | null;
19
25
  type OpenTagScanVisitor = (...args: Parameters<OpenTagVisitor>) => null;
26
+ type ElementScanOptions = {
27
+ element: XmlElement;
28
+ attributeValueSpans: ReadonlyMap<string, AttributeValueSpan>;
29
+ nameEnd: number;
30
+ parent: XmlElement | undefined;
31
+ };
32
+ type ElementScanVisitor = (options: ElementScanOptions) => void;
20
33
  /**
21
34
  * @param inheritedNamespaceScope the bindings in scope around `xml`, for a
22
35
  * fragment captured out of a part whose root declared them.
23
36
  */
24
37
  declare const parseStreamingXml: (xml: string, inheritedNamespaceScope?: XmlNamespaceScope) => ParseXmlResult;
38
+ /** Retain source spans for every element without changing the parsed namespace tree. */
39
+ declare const parseStreamingXmlWithSourceRanges: (xml: string, inheritedNamespaceScope?: XmlNamespaceScope) => ParseXmlResult;
25
40
  /** Retain the body tree while collecting identities for import repair. */
26
41
  declare const parseStreamingXmlWithIdentityVisitor: (xml: string, visitOpenTag: OpenTagScanVisitor) => ParseXmlResult;
27
42
  /** Rewrite selected decimal attribute values while preserving every other source byte. */
@@ -37,5 +52,11 @@ declare const scanStreamingXmlNumericIdAttributes: (xml: string, visitOpenTag: O
37
52
  } | {
38
53
  status: "scanned";
39
54
  };
55
+ /** Visit namespace-resolved start tags and source spans without building a tree. */
56
+ declare const scanStreamingXmlElements: (xml: string, visitElement: ElementScanVisitor) => {
57
+ status: "unsupported";
58
+ } | {
59
+ status: "scanned";
60
+ };
40
61
  //#endregion
41
- export { parseStreamingXml, parseStreamingXmlWithIdentityVisitor, rewriteStreamingXmlDecimalAttributes, scanStreamingXmlNumericIdAttributes };
62
+ export { getXmlSourceRange, parseStreamingXml, parseStreamingXmlWithIdentityVisitor, parseStreamingXmlWithSourceRanges, rewriteStreamingXmlDecimalAttributes, scanStreamingXmlElements, scanStreamingXmlNumericIdAttributes };
@@ -2,6 +2,9 @@ import { EMPTY_NAMESPACE_SCOPE, attachXmlNamespaceContext } from "./xmlNamespace
2
2
  import { FOLIO_XML_RESOURCE_LIMITS } from "./xmlResourceLimits.js";
3
3
  import { isOoxmlNumericIdAttributeName } from "@stll/docx-core";
4
4
  //#region src/docx/streamingXmlParser.ts
5
+ const sourceRanges = /* @__PURE__ */ new WeakMap();
6
+ /** Exact element source ranges, available only from the opt-in source-range reader. */
7
+ const getXmlSourceRange = (element) => sourceRanges.get(element);
5
8
  const BUILT_IN_ENTITIES = {
6
9
  amp: "&",
7
10
  apos: "'",
@@ -14,6 +17,7 @@ const parseStreamingXmlInternal = (options) => {
14
17
  const visitOpenTag = options.visitOpenTag;
15
18
  let spanMode = "none";
16
19
  if (visitOpenTag !== void 0) spanMode = options.mode === "tree" ? "identities" : "all";
20
+ if (options.visitElement !== void 0) spanMode = "all";
17
21
  const openTagOptions = {
18
22
  xml,
19
23
  start: 0,
@@ -67,6 +71,10 @@ const parseStreamingXmlInternal = (options) => {
67
71
  const name = xml.slice(open + 2, close).trim();
68
72
  const frame = stack.pop();
69
73
  if (!frame || frame.name !== name) return { status: "unsupported" };
74
+ if (frame.start !== void 0) sourceRanges.set(frame.element, {
75
+ start: frame.start,
76
+ end: close + 1
77
+ });
70
78
  cursor = close + 1;
71
79
  mergeAdjacentText = false;
72
80
  continue;
@@ -79,6 +87,12 @@ const parseStreamingXmlInternal = (options) => {
79
87
  const scope = parent?.namespaceScope ?? inheritedNamespaceScope;
80
88
  if (options.mode === "attributes" && options.retainAttribute !== void 0 && parsedTag.element.attributes === void 0) parsedTag.element.namespaceScope = scope;
81
89
  else attachXmlNamespaceContext(parsedTag.element, scope);
90
+ options.visitElement?.({
91
+ element: parsedTag.element,
92
+ attributeValueSpans: parsedTag.attributeValueSpans ?? /* @__PURE__ */ new Map(),
93
+ nameEnd: parsedTag.nameEnd,
94
+ parent
95
+ });
82
96
  const rewritten = (options.mode === "tree" ? parsedTag.attributeValueSpans !== void 0 : options.retainAttribute === void 0 || parsedTag.element.attributes !== void 0) ? visitOpenTag?.(parsedTag.element, parsedTag.attributeValueSpans ?? /* @__PURE__ */ new Map()) : null;
83
97
  if (rewritten) for (const [attributeName, value] of rewritten) {
84
98
  const span = parsedTag.attributeValueSpans?.get(attributeName);
@@ -91,11 +105,18 @@ const parseStreamingXmlInternal = (options) => {
91
105
  if (options.mode === "tree") appendElement(parent ?? root, parsedTag.element);
92
106
  if (!parsedTag.selfClosing) {
93
107
  if (stack.length >= FOLIO_XML_RESOURCE_LIMITS.maxDepth) return { status: "unsupported" };
94
- stack.push({
108
+ stack.push(options.mode === "tree" && options.sourceRanges === "tracked" ? {
109
+ element: parsedTag.element,
110
+ name: parsedTag.name,
111
+ start: open
112
+ } : {
95
113
  element: parsedTag.element,
96
114
  name: parsedTag.name
97
115
  });
98
- }
116
+ } else if (options.mode === "tree" && options.sourceRanges === "tracked") sourceRanges.set(parsedTag.element, {
117
+ start: open,
118
+ end: close + 1
119
+ });
99
120
  cursor = close + 1;
100
121
  mergeAdjacentText = false;
101
122
  }
@@ -121,6 +142,19 @@ const parseStreamingXml = (xml, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE)
121
142
  value: parsed.value
122
143
  } : { status: "unsupported" };
123
144
  };
145
+ /** Retain source spans for every element without changing the parsed namespace tree. */
146
+ const parseStreamingXmlWithSourceRanges = (xml, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE) => {
147
+ const parsed = parseStreamingXmlInternal({
148
+ xml,
149
+ mode: "tree",
150
+ inheritedNamespaceScope,
151
+ sourceRanges: "tracked"
152
+ });
153
+ return parsed.status === "parsed" ? {
154
+ status: "parsed",
155
+ value: parsed.value
156
+ } : { status: "unsupported" };
157
+ };
124
158
  /** Retain the body tree while collecting identities for import repair. */
125
159
  const parseStreamingXmlWithIdentityVisitor = (xml, visitOpenTag) => {
126
160
  const parsed = parseStreamingXmlInternal({
@@ -170,12 +204,23 @@ const scanStreamingXmlNumericIdAttributes = (xml, visitOpenTag) => {
170
204
  });
171
205
  return parsed.status === "parsed" ? { status: "scanned" } : parsed;
172
206
  };
207
+ /** Visit namespace-resolved start tags and source spans without building a tree. */
208
+ const scanStreamingXmlElements = (xml, visitElement) => {
209
+ const parsed = parseStreamingXmlInternal({
210
+ xml,
211
+ mode: "attributes",
212
+ visitOpenTag: () => null,
213
+ visitElement
214
+ });
215
+ return parsed.status === "parsed" ? { status: "scanned" } : parsed;
216
+ };
173
217
  const parseOpenTag = ({ xml, start, close, spanMode, retainAttribute }) => {
174
218
  let cursor = skipWhitespace(xml, start, close);
175
219
  const nameStart = cursor;
176
220
  cursor = scanName(xml, cursor, close);
177
221
  if (cursor === nameStart) return { status: "unsupported" };
178
222
  const name = xml.slice(nameStart, cursor);
223
+ const nameEnd = cursor;
179
224
  if (isUnsafePropertyName(name)) return { status: "unsupported" };
180
225
  let attributes;
181
226
  let attributeValueSpans;
@@ -232,6 +277,7 @@ const parseOpenTag = ({ xml, start, close, spanMode, retainAttribute }) => {
232
277
  status: "parsed",
233
278
  element,
234
279
  name,
280
+ nameEnd,
235
281
  selfClosing,
236
282
  attributeValueSpans
237
283
  };
@@ -358,4 +404,4 @@ const isUnsafePropertyName = (name) => {
358
404
  }
359
405
  };
360
406
  //#endregion
361
- export { parseStreamingXml, parseStreamingXmlWithIdentityVisitor, rewriteStreamingXmlDecimalAttributes, scanStreamingXmlNumericIdAttributes };
407
+ export { getXmlSourceRange, parseStreamingXml, parseStreamingXmlWithIdentityVisitor, parseStreamingXmlWithSourceRanges, rewriteStreamingXmlDecimalAttributes, scanStreamingXmlElements, scanStreamingXmlNumericIdAttributes };
@@ -1,8 +1,8 @@
1
1
  import { canonicalJson } from "../utils/canonicalJson.js";
2
2
  import { parseDocumentBody } from "./documentParser.js";
3
3
  import { paraIdAttribute } from "./paraIdAttribute.js";
4
- import { spliceXml } from "./selectiveXmlPatch.js";
5
- import { readRootNamespaceBindings, serializePartElement } from "./serializer/partNamespaces.js";
4
+ import { spliceSupportsRootNamespaces, spliceXml } from "./selectiveXmlPatch.js";
5
+ import { materializeReplayFragment } from "./storyBlockReplay.js";
6
6
  import { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, getChildElements, getLocalName, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
7
7
  //#region src/docx/structuralXmlPatch.ts
8
8
  /** Conservative body-paragraph splices; source offsets never come from reserialization. */
@@ -111,21 +111,6 @@ const paragraphIds = (root) => {
111
111
  }
112
112
  return ids;
113
113
  };
114
- /** Bind fragment prefixes locally: the source root may use entirely different aliases. */
115
- const paragraphFragment = ({ xml, block, bindings }) => {
116
- const fragment = xml.slice(block.start, block.end);
117
- const openEnd = fragment.indexOf(">");
118
- const name = block.element.name ?? "w:p";
119
- const selfClosing = fragment[openEnd - 1] === "/";
120
- return serializePartElement({
121
- partPath: "word/document.xml",
122
- rootName: name,
123
- rootAttributes: fragment.slice(name.length + 1, selfClosing ? openEnd - 1 : openEnd).trim(),
124
- baselinePrefixes: [],
125
- sourceBindings: bindings,
126
- body: selfClosing ? "" : fragment.slice(openEnd + 1, fragment.lastIndexOf("</"))
127
- });
128
- };
129
114
  /**
130
115
  * Insert/delete direct body paragraphs between surviving paragraph/table anchors.
131
116
  * Tables are opaque barriers: their modeled content and order must stay identical.
@@ -168,11 +153,11 @@ const buildStructuralDocumentPatch = ({ originalXml, serializedXml, changedIds }
168
153
  const survivingBefore = [...before.keys()].filter((key) => after.has(key));
169
154
  const survivingAfter = [...after.keys()].filter((key) => before.has(key));
170
155
  if (canonicalJson(survivingBefore) !== canonicalJson(survivingAfter)) return null;
171
- const bindings = readRootNamespaceBindings(serializedXml);
172
- const fragmentFor = (block) => paragraphFragment({
173
- xml: serializedXml,
174
- block,
175
- bindings
156
+ const fragmentFor = (block) => materializeReplayFragment({
157
+ xml: serializedXml.slice(block.start, block.end),
158
+ element: block.element,
159
+ sourceNamespace: getNamespaceUri(source.root) ?? "",
160
+ generatedRoot: current.root
176
161
  });
177
162
  const splices = [];
178
163
  for (const [key, block] of before) if (!after.has(key)) splices.push({
@@ -186,11 +171,14 @@ const buildStructuralDocumentPatch = ({ originalXml, serializedXml, changedIds }
186
171
  if (!original) {
187
172
  const id = paraIdAttribute(block.element)?.toUpperCase();
188
173
  if (!id || allSourceIds.has(id)) return null;
189
- pending.push(fragmentFor(block));
174
+ const fragment = fragmentFor(block);
175
+ if (fragment === null) return null;
176
+ pending.push(fragment);
190
177
  continue;
191
178
  }
192
179
  const id = paraIdAttribute(block.element)?.toUpperCase();
193
180
  const replacement = id !== void 0 && changed.has(id) ? fragmentFor(block) : originalXml.slice(original.start, original.end);
181
+ if (replacement === null) return null;
194
182
  if (pending.length > 0 || replacement !== originalXml.slice(original.start, original.end)) splices.push({
195
183
  start: original.start,
196
184
  end: original.end,
@@ -203,6 +191,13 @@ const buildStructuralDocumentPatch = ({ originalXml, serializedXml, changedIds }
203
191
  end: source.bodyEnd,
204
192
  newXml: pending.join("")
205
193
  });
194
+ if (!spliceSupportsRootNamespaces({
195
+ originalXml,
196
+ serializedXml,
197
+ fragments: splices.map(({ newXml }) => newXml).filter(Boolean),
198
+ originalRoot: source.root,
199
+ serializedRoot: current.root
200
+ })) return null;
206
201
  return spliceXml(originalXml, splices);
207
202
  };
208
203
  //#endregion
@@ -8,6 +8,10 @@ type ParsedStylesPackage = {
8
8
  styleDefinitions: document_d_exports.StyleDefinitions;
9
9
  styles: StyleMap;
10
10
  };
11
+ /**
12
+ * Resolve style inheritance chain
13
+ */
14
+ declare function resolveStyleInheritance(style: document_d_exports.Style, styleMap: StyleMap, visited?: Set<string>): document_d_exports.Style;
11
15
  /**
12
16
  * Parse styles.xml content
13
17
  *
@@ -46,4 +50,4 @@ declare function getDefaultCharacterStyle(styleMap: StyleMap): document_d_export
46
50
  */
47
51
  declare function getStylesByType(styleMap: StyleMap, type: document_d_exports.StyleType): document_d_exports.Style[];
48
52
  //#endregion
49
- export { ParsedStylesPackage, StyleMap, getDefaultCharacterStyle, getDefaultParagraphStyle, getResolvedStyle, getStylesByType, parseStyleDefinitions, parseStyles, parseStylesPackage };
53
+ export { ParsedStylesPackage, StyleMap, getDefaultCharacterStyle, getDefaultParagraphStyle, getResolvedStyle, getStylesByType, parseStyleDefinitions, parseStyles, parseStylesPackage, resolveStyleInheritance };
@@ -554,4 +554,4 @@ function getStylesByType(styleMap, type) {
554
554
  return result;
555
555
  }
556
556
  //#endregion
557
- export { getDefaultCharacterStyle, getDefaultParagraphStyle, getResolvedStyle, getStylesByType, parseStyleDefinitions, parseStyles, parseStylesPackage };
557
+ export { getDefaultCharacterStyle, getDefaultParagraphStyle, getResolvedStyle, getStylesByType, parseStyleDefinitions, parseStyles, parseStylesPackage, resolveStyleInheritance };
@@ -342,13 +342,6 @@ declare function isCellMergeStart(cell: document_d_exports.TableCell): boolean;
342
342
  * @returns true if cell spans multiple columns
343
343
  */
344
344
  declare function isCellHorizontallyMerged(cell: document_d_exports.TableCell): boolean;
345
- /**
346
- * Get the plain text content of a table
347
- *
348
- * @param table - The table to extract text from
349
- * @returns Plain text content
350
- */
351
- declare function getTableText(table: document_d_exports.Table): string;
352
345
  /**
353
346
  * Check if table has header row
354
347
  *
@@ -371,4 +364,4 @@ declare function getHeaderRows(table: document_d_exports.Table): document_d_expo
371
364
  */
372
365
  declare function isFloatingTable(table: document_d_exports.Table): boolean;
373
366
  //#endregion
374
- export { CELL_CONTENT_HANDLERS, MAX_TABLE_COLUMNS, ROW_CONTENT_HANDLERS, TABLE_CONTENT_HANDLERS, getHeaderRows, getTableColumnCount, getTableRowCount, getTableText, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable, isTableCellMergeRevisionContinuation, isTableCellMergeRevisionValue, parseCellMargins, parseConditionalFormatStyle, parseFloatingTableProperties, parseTable, parseTableBorders, parseTableCell, parseTableCellProperties, parseTableGrid, parseTableLook, parseTableMeasurement, parseTableProperties, parseTablePropertyExceptions, parseTableRow, parseTableRowProperties };
367
+ export { CELL_CONTENT_HANDLERS, MAX_TABLE_COLUMNS, ROW_CONTENT_HANDLERS, TABLE_CONTENT_HANDLERS, getHeaderRows, getTableColumnCount, getTableRowCount, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable, isTableCellMergeRevisionContinuation, isTableCellMergeRevisionValue, parseCellMargins, parseConditionalFormatStyle, parseFloatingTableProperties, parseTable, parseTableBorders, parseTableCell, parseTableCellProperties, parseTableGrid, parseTableLook, parseTableMeasurement, parseTableProperties, parseTablePropertyExceptions, parseTableRow, parseTableRowProperties };
@@ -1406,33 +1406,6 @@ function isCellHorizontallyMerged(cell) {
1406
1406
  return (cell.formatting?.gridSpan ?? 1) > 1;
1407
1407
  }
1408
1408
  /**
1409
- * Get the plain text content of a table
1410
- *
1411
- * @param table - The table to extract text from
1412
- * @returns Plain text content
1413
- */
1414
- function getTableText(table) {
1415
- const rows = [];
1416
- for (const row of table.rows) {
1417
- const cells = [];
1418
- for (const cell of row.cells) {
1419
- const cellText = cell.content.filter((c) => c.type === "paragraph").map((p) => getParagraphText(p)).join("\n");
1420
- cells.push(cellText);
1421
- }
1422
- rows.push(cells.join(" "));
1423
- }
1424
- return rows.join("\n");
1425
- }
1426
- /**
1427
- * Helper to get paragraph text (simplified)
1428
- */
1429
- function getParagraphText(para) {
1430
- return para.content.filter((c) => "content" in c).flatMap((run) => {
1431
- if (!("content" in run) || !Array.isArray(run.content)) return [];
1432
- return run.content.filter((c) => typeof c === "object" && c !== null && "type" in c && c.type === "text" && "text" in c).map((c) => c.text);
1433
- }).join("");
1434
- }
1435
- /**
1436
1409
  * Check if table has header row
1437
1410
  *
1438
1411
  * @param table - The table to check
@@ -1461,4 +1434,4 @@ function isFloatingTable(table) {
1461
1434
  return table.formatting?.floating !== void 0;
1462
1435
  }
1463
1436
  //#endregion
1464
- export { CELL_CONTENT_HANDLERS, MAX_TABLE_COLUMNS, ROW_CONTENT_HANDLERS, TABLE_CONTENT_HANDLERS, getHeaderRows, getTableColumnCount, getTableRowCount, getTableText, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable, isTableCellMergeRevisionContinuation, isTableCellMergeRevisionValue, parseCellMargins, parseConditionalFormatStyle, parseFloatingTableProperties, parseTable, parseTableBorders, parseTableCell, parseTableCellProperties, parseTableGrid, parseTableLook, parseTableMeasurement, parseTableProperties, parseTablePropertyExceptions, parseTableRow, parseTableRowProperties };
1437
+ export { CELL_CONTENT_HANDLERS, MAX_TABLE_COLUMNS, ROW_CONTENT_HANDLERS, TABLE_CONTENT_HANDLERS, getHeaderRows, getTableColumnCount, getTableRowCount, hasHeaderRow, isCellHorizontallyMerged, isCellMergeContinuation, isCellMergeStart, isFloatingTable, isTableCellMergeRevisionContinuation, isTableCellMergeRevisionValue, parseCellMargins, parseConditionalFormatStyle, parseFloatingTableProperties, parseTable, parseTableBorders, parseTableCell, parseTableCellProperties, parseTableGrid, parseTableLook, parseTableMeasurement, parseTableProperties, parseTablePropertyExceptions, parseTableRow, parseTableRowProperties };
@@ -103,10 +103,6 @@ declare function hasTextBoxOutline(textBox: document_d_exports.TextBox): boolean
103
103
  * Check if text box has content
104
104
  */
105
105
  declare function hasTextBoxContent(textBox: document_d_exports.TextBox): boolean;
106
- /**
107
- * Get plain text from text box (helper for search/indexing)
108
- */
109
- declare function getTextBoxText(textBox: document_d_exports.TextBox): string;
110
106
  /**
111
107
  * Resolve fill color to CSS color string
112
108
  */
@@ -151,4 +147,4 @@ type ScanRunForTextBoxDrawingsOptions = {
151
147
  };
152
148
  declare const scanRunForTextBoxDrawings: ({ xmlRun, claimedByRunParser }: ScanRunForTextBoxDrawingsOptions) => TextBoxRunScan;
153
149
  //#endregion
154
- export { ParagraphParserFn, ScanRunForTextBoxDrawingsOptions, TableParserFn, TextBoxDrawing, TextBoxRunScan, extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
150
+ export { ParagraphParserFn, ScanRunForTextBoxDrawingsOptions, TableParserFn, TextBoxDrawing, TextBoxRunScan, extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
@@ -285,25 +285,6 @@ function hasTextBoxContent(textBox) {
285
285
  return textBox.content.length > 0;
286
286
  }
287
287
  /**
288
- * Get plain text from text box (helper for search/indexing)
289
- */
290
- function getTextBoxText(textBox) {
291
- return textBox.content.map(getTextBoxBlockText).join("\n");
292
- }
293
- const getTextBoxBlockText = (block) => {
294
- if (block.type === "paragraph") {
295
- const runTexts = [];
296
- for (const item of block.content) {
297
- if (item.type !== "run") continue;
298
- for (const content of item.content) if (content.type === "text") runTexts.push(content.text);
299
- }
300
- return runTexts.join("");
301
- }
302
- if (block.type === "table") return block.rows.map((row) => row.cells.map((cell) => cell.content.map(getTextBoxBlockText).join("\n")).join(" ")).join("\n");
303
- if (block.type !== "blockSdt" && block.type !== "blockCustomXml") return "";
304
- return block.content.map(getTextBoxBlockText).join("\n");
305
- };
306
- /**
307
288
  * Resolve fill color to CSS color string
308
289
  */
309
290
  function resolveTextBoxFillColor(textBox) {
@@ -380,4 +361,4 @@ const scanRunForTextBoxDrawings = ({ xmlRun, claimedByRunParser }) => {
380
361
  };
381
362
  };
382
363
  //#endregion
383
- export { extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
364
+ export { extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
@@ -5,6 +5,7 @@ import { openDocxBuffer } from "./encryption/openEncryptedDocx.js";
5
5
  import { RASTER_MIME_TYPES, detectRasterMimeType } from "./rasterMime.js";
6
6
  import { decodeXmlBytes } from "./xmlEncoding.js";
7
7
  import { FOLIO_XML_RESOURCE_LIMITS, assertXmlResourceLimits, createXmlPackageBudget } from "./xmlResourceLimits.js";
8
+ import { writeZipPart } from "@stll/docx-core/zip";
8
9
  import JSZip from "jszip";
9
10
  //#region src/docx/unzip.ts
10
11
  /**
@@ -282,7 +283,11 @@ const replaceRawDocxXmlParts = async (content, parts) => {
282
283
  lowerPath: path.toLowerCase(),
283
284
  content: xmlContent
284
285
  });
285
- content.originalZip.file(path, xmlContent);
286
+ writeZipPart({
287
+ zip: content.originalZip,
288
+ path,
289
+ data: xmlContent
290
+ });
286
291
  identitiesChanged = true;
287
292
  }
288
293
  if (identitiesChanged) content.originalBuffer = await content.originalZip.generateAsync({
@@ -1,3 +1,4 @@
1
+ import { document_d_exports } from "../types/document.js";
1
2
  import { XmlElement, XmlNamespaceScope } from "./xmlParser.js";
2
3
  //#region src/docx/verbatimCapture.d.ts
3
4
  declare const UntranslatableStrictNamespaceError_base: import("better-result").TaggedErrorClass<"UntranslatableStrictNamespaceError">;
@@ -29,5 +30,6 @@ declare const isSafeCapturedXmlDocument: (xml: string) => boolean;
29
30
  * must therefore depend on nothing but the element they are given.
30
31
  */
31
32
  declare const createCapturedXmlSanitizer: (options: SanitizeCapturedXmlOptions) => ((xml: string | undefined) => string | null);
33
+ declare const captureSourceProfileXml: (element: XmlElement, conformance: document_d_exports.DocxConformanceClass | undefined) => string;
32
34
  //#endregion
33
- export { UntranslatableStrictNamespaceError, captureVerbatimXml, createCapturedXmlSanitizer, isSafeCapturedXmlDocument };
35
+ export { UntranslatableStrictNamespaceError, captureSourceProfileXml, captureVerbatimXml, createCapturedXmlSanitizer, isSafeCapturedXmlDocument };
@@ -1,10 +1,11 @@
1
1
  import { TRANSITIONAL_NAMESPACE_BY_STRICT_URI } from "./strictValueEncodings.gen.js";
2
2
  import { NUMBERS_PER_PERCENT, isStrictNamespaceUri, percentageSpelling, toTransitionalNamespaceUri, transitionalSlotEncoding } from "./transitionalSpelling.js";
3
3
  import { roundHalfAwayFromZero, universalMeasureAs } from "./universalMeasure.js";
4
- import { NAMESPACES, OOXML_NAMESPACE_SCOPE, cloneElement, elementToXml, getChildElements, getLocalName, getNamespacePrefix, parseXml } from "./xmlParser.js";
4
+ import { NAMESPACES, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, elementToXml, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespacePrefix, parseXml, resolveAttributeNamespaceUri } from "./xmlParser.js";
5
5
  import { assertXmlResourceLimits } from "./xmlResourceLimits.js";
6
6
  import { getDocxXmlSafetyIssue } from "./xmlSafety.js";
7
- import { TaggedError } from "better-result";
7
+ import { TaggedError, panic } from "better-result";
8
+ import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
8
9
  //#region src/docx/verbatimCapture.ts
9
10
  /**
10
11
  * The one way to turn parsed markup into a string a rebuilt part replays.
@@ -75,14 +76,22 @@ const originOf = (element, inherited) => {
75
76
  });
76
77
  return TRANSITIONAL_URIS.has(uri) ? "transitional" : inherited;
77
78
  };
78
- const convertAttributes = (element, namespaceUri, localName, convertValues) => {
79
+ /** A same-local-name extension attribute is not a slot in the element's vocabulary. */
80
+ const attributeSlotEncoding = (element, name) => {
81
+ if (getNamespacePrefix(name) !== null) {
82
+ const attributeUri = resolveAttributeNamespaceUri(element, name);
83
+ if (attributeUri === void 0 || element.namespaceUri === void 0 || toTransitionalNamespaceUri(attributeUri) !== toTransitionalNamespaceUri(element.namespaceUri)) return void 0;
84
+ }
85
+ return transitionalSlotEncoding(element.namespaceUri, getLocalName(element.name), getLocalName(name));
86
+ };
87
+ const convertAttributes = (element, namespaceUri, convertValues) => {
79
88
  const attributes = element.attributes ?? {};
80
89
  let converted;
81
90
  for (const [name, raw] of Object.entries(attributes)) {
82
91
  if (typeof raw !== "string") continue;
83
92
  let next = raw;
84
93
  if (name === "xmlns" || name.startsWith("xmlns:")) next = toTransitionalNamespaceUri(raw);
85
- else if (convertValues) next = transitionalValue(raw, namespaceUri, transitionalSlotEncoding(element.namespaceUri, localName, getLocalName(name)));
94
+ else if (convertValues) next = transitionalValue(raw, namespaceUri, attributeSlotEncoding(element, name));
86
95
  if (next === raw) continue;
87
96
  converted ??= { ...attributes };
88
97
  converted[name] = next;
@@ -99,7 +108,7 @@ const toTransitional = (element, inherited) => {
99
108
  const namespaceUri = element.namespaceUri === void 0 ? "" : toTransitionalNamespaceUri(element.namespaceUri);
100
109
  const localName = getLocalName(element.name);
101
110
  const textEncoding = transitionalSlotEncoding(element.namespaceUri, localName);
102
- const attributes = element.attributes ? convertAttributes(element, namespaceUri, localName, origin === "strict") : void 0;
111
+ const attributes = element.attributes ? convertAttributes(element, namespaceUri, origin === "strict") : void 0;
103
112
  let elements;
104
113
  for (const [index, child] of (element.elements ?? []).entries()) {
105
114
  const next = origin === "strict" && child.type === "text" && typeof child.text === "string" ? childWithValue(child, transitionalValue(child.text, namespaceUri, textEncoding)) : toTransitional(child, origin);
@@ -363,5 +372,39 @@ const createCapturedXmlSanitizer = (options) => {
363
372
  return sanitized;
364
373
  };
365
374
  };
375
+ const STRICT_BY_TRANSITIONAL = new Map([...TRANSITIONAL_NAMESPACE_BY_STRICT_URI].map(([strict, transitional]) => [transitional, strict]));
376
+ const strictPercentage = (value, unit) => /^-?\d+(?:\.\d+)?$/u.test(value) ? `${Number(value) / NUMBERS_PER_PERCENT[unit]}%` : value;
377
+ /** Translate generated markup by namespace/slot, never by a user-text replacement. */
378
+ const strictElement = (element) => {
379
+ const localName = getLocalName(element.name);
380
+ for (const [name, value] of Object.entries(element.attributes ?? {})) {
381
+ if (typeof value !== "string") continue;
382
+ if (name === "xmlns" || name.startsWith("xmlns:")) {
383
+ element.attributes[name] = STRICT_BY_TRANSITIONAL.get(value) ?? value;
384
+ continue;
385
+ }
386
+ if (localName === "graphicData" && toTransitionalNamespaceUri(element.namespaceUri ?? "") === NAMESPACES.a && name === "uri") {
387
+ element.attributes[name] = STRICT_BY_TRANSITIONAL.get(value) ?? value;
388
+ continue;
389
+ }
390
+ const encoding = attributeSlotEncoding(element, name);
391
+ if (encoding?.percent && (!encoding.measure || getAttributeByNamespaceUri(element, WORDPROCESSINGML_NAMESPACE_URIS, "type") === "pct")) element.attributes[name] = strictPercentage(value, encoding.percent);
392
+ }
393
+ const encoding = transitionalSlotEncoding(element.namespaceUri, localName);
394
+ for (const child of element.elements ?? []) if (child.type === "element") strictElement(child);
395
+ else if (child.type === "text" && typeof child.text === "string" && encoding?.percent) child.text = strictPercentage(child.text, encoding.percent);
396
+ };
397
+ /** Serialize a generated, scope-bound block in the source part's profile. */
398
+ const cloneSourceProfileTree = (element) => cloneElement(element, {
399
+ ...element.attributes === void 0 ? {} : { attributes: { ...element.attributes } },
400
+ ...element.elements === void 0 ? {} : { elements: element.elements.map(cloneSourceProfileTree) }
401
+ });
402
+ const captureSourceProfileXml = (element, conformance) => {
403
+ const owned = cloneSourceProfileTree(element);
404
+ if (conformance === DOCX_CONFORMANCE_CLASSES.STRICT) strictElement(owned);
405
+ const xml = elementToXml(owned);
406
+ if (!isSafeCapturedXmlDocument(xml)) panic("Generated source-profile markup is unsafe.");
407
+ return xml;
408
+ };
366
409
  //#endregion
367
- export { UntranslatableStrictNamespaceError, captureVerbatimXml, createCapturedXmlSanitizer, isSafeCapturedXmlDocument };
410
+ export { UntranslatableStrictNamespaceError, captureSourceProfileXml, captureVerbatimXml, createCapturedXmlSanitizer, isSafeCapturedXmlDocument };
@@ -0,0 +1,5 @@
1
+ import { FolioMessages } from "../messages.js";
2
+ //#region src/i18n/messages/ar.gen.d.ts
3
+ declare const messages: FolioMessages;
4
+ //#endregion
5
+ export { messages as default, messages };