@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
@@ -1,13 +1,17 @@
1
1
  import { consumeTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
2
2
  import { consumeSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
3
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
4
+ import { canonicalJson } from "../utils/canonicalJson.js";
3
5
  import { isAllowedExternalWatermarkImageUrl } from "../watermark/index.js";
4
6
  import { appVersionInSchemaForm, normalizeAppVersionInExtendedProperties } from "./appVersionNormalization.js";
5
7
  import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
6
8
  import { applyReplyThreadMarkers } from "./commentReplyMarkers.js";
9
+ import { registerContentTypeParts } from "./contentTypeRegistry.js";
7
10
  import { readDocumentSectionFacts } from "./documentSectionFacts.js";
8
11
  import { normalizeDrawingIds } from "./drawingIdNormalization.js";
9
12
  import { rebindDrawingImageRelationship } from "./drawingRelationships.js";
10
13
  import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
14
+ import { getDocumentSourceBaseline } from "./headerFooterVerbatim.js";
11
15
  import { assertValidFolioDocumentModel } from "./modelValidation.js";
12
16
  import { isNewDataUrlDrawing } from "./newImage.js";
13
17
  import { missingNoteReferenceStyles, noteReferenceNeeds } from "./noteReferenceStyles.js";
@@ -15,11 +19,12 @@ import { parseNumbering } from "./numberingParser.js";
15
19
  import { paragraphNumberingReferenceId as paragraphNumberingReferenceId$1 } from "./numberingReference.js";
16
20
  import { isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries } from "./packageParts.js";
17
21
  import { normalizeParaIdRangeInXmlParts } from "./paraIdRangeNormalization.js";
18
- import { BLOCK_TREE_DESCENT, visitBlockTreeRecords } from "./paragraphTraversal.js";
22
+ import { cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, copyDocumentParagraphPropertySourceContract } from "./paragraphPropertySource.js";
23
+ import { BLOCK_TREE_DESCENT, visitBlockTreeRecords, visitInlineContentSlots, visitParagraphRuns } from "./paragraphTraversal.js";
19
24
  import { RELATIONSHIP_TYPES, parseRelationships, resolveRelativePath } from "./relsParser.js";
20
25
  import { removeResolvedHeaderFooterParts } from "./removeHeaderFooterParts.js";
21
26
  import { normalizeRevisionIdsInXmlParts } from "./revisionIdNormalization.js";
22
- import { buildPatchedNotePartXml, collectChangedNoteParaIds, collectParaIds, patchNumberingDefinitions } from "./selectiveXmlPatch.js";
27
+ import { buildPatchedNotePartXml, collectChangedNoteParaIds, collectParaIds, patchNumberingDefinitions, spliceXml } from "./selectiveXmlPatch.js";
23
28
  import { planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
24
29
  import { serializeDocument } from "./serializer/documentSerializer.js";
25
30
  import { serializeFontTableXml } from "./serializer/fontTableSerializer.js";
@@ -30,11 +35,16 @@ import { readRootNamespaceBindings, serializePartElement } from "./serializer/pa
30
35
  import { serializeSettingsXml } from "./serializer/settingsSerializer.js";
31
36
  import { serializeStyle, serializeStylesXml } from "./serializer/stylesSerializer.js";
32
37
  import { serializeThemeXml } from "./serializer/themeSerializer.js";
38
+ import { updateEvenAndOddHeaders } from "./settingsHeaderFooterUpdate.js";
39
+ import { parseSettings } from "./settingsParser.js";
40
+ import { getXmlSourceRange, parseStreamingXmlWithSourceRanges } from "./streamingXmlParser.js";
41
+ import { captureVerbatimXml } from "./verbatimCapture.js";
33
42
  import { hasCanonicalWordprocessingPrefixes } from "./wordprocessingPrefixes.js";
34
- import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
35
- import { TaggedError, panic } from "better-result";
43
+ import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, findAttributeByNamespaceUri, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
44
+ import { Result, TaggedError, panic } from "better-result";
36
45
  import { assertValidOoxmlNumericIds, escapeXmlAttribute, escapeXmlText, validateDocxPackage } from "@stll/docx-core";
37
46
  import { mintRelationshipId } from "@stll/docx-core/model";
47
+ import { writeZipPart } from "@stll/docx-core/zip";
38
48
  import JSZip from "jszip";
39
49
  //#region src/docx/rezip.ts
40
50
  /**
@@ -196,9 +206,14 @@ async function serializeCommentsToZip(doc, zip, compressionLevel) {
196
206
  }
197
207
  const plan = planCommentParts(comments);
198
208
  const commentsXml = serializeComments(plan, sourceCommentsXml === void 0 ? void 0 : readRootNamespaceBindings(sourceCommentsXml));
199
- zip.file(sourceCommentsFile?.name ?? "word/comments.xml", commentsXml, {
200
- compression: "DEFLATE",
201
- compressionOptions: { level: compressionLevel }
209
+ writeZipPart({
210
+ zip,
211
+ path: sourceCommentsFile?.name ?? "word/comments.xml",
212
+ data: commentsXml,
213
+ options: {
214
+ compression: "DEFLATE",
215
+ compressionOptions: { level: compressionLevel }
216
+ }
202
217
  });
203
218
  await ensureCommentsContentType(zip, compressionLevel);
204
219
  await ensureCommentsRelationship(zip, compressionLevel);
@@ -227,9 +242,14 @@ async function syncCommentsExtendedPart(plan, zip, compressionLevel) {
227
242
  await transformPackagingFile(zip, "word/_rels/document.xml.rels", removeCommentsExtendedRelationship, compressionLevel);
228
243
  return;
229
244
  }
230
- zip.file(existing?.name ?? "word/commentsExtended.xml", xml, {
231
- compression: "DEFLATE",
232
- compressionOptions: { level: compressionLevel }
245
+ writeZipPart({
246
+ zip,
247
+ path: existing?.name ?? "word/commentsExtended.xml",
248
+ data: xml,
249
+ options: {
250
+ compression: "DEFLATE",
251
+ compressionOptions: { level: compressionLevel }
252
+ }
233
253
  });
234
254
  await transformPackagingFile(zip, "[Content_Types].xml", addCommentsExtendedOverride, compressionLevel);
235
255
  await transformPackagingFile(zip, "word/_rels/document.xml.rels", addCommentsExtendedRelationship, compressionLevel);
@@ -240,9 +260,14 @@ async function transformPackagingFile(zip, path, transform, compressionLevel) {
240
260
  if (!file) return;
241
261
  const xml = await file.async("text");
242
262
  const next = transform(xml);
243
- if (next !== xml) zip.file(path, next, {
244
- compression: "DEFLATE",
245
- compressionOptions: { level: compressionLevel }
263
+ if (next !== xml) writeZipPart({
264
+ zip,
265
+ path,
266
+ data: next,
267
+ options: {
268
+ compression: "DEFLATE",
269
+ compressionOptions: { level: compressionLevel }
270
+ }
246
271
  });
247
272
  }
248
273
  function findZipEntryCaseInsensitive(zip, lowerPath) {
@@ -251,7 +276,9 @@ function findZipEntryCaseInsensitive(zip, lowerPath) {
251
276
  for (const [path, file] of Object.entries(zip.files)) if (!file.dir && path.toLowerCase() === lowerPath) return file;
252
277
  return null;
253
278
  }
254
- const EMPTY_RELS_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"></Relationships>";
279
+ const PACKAGE_RELATIONSHIPS_NAMESPACE_URI = "http://schemas.openxmlformats.org/package/2006/relationships";
280
+ const EMPTY_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
281
+ <Relationships xmlns="${PACKAGE_RELATIONSHIPS_NAMESPACE_URI}"></Relationships>`;
255
282
  function headerFooterFilename(target) {
256
283
  return target.startsWith("/") ? target.slice(1) : `word/${target}`;
257
284
  }
@@ -348,9 +375,14 @@ async function registerImageExtensions(zip, extensions, compressionLevel) {
348
375
  changed = true;
349
376
  }
350
377
  if (!changed) return;
351
- zip.file("[Content_Types].xml", ctXml, {
352
- compression: "DEFLATE",
353
- compressionOptions: { level: compressionLevel }
378
+ writeZipPart({
379
+ zip,
380
+ path: "[Content_Types].xml",
381
+ data: ctXml,
382
+ options: {
383
+ compression: "DEFLATE",
384
+ compressionOptions: { level: compressionLevel }
385
+ }
354
386
  });
355
387
  }
356
388
  function collectNewImages(blocks) {
@@ -421,9 +453,14 @@ async function processNewImages(parts, zip, compressionLevel) {
421
453
  maxId++;
422
454
  const mediaPath = `word/media/${`image${maxImageNum}.${extension}`}`;
423
455
  const newRId = mintRelationshipId(maxId);
424
- zip.file(mediaPath, data, {
425
- compression: "DEFLATE",
426
- compressionOptions: { level: compressionLevel }
456
+ writeZipPart({
457
+ zip,
458
+ path: mediaPath,
459
+ data,
460
+ options: {
461
+ compression: "DEFLATE",
462
+ compressionOptions: { level: compressionLevel }
463
+ }
427
464
  });
428
465
  relEntries.push(`<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.image}" Target="${escapeXmlAttribute(relativeTargetForPart(partPath, mediaPath))}"/>`);
429
466
  extensionsAdded.add(extension);
@@ -434,13 +471,15 @@ async function processNewImages(parts, zip, compressionLevel) {
434
471
  }) ?? panic("A detached drawing lost its embedded image relationship");
435
472
  image.rId = newRId;
436
473
  }
437
- if (relEntries.length > 0) {
438
- const updatedRelsXml = relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`);
439
- zip.file(relsPath, updatedRelsXml, {
474
+ if (relEntries.length > 0) writeZipPart({
475
+ zip,
476
+ path: relsPath,
477
+ data: relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`),
478
+ options: {
440
479
  compression: "DEFLATE",
441
480
  compressionOptions: { level: compressionLevel }
442
- });
443
- }
481
+ }
482
+ });
444
483
  }
445
484
  await registerImageExtensions(zip, extensionsAdded, compressionLevel);
446
485
  }
@@ -452,67 +491,259 @@ const materializeEmbeddedMedia = async ({ document, zip, compressionLevel }) =>
452
491
  if (isUnsafePackagePath(media.path) || !media.path.startsWith("word/media/")) panic("Detached media has an invalid package path");
453
492
  const extension = media.path.split(".").at(-1)?.toLowerCase();
454
493
  if (!extension || !/^[a-z0-9]+$/u.test(extension)) panic("Embedded media has an invalid extension");
455
- zip.file(media.path, media.data, {
456
- compression: "DEFLATE",
457
- compressionOptions: { level: compressionLevel }
494
+ writeZipPart({
495
+ zip,
496
+ path: media.path,
497
+ data: media.data,
498
+ options: {
499
+ compression: "DEFLATE",
500
+ compressionOptions: { level: compressionLevel }
501
+ }
458
502
  });
459
503
  extensions.add(extension);
460
504
  }
461
505
  await registerImageExtensions(zip, extensions, compressionLevel);
462
506
  };
463
- /**
464
- * Collect all hyperlinks that have an href but no rId from block content.
465
- * These are newly created hyperlinks that need relationship entries.
466
- */
467
- function collectHyperlinksWithoutRId(blocks) {
468
- const hyperlinks = [];
469
- const collectInlineHyperlinks = (content) => {
470
- for (const item of content) switch (item.type) {
471
- case "hyperlink":
472
- if (item.href && !item.rId && !item.anchor) hyperlinks.push(item);
473
- break;
474
- case "simpleField":
475
- case "inlineSdt":
476
- case "inlineWrapper":
477
- case "insertion":
478
- case "deletion":
479
- case "moveFrom":
480
- case "moveTo":
481
- collectInlineHyperlinks(item.content);
482
- break;
483
- default: break;
507
+ var CanonicalImageRelationshipRefusalError = class extends TaggedError("CanonicalImageRelationshipRefusalError") {};
508
+ /** Publish allocated pictures into each owning story without rebinding canonical ids. */
509
+ const publishCanonicalImageResources = async ({ document, zip, compressionLevel }) => {
510
+ const baseline = getDocumentSourceBaseline(document);
511
+ for (const { relsPath, blocks } of collectDocxParts(document, zip)) {
512
+ const images = [];
513
+ visitBlockTreeRecords(blocks, (record) => {
514
+ if (record.type !== "paragraph") return BLOCK_TREE_DESCENT.descend;
515
+ visitParagraphRuns(record, (run) => {
516
+ for (const content of run.content) if (content.type === "drawing" && content.image.rId !== void 0) images.push(content.image);
517
+ });
518
+ return BLOCK_TREE_DESCENT.descend;
519
+ });
520
+ if (images.length === 0) continue;
521
+ let xml = await readRelsOrStub(zip, relsPath);
522
+ const local = parseRelationships(xml);
523
+ const partPath = relsPath.replace("/_rels/", "/").replace(/\.rels$/u, "");
524
+ let changed = false;
525
+ for (const image of images) {
526
+ const id = image.rId;
527
+ if (id === void 0) continue;
528
+ const refuse = (reason, message) => {
529
+ throw new CanonicalImageRelationshipRefusalError({
530
+ message,
531
+ gap: CANONICAL_GAP.save,
532
+ part: relsPath,
533
+ relationshipId: id,
534
+ reason
535
+ });
536
+ };
537
+ const source = local.get(id);
538
+ const allocated = document.package.relationships?.get(id);
539
+ const allocatedMediaPath = allocated?.targetMode === "External" || allocated?.type !== RELATIONSHIP_TYPES.image ? void 0 : resolveRelativePath("word/_rels/document.xml.rels", allocated.target);
540
+ const allocatedMedia = allocatedMediaPath === void 0 ? void 0 : document.package.media?.get(allocatedMediaPath);
541
+ const newlyAllocated = allocated?.type === RELATIONSHIP_TYPES.image && (baseline.type !== "captured" || canonicalJson(allocated) !== canonicalJson(baseline.resourceRelationships?.get(id)));
542
+ const desired = relsPath === "word/_rels/document.xml.rels" || newlyAllocated || source === void 0 && image.src !== void 0 && (allocated?.targetMode === "External" ? image.src === allocated.target : image.src === allocatedMedia?.dataUrl) ? allocated : source;
543
+ if (desired?.type !== RELATIONSHIP_TYPES.image) return refuse("unresolved", "A canonical inline picture has no owning image relationship.");
544
+ const desiredRelsPath = desired === allocated ? "word/_rels/document.xml.rels" : relsPath;
545
+ const absolute = desired.targetMode === "External" ? void 0 : resolveRelativePath(desiredRelsPath, desired.target);
546
+ if (desired.targetMode === "External" && !isAllowedExternalWatermarkImageUrl(desired.target)) return refuse("unsafeTarget", "A canonical inline picture has an unsafe external target.");
547
+ if (absolute !== void 0 && isUnsafePackagePath(absolute)) return refuse("unsafeTarget", "A canonical inline picture has an unsafe media target.");
548
+ if (source) {
549
+ const sourceAbsolute = source.targetMode === "External" ? void 0 : resolveRelativePath(relsPath, source.target);
550
+ if (source.type !== RELATIONSHIP_TYPES.image || source.targetMode === "External" !== (desired.targetMode === "External") || (absolute === void 0 ? source.target !== desired.target : sourceAbsolute !== absolute)) return refuse("conflict", "A canonical inline picture relationship conflicts with its owning source binding.");
551
+ }
552
+ if (absolute !== void 0 && !findZipEntryCaseInsensitive(zip, absolute.toLowerCase())) {
553
+ const media = document.package.media?.get(absolute);
554
+ if (!media) return refuse("missingMedia", "A canonical inline picture is missing its media bytes.");
555
+ if (isUnsafePackagePath(media.path) || media.path !== absolute) return refuse("unsafeTarget", "A canonical inline picture media path does not match its relationship.");
556
+ writeZipPart({
557
+ zip,
558
+ path: absolute,
559
+ data: media.data,
560
+ options: {
561
+ compression: "DEFLATE",
562
+ compressionOptions: { level: compressionLevel }
563
+ }
564
+ });
565
+ const extension = absolute.split(".").at(-1)?.toLowerCase();
566
+ if (!extension || !/^[a-z0-9]+$/u.test(extension)) return refuse("unsafeTarget", "A canonical inline picture media extension is invalid.");
567
+ await registerImageExtensions(zip, /* @__PURE__ */ new Set([extension]), compressionLevel);
568
+ }
569
+ if (source) continue;
570
+ const root = parseXmlDocument(xml);
571
+ const type = root !== null && getChildElements(root).some((element) => getAttribute(element, null, "Type")?.startsWith("http://purl.oclc.org/ooxml/officeDocument/relationships/")) ? "http://purl.oclc.org/ooxml/officeDocument/relationships/image" : RELATIONSHIP_TYPES.image;
572
+ const target = absolute === void 0 ? desired.target : relativeTargetForPart(partPath, absolute);
573
+ xml = appendCanonicalRelationship({
574
+ xml,
575
+ partPath: relsPath,
576
+ relationshipXml: `<Relationship Id="${escapeXmlAttribute(id)}" Type="${type}" Target="${escapeXmlAttribute(target)}"${desired.targetMode === "External" ? " TargetMode=\"External\"" : ""}/>`
577
+ });
578
+ local.set(id, {
579
+ id,
580
+ type: RELATIONSHIP_TYPES.image,
581
+ target,
582
+ ...desired.targetMode === "External" ? { targetMode: "External" } : {}
583
+ });
584
+ changed = true;
484
585
  }
485
- };
486
- for (const block of blocks) if (block.type === "paragraph") collectInlineHyperlinks(block.content);
487
- else if (block.type === "table") for (const row of block.rows) for (const cell of row.cells) hyperlinks.push(...collectHyperlinksWithoutRId(cell.content));
488
- else if (block.type === "blockSdt" || block.type === "blockCustomXml") hyperlinks.push(...collectHyperlinksWithoutRId(block.content));
489
- return hyperlinks;
586
+ if (changed) writeZipPart({
587
+ zip,
588
+ path: relsPath,
589
+ data: xml,
590
+ options: {
591
+ compression: "DEFLATE",
592
+ compressionOptions: { level: compressionLevel }
593
+ }
594
+ });
595
+ }
596
+ };
597
+ const collectExternalHyperlinkResources = (blocks) => {
598
+ const resources = [];
599
+ visitBlockTreeRecords(blocks, (record) => {
600
+ if (record.type !== "paragraph") return BLOCK_TREE_DESCENT.descend;
601
+ visitInlineContentSlots(record, ({ item }) => {
602
+ if (item.type === "hyperlink" && item.href && !item.anchor) resources.push({
603
+ type: "text",
604
+ hyperlink: item
605
+ });
606
+ });
607
+ visitParagraphRuns(record, (run) => {
608
+ for (const drawing of run.content) if (drawing.type === "drawing" && drawing.rawXmlMode === void 0 && drawing.image.hlinkHref) resources.push({
609
+ type: "drawing",
610
+ image: drawing.image,
611
+ drawing
612
+ });
613
+ });
614
+ return BLOCK_TREE_DESCENT.descend;
615
+ });
616
+ return resources;
617
+ };
618
+ const externalHyperlinkTarget = (resource) => {
619
+ switch (resource.type) {
620
+ case "text": return {
621
+ id: resource.hyperlink.rId,
622
+ href: resource.hyperlink.href
623
+ };
624
+ case "drawing": return {
625
+ id: resource.image.hlinkRId,
626
+ href: resource.image.hlinkHref
627
+ };
628
+ default: return resource;
629
+ }
630
+ };
631
+ /** Newly authored text links; the census shares the complete owning-part traversal. */
632
+ function collectHyperlinksWithoutRId(blocks) {
633
+ return collectExternalHyperlinkResources(blocks).flatMap((resource) => resource.type === "text" && !resource.hyperlink.rId ? [resource.hyperlink] : []);
490
634
  }
491
635
  /** The selective save boundary must use the same resource census as full repack. */
492
- const hasUnmaterializedInlineResources = (blocks) => collectNewImages(blocks).length > 0 || collectHyperlinksWithoutRId(blocks).length > 0;
493
- /**
494
- * Process newly created hyperlinks in every part: assign rIds and add
495
- * relationship entries to the owning part's rels. Mutates the hyperlinks' rId
496
- * fields in-place.
497
- */
636
+ const hasUnmaterializedInlineResources = (blocks) => collectNewImages(blocks).length > 0 || collectExternalHyperlinkResources(blocks).some((resource) => externalHyperlinkTarget(resource).id === void 0);
637
+ const matchesExternalHyperlink = (relation, href) => relation?.type === RELATIONSHIP_TYPES.hyperlink && relation.targetMode === "External" && relation.target === href;
638
+ /** Compare against the already loaded source ZIP, without mutating model or package. */
639
+ const hasUnmaterializedHyperlinkBindings = async (document, zip) => {
640
+ for (const { blocks, relsPath } of collectDocxParts(document, zip)) {
641
+ const resources = collectExternalHyperlinkResources(blocks);
642
+ if (resources.length === 0) continue;
643
+ const relationships = parseRelationships(await readRelsOrStub(zip, relsPath));
644
+ for (const resource of resources) {
645
+ const { id, href } = externalHyperlinkTarget(resource);
646
+ if (href && (id === void 0 || !matchesExternalHyperlink(relationships.get(id), href))) return true;
647
+ }
648
+ }
649
+ return false;
650
+ };
651
+ const rebindCapturedDrawingLink = ({ xml, previousId, nextId }) => {
652
+ const parsed = Result.try({
653
+ try: () => parseXml(xml, OOXML_NAMESPACE_SCOPE),
654
+ catch: () => new DocxPackageFidelityError("Cannot rebind invalid captured drawing hyperlink XML.")
655
+ });
656
+ if (parsed.isErr()) throw parsed.error;
657
+ let rebound = 0;
658
+ const visit = (element) => {
659
+ const attribute = findAttributeByNamespaceUri(element, OFFICE_RELATIONSHIP_NAMESPACE_URIS, "id");
660
+ if (attribute?.value === previousId && element.attributes !== void 0) {
661
+ element.attributes[attribute.name] = nextId;
662
+ rebound += 1;
663
+ }
664
+ for (const child of element.elements ?? []) visit(child);
665
+ };
666
+ visit(parsed.value);
667
+ if (rebound !== 1) throw new DocxPackageFidelityError("Captured drawing hyperlink must own exactly one click relationship.");
668
+ return (parsed.value.elements ?? []).map(captureVerbatimXml).join("");
669
+ };
670
+ /** Materialize each modeled hyperlink against the actual owning ZIP relationship table. */
498
671
  async function processNewHyperlinks(parts, zip, compressionLevel) {
499
672
  for (const { relsPath, blocks } of parts) {
500
- const newHyperlinks = collectHyperlinksWithoutRId(blocks);
501
- if (newHyperlinks.length === 0) continue;
673
+ const resources = collectExternalHyperlinkResources(blocks);
674
+ if (resources.length === 0) continue;
502
675
  const relsXml = await readRelsOrStub(zip, relsPath);
676
+ const relationships = parseRelationships(relsXml);
677
+ const root = parseXmlDocument(relsXml);
678
+ if (root?.name === void 0 || getLocalName(root.name) !== "Relationships" || getNamespaceUri(root) !== PACKAGE_RELATIONSHIPS_NAMESPACE_URI) throw new DocxPackageFidelityError("Cannot materialize hyperlinks into an invalid relationship part.");
679
+ const strictRelationshipNamespace = "http://purl.oclc.org/ooxml/officeDocument/relationships";
680
+ const relationshipType = getChildElements(root).some((child) => getAttribute(child, null, "Type")?.startsWith(`${strictRelationshipNamespace}/`)) ? `${strictRelationshipNamespace}/hyperlink` : RELATIONSHIP_TYPES.hyperlink;
681
+ const byTarget = /* @__PURE__ */ new Map();
682
+ for (const relation of relationships.values()) if (relation.type === RELATIONSHIP_TYPES.hyperlink && relation.targetMode === "External" && !byTarget.has(relation.target)) byTarget.set(relation.target, relation.id);
503
683
  let maxId = findMaxRId(relsXml);
504
684
  const relEntries = [];
505
- for (const hyperlink of newHyperlinks) {
506
- if (!hyperlink.href) continue;
507
- maxId++;
508
- const newRId = `rId${maxId}`;
509
- relEntries.push(`<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.hyperlink}" Target="${escapeXmlAttribute(hyperlink.href)}" TargetMode="External"/>`);
510
- hyperlink.rId = newRId;
685
+ for (const resource of resources) {
686
+ const { id, href } = externalHyperlinkTarget(resource);
687
+ if (!href || id !== void 0 && matchesExternalHyperlink(relationships.get(id), href)) continue;
688
+ let nextId = byTarget.get(href);
689
+ if (nextId === void 0) {
690
+ if (id !== void 0 && !relationships.has(id)) nextId = id;
691
+ else do {
692
+ maxId += 1;
693
+ nextId = `rId${maxId}`;
694
+ } while (relationships.has(nextId));
695
+ relationships.set(nextId, {
696
+ id: nextId,
697
+ type: RELATIONSHIP_TYPES.hyperlink,
698
+ target: href,
699
+ targetMode: "External"
700
+ });
701
+ byTarget.set(href, nextId);
702
+ relEntries.push({
703
+ type: "element",
704
+ name: `${root.name.slice(0, -13)}Relationship`,
705
+ attributes: {
706
+ Id: nextId,
707
+ Type: relationshipType,
708
+ Target: href,
709
+ TargetMode: "External"
710
+ }
711
+ });
712
+ }
713
+ switch (resource.type) {
714
+ case "text":
715
+ resource.hyperlink.rId = nextId;
716
+ break;
717
+ case "drawing": {
718
+ const source = resource.image.hlinkClickSource;
719
+ if (source !== void 0 && source.rId === id && id !== void 0) resource.image.hlinkClickSource = {
720
+ xml: rebindCapturedDrawingLink({
721
+ xml: source.xml,
722
+ previousId: id,
723
+ nextId
724
+ }),
725
+ rId: nextId
726
+ };
727
+ resource.image.hlinkRId = nextId;
728
+ delete resource.drawing.rawXml;
729
+ delete resource.drawing.rawImageFingerprint;
730
+ break;
731
+ }
732
+ default: panic(`Unsupported hyperlink resource ${JSON.stringify(resource)}`);
733
+ }
734
+ }
735
+ if (relEntries.length > 0) {
736
+ (root.elements ??= []).push(...relEntries);
737
+ writeZipPart({
738
+ zip,
739
+ path: relsPath,
740
+ data: captureVerbatimXml(root),
741
+ options: {
742
+ compression: "DEFLATE",
743
+ compressionOptions: { level: compressionLevel }
744
+ }
745
+ });
511
746
  }
512
- zip.file(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), {
513
- compression: "DEFLATE",
514
- compressionOptions: { level: compressionLevel }
515
- });
516
747
  }
517
748
  }
518
749
  /**
@@ -537,9 +768,14 @@ const normalizePackageIdsInZip = async (zip, compressionLevel) => {
537
768
  const normalizedParts = normalizeParaIdRangeInXmlParts(normalizeRevisionIdsInXmlParts(xmlParts));
538
769
  for (const [path, xml] of normalizedParts) if (xml !== xmlParts.get(path)) {
539
770
  assertValidOoxmlNumericIds(xml, path);
540
- zip.file(path, xml, {
541
- compression: "DEFLATE",
542
- compressionOptions: { level: compressionLevel }
771
+ writeZipPart({
772
+ zip,
773
+ path,
774
+ data: xml,
775
+ options: {
776
+ compression: "DEFLATE",
777
+ compressionOptions: { level: compressionLevel }
778
+ }
543
779
  });
544
780
  }
545
781
  };
@@ -554,9 +790,14 @@ const normalizeAppVersionInZip = async (zip, compressionLevel) => {
554
790
  if (!extendedProperties) return;
555
791
  const xml = await extendedProperties.async("text");
556
792
  const normalized = normalizeAppVersionInExtendedProperties(xml);
557
- if (normalized !== xml) zip.file(EXTENDED_PROPERTIES_PATH, normalized, {
558
- compression: "DEFLATE",
559
- compressionOptions: { level: compressionLevel }
793
+ if (normalized !== xml) writeZipPart({
794
+ zip,
795
+ path: EXTENDED_PROPERTIES_PATH,
796
+ data: normalized,
797
+ options: {
798
+ compression: "DEFLATE",
799
+ compressionOptions: { level: compressionLevel }
800
+ }
560
801
  });
561
802
  };
562
803
  /**
@@ -635,20 +876,37 @@ const normalizeExportDrawingIds = ({ package: docxPackage }) => {
635
876
  ...docxPackage.endnotes !== void 0 ? { endnotes: docxPackage.endnotes } : {}
636
877
  });
637
878
  };
638
- const finishRepack = async ({ document, originalZip, outputZip, originalDocument, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, sectionEndpointRemoval, sectionReferenceRemovals }) => {
639
- await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
879
+ /** Keep export mutations off authority while retaining the source-buffer cache key. */
880
+ const cloneExportDocument = (sourceDocument) => {
881
+ const { originalBuffer, ...graph } = sourceDocument;
882
+ const document = cloneDocumentWithParagraphPropertySources(graph);
883
+ if (originalBuffer !== void 0) document.originalBuffer = originalBuffer;
884
+ copyDocumentParagraphPropertySourceContract(document, sourceDocument);
885
+ return document;
886
+ };
887
+ const finishRepack = async ({ document: sourceDocument, originalZip, outputZip, originalDocument, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, onDiagnostic, bodyAuthority, sectionEndpointRemoval, sectionReferenceRemovals }) => {
888
+ const document = await materializeNewHeaderFooterParts(cloneExportDocument(sourceDocument), outputZip, compressionLevel);
640
889
  await materializeEmbeddedMedia({
641
890
  document,
642
891
  zip: outputZip,
643
892
  compressionLevel
644
893
  });
894
+ if (bodyAuthority === "canonical") await publishCanonicalImageResources({
895
+ document,
896
+ zip: outputZip,
897
+ compressionLevel
898
+ });
645
899
  const parts = collectDocxParts(document, outputZip);
646
900
  normalizeExportDrawingIds(document);
647
901
  await processNewImages(parts, outputZip, compressionLevel);
648
902
  await processNewHyperlinks(parts, outputZip, compressionLevel);
649
903
  assertValidFolioDocumentModel(document, "Cannot repack invalid DOCX document model");
650
904
  applyReplyThreadMarkers(document);
651
- const documentXml = serializeDocument(document, originalDocument === void 0 ? void 0 : readRootNamespaceBindings(originalDocument.xml));
905
+ const documentXml = serializeDocument(document, originalDocument === void 0 ? void 0 : readRootNamespaceBindings(originalDocument.xml), {
906
+ xml: originalDocument?.xml,
907
+ onDiagnostic,
908
+ bodyAuthority
909
+ });
652
910
  if (originalDocument?.xml) assertDocumentPackageFidelity({
653
911
  originalDocumentFacts: originalDocument.sectionFacts(),
654
912
  serializedDocumentXml: documentXml,
@@ -662,12 +920,27 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
662
920
  removedReferences: [...sectionReferenceRemovals ?? [], ...sectionEndpointRemoval?.removedReferences ?? []],
663
921
  compressionLevel
664
922
  });
665
- outputZip.file("word/document.xml", documentXml, {
666
- compression: "DEFLATE",
667
- compressionOptions: { level: compressionLevel }
923
+ writeZipPart({
924
+ zip: outputZip,
925
+ path: "word/document.xml",
926
+ data: documentXml,
927
+ options: {
928
+ compression: "DEFLATE",
929
+ compressionOptions: { level: compressionLevel }
930
+ }
931
+ });
932
+ await rebindWatermarkRelIds({
933
+ document,
934
+ zip: outputZip,
935
+ compressionLevel,
936
+ bodyAuthority
937
+ });
938
+ await serializeHeadersFootersToZip({
939
+ doc: document,
940
+ zip: outputZip,
941
+ compressionLevel,
942
+ onDiagnostic
668
943
  });
669
- await rebindWatermarkRelIds(document, outputZip, compressionLevel);
670
- await serializeHeadersFootersToZip(document, outputZip, compressionLevel);
671
944
  await serializeNotesToZip({
672
945
  doc: document,
673
946
  originalZip,
@@ -677,18 +950,21 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
677
950
  });
678
951
  await serializeNumberingIntoZip(document, originalZip, outputZip, compressionLevel);
679
952
  await serializeAddedStylesIntoZip(document, originalZip, outputZip, compressionLevel);
953
+ await serializeHeaderFooterSettingsIntoZip(document, outputZip, compressionLevel);
680
954
  await serializeCommentsToZip(document, outputZip, compressionLevel);
681
955
  await dropAttachedTemplateReference(outputZip, compressionLevel);
682
- if (updateModifiedDate && originalCorePropertiesXml) {
683
- const updatedCoreProperties = updateCoreProperties(originalCorePropertiesXml, {
956
+ if (updateModifiedDate && originalCorePropertiesXml) writeZipPart({
957
+ zip: outputZip,
958
+ path: "docProps/core.xml",
959
+ data: updateCoreProperties(originalCorePropertiesXml, {
684
960
  updateModifiedDate,
685
961
  ...modifiedBy !== void 0 ? { modifiedBy } : {}
686
- });
687
- outputZip.file("docProps/core.xml", updatedCoreProperties, {
962
+ }),
963
+ options: {
688
964
  compression: "DEFLATE",
689
965
  compressionOptions: { level: compressionLevel }
690
- });
691
- }
966
+ }
967
+ });
692
968
  return generateDocxZip(outputZip, compressionLevel);
693
969
  };
694
970
  async function repackDocxWithSectionEndpointRemoval({ document: doc, options, sectionEndpointRemoval, sectionReferenceRemovals }) {
@@ -707,6 +983,8 @@ async function repackDocxWithSectionEndpointRemoval({ document: doc, options, se
707
983
  originalCorePropertiesXml,
708
984
  compressionLevel,
709
985
  updateModifiedDate,
986
+ onDiagnostic: options.onDiagnostic,
987
+ bodyAuthority: options.bodyAuthority,
710
988
  ...modifiedBy !== void 0 ? { modifiedBy } : {},
711
989
  ...changedNoteParaIds !== void 0 ? { changedNoteParaIds } : {},
712
990
  ...sectionEndpointRemoval !== void 0 ? { sectionEndpointRemoval } : {},
@@ -733,44 +1011,81 @@ function repackDocx(doc, options = {}) {
733
1011
  */
734
1012
  async function repackDocxFromRaw(doc, rawContent, options = {}) {
735
1013
  const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
736
- const exportDocument = withoutOrphanCommentRanges(doc);
1014
+ let exportDocument = withoutOrphanCommentRanges(cloneExportDocument(doc));
737
1015
  const newZip = new JSZip();
738
1016
  for (const [path, file] of Object.entries(rawContent.originalZip.files)) {
739
1017
  if (file.dir) {
740
- newZip.folder(path.replace(/\/$/u, ""));
1018
+ writeZipPart({
1019
+ zip: newZip,
1020
+ path,
1021
+ data: "",
1022
+ options: {
1023
+ dir: true,
1024
+ date: file.date
1025
+ }
1026
+ });
741
1027
  continue;
742
1028
  }
743
1029
  if (isUnsafePackagePath(path)) continue;
744
- const content = await file.async("arraybuffer");
745
- newZip.file(path, content, {
746
- compression: "DEFLATE",
747
- compressionOptions: { level: compressionLevel }
1030
+ writeZipPart({
1031
+ zip: newZip,
1032
+ path,
1033
+ data: await file.async("arraybuffer"),
1034
+ options: {
1035
+ compression: "DEFLATE",
1036
+ compressionOptions: { level: compressionLevel }
1037
+ }
748
1038
  });
749
1039
  }
750
- await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
1040
+ exportDocument = await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
751
1041
  await materializeEmbeddedMedia({
752
1042
  document: exportDocument,
753
1043
  zip: newZip,
754
1044
  compressionLevel
755
1045
  });
1046
+ if (options.bodyAuthority === "canonical") await publishCanonicalImageResources({
1047
+ document: exportDocument,
1048
+ zip: newZip,
1049
+ compressionLevel
1050
+ });
756
1051
  const parts = collectDocxParts(exportDocument, newZip);
757
1052
  normalizeExportDrawingIds(exportDocument);
758
1053
  await processNewImages(parts, newZip, compressionLevel);
759
1054
  await processNewHyperlinks(parts, newZip, compressionLevel);
760
1055
  assertValidFolioDocumentModel(exportDocument, "Cannot repack invalid DOCX document model");
761
1056
  applyReplyThreadMarkers(exportDocument);
762
- const documentXml = serializeDocument(exportDocument, rawContent.documentXml ? readRootNamespaceBindings(rawContent.documentXml) : void 0);
1057
+ const documentXml = serializeDocument(exportDocument, rawContent.documentXml ? readRootNamespaceBindings(rawContent.documentXml) : void 0, {
1058
+ xml: rawContent.documentXml ?? void 0,
1059
+ onDiagnostic: options.onDiagnostic,
1060
+ bodyAuthority: options.bodyAuthority
1061
+ });
763
1062
  if (rawContent.documentXml) assertDocumentPackageFidelity({
764
1063
  originalDocumentFacts: readDocumentSectionFacts(rawContent.documentXml),
765
1064
  serializedDocumentXml: documentXml,
766
1065
  doc: exportDocument
767
1066
  });
768
- newZip.file("word/document.xml", documentXml, {
769
- compression: "DEFLATE",
770
- compressionOptions: { level: compressionLevel }
1067
+ writeZipPart({
1068
+ zip: newZip,
1069
+ path: "word/document.xml",
1070
+ data: documentXml,
1071
+ options: {
1072
+ compression: "DEFLATE",
1073
+ compressionOptions: { level: compressionLevel }
1074
+ }
1075
+ });
1076
+ await rebindWatermarkRelIds({
1077
+ document: exportDocument,
1078
+ zip: newZip,
1079
+ compressionLevel,
1080
+ bodyAuthority: options.bodyAuthority
771
1081
  });
772
- await rebindWatermarkRelIds(exportDocument, newZip, compressionLevel);
773
- await serializeHeadersFootersToZip(exportDocument, newZip, compressionLevel);
1082
+ await serializeHeadersFootersToZip({
1083
+ doc: exportDocument,
1084
+ zip: newZip,
1085
+ compressionLevel,
1086
+ onDiagnostic: options.onDiagnostic
1087
+ });
1088
+ await serializeHeaderFooterSettingsIntoZip(exportDocument, newZip, compressionLevel);
774
1089
  await serializeNotesToZip({
775
1090
  doc: exportDocument,
776
1091
  originalZip: rawContent.originalZip,
@@ -782,16 +1097,18 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
782
1097
  await serializeAddedStylesIntoZip(exportDocument, rawContent.originalZip, newZip, compressionLevel);
783
1098
  await serializeCommentsToZip(exportDocument, newZip, compressionLevel);
784
1099
  await dropAttachedTemplateReference(newZip, compressionLevel);
785
- if (updateModifiedDate && rawContent.corePropsXml) {
786
- const updatedCoreProps = updateCoreProperties(rawContent.corePropsXml, {
1100
+ if (updateModifiedDate && rawContent.corePropsXml) writeZipPart({
1101
+ zip: newZip,
1102
+ path: "docProps/core.xml",
1103
+ data: updateCoreProperties(rawContent.corePropsXml, {
787
1104
  updateModifiedDate,
788
1105
  ...modifiedBy !== void 0 ? { modifiedBy } : {}
789
- });
790
- newZip.file("docProps/core.xml", updatedCoreProps, {
1106
+ }),
1107
+ options: {
791
1108
  compression: "DEFLATE",
792
1109
  compressionOptions: { level: compressionLevel }
793
- });
794
- }
1110
+ }
1111
+ });
795
1112
  return generateDocxZip(newZip, compressionLevel);
796
1113
  }
797
1114
  const COMMENTS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml";
@@ -880,13 +1197,23 @@ async function dropAttachedTemplateReference(zip, compressionLevel) {
880
1197
  if (!settingsFile) return;
881
1198
  const relsFile = zip.file(SETTINGS_RELS_PART);
882
1199
  const filtered = withoutAttachedTemplate(await settingsFile.async("text"), await relsFile?.async("text"));
883
- if (filtered.settingsXml !== void 0) zip.file(SETTINGS_PART, filtered.settingsXml, {
884
- compression: "DEFLATE",
885
- compressionOptions: { level: compressionLevel }
1200
+ if (filtered.settingsXml !== void 0) writeZipPart({
1201
+ zip,
1202
+ path: SETTINGS_PART,
1203
+ data: filtered.settingsXml,
1204
+ options: {
1205
+ compression: "DEFLATE",
1206
+ compressionOptions: { level: compressionLevel }
1207
+ }
886
1208
  });
887
- if (filtered.relsXml !== void 0) zip.file(SETTINGS_RELS_PART, filtered.relsXml, {
888
- compression: "DEFLATE",
889
- compressionOptions: { level: compressionLevel }
1209
+ if (filtered.relsXml !== void 0) writeZipPart({
1210
+ zip,
1211
+ path: SETTINGS_RELS_PART,
1212
+ data: filtered.relsXml,
1213
+ options: {
1214
+ compression: "DEFLATE",
1215
+ compressionOptions: { level: compressionLevel }
1216
+ }
890
1217
  });
891
1218
  }
892
1219
  /**
@@ -899,9 +1226,14 @@ async function ensureCommentsContentType(zip, compressionLevel) {
899
1226
  let ctXml = await ctFile.async("text");
900
1227
  if (ctXml.includes("/word/comments.xml")) return;
901
1228
  ctXml = ctXml.replace("</Types>", `<Override PartName="/word/comments.xml" ContentType="${COMMENTS_CONTENT_TYPE}"/></Types>`);
902
- zip.file("[Content_Types].xml", ctXml, {
903
- compression: "DEFLATE",
904
- compressionOptions: { level: compressionLevel }
1229
+ writeZipPart({
1230
+ zip,
1231
+ path: "[Content_Types].xml",
1232
+ data: ctXml,
1233
+ options: {
1234
+ compression: "DEFLATE",
1235
+ compressionOptions: { level: compressionLevel }
1236
+ }
905
1237
  });
906
1238
  }
907
1239
  /**
@@ -916,9 +1248,14 @@ async function ensureCommentsRelationship(zip, compressionLevel) {
916
1248
  if (relsXml.includes("comments.xml")) return;
917
1249
  const newRId = `rId${findMaxRId(relsXml) + 1}`;
918
1250
  relsXml = relsXml.replace("</Relationships>", `<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.comments}" Target="comments.xml"/></Relationships>`);
919
- zip.file(relsPath, relsXml, {
920
- compression: "DEFLATE",
921
- compressionOptions: { level: compressionLevel }
1251
+ writeZipPart({
1252
+ zip,
1253
+ path: relsPath,
1254
+ data: relsXml,
1255
+ options: {
1256
+ compression: "DEFLATE",
1257
+ compressionOptions: { level: compressionLevel }
1258
+ }
922
1259
  });
923
1260
  }
924
1261
  /**
@@ -932,9 +1269,14 @@ async function ensureCommentsRelationship(zip, compressionLevel) {
932
1269
  async function updateDocumentXml(originalBuffer, newDocumentXml, options = {}) {
933
1270
  const { compressionLevel = 6 } = options;
934
1271
  const zip = await JSZip.loadAsync(originalBuffer);
935
- zip.file("word/document.xml", newDocumentXml, {
936
- compression: "DEFLATE",
937
- compressionOptions: { level: compressionLevel }
1272
+ writeZipPart({
1273
+ zip,
1274
+ path: "word/document.xml",
1275
+ data: newDocumentXml,
1276
+ options: {
1277
+ compression: "DEFLATE",
1278
+ compressionOptions: { level: compressionLevel }
1279
+ }
938
1280
  });
939
1281
  return zip.generateAsync({
940
1282
  type: "arraybuffer",
@@ -954,9 +1296,14 @@ async function updateDocumentXml(originalBuffer, newDocumentXml, options = {}) {
954
1296
  async function updateXmlFile(originalBuffer, path, content, options = {}) {
955
1297
  const { compressionLevel = 6 } = options;
956
1298
  const zip = await JSZip.loadAsync(originalBuffer);
957
- zip.file(path, content, {
958
- compression: "DEFLATE",
959
- compressionOptions: { level: compressionLevel }
1299
+ writeZipPart({
1300
+ zip,
1301
+ path,
1302
+ data: content,
1303
+ options: {
1304
+ compression: "DEFLATE",
1305
+ compressionOptions: { level: compressionLevel }
1306
+ }
960
1307
  });
961
1308
  return zip.generateAsync({
962
1309
  type: "arraybuffer",
@@ -987,9 +1334,14 @@ async function updateMultipleFiles(originalBuffer, updates, options = {}) {
987
1334
  */
988
1335
  async function applyUpdatesToZip(zip, updates, options = {}) {
989
1336
  const { compressionLevel = 6 } = options;
990
- for (const [path, content] of updates) zip.file(path, content, {
991
- compression: "DEFLATE",
992
- compressionOptions: { level: compressionLevel }
1337
+ for (const [path, content] of updates) writeZipPart({
1338
+ zip,
1339
+ path,
1340
+ data: content,
1341
+ options: {
1342
+ compression: "DEFLATE",
1343
+ compressionOptions: { level: compressionLevel }
1344
+ }
993
1345
  });
994
1346
  await normalizePackageOnSave(zip, compressionLevel);
995
1347
  return await zip.generateAsync({
@@ -1014,8 +1366,11 @@ async function addRelationship(originalBuffer, relationship) {
1014
1366
  const newRId = `rId${findMaxRId(relsXml) + 1}`;
1015
1367
  const targetModeAttr = relationship.targetMode === "External" ? " TargetMode=\"External\"" : "";
1016
1368
  const newRelElement = `<Relationship Id="${newRId}" Type="${relationship.type}" Target="${escapeXmlAttribute(relationship.target)}"${targetModeAttr}/>`;
1017
- const updatedRelsXml = relsXml.replace("</Relationships>", `${newRelElement}</Relationships>`);
1018
- zip.file(relsPath, updatedRelsXml);
1369
+ writeZipPart({
1370
+ zip,
1371
+ path: relsPath,
1372
+ data: relsXml.replace("</Relationships>", `${newRelElement}</Relationships>`)
1373
+ });
1019
1374
  return {
1020
1375
  buffer: await zip.generateAsync({
1021
1376
  type: "arraybuffer",
@@ -1037,7 +1392,11 @@ async function addRelationship(originalBuffer, relationship) {
1037
1392
  async function addMedia(originalBuffer, filename, data, mimeType) {
1038
1393
  const zip = await JSZip.loadAsync(originalBuffer);
1039
1394
  const mediaPath = `word/media/${filename}`;
1040
- zip.file(mediaPath, data);
1395
+ writeZipPart({
1396
+ zip,
1397
+ path: mediaPath,
1398
+ data
1399
+ });
1041
1400
  const relResult = await addRelationship(await zip.generateAsync({ type: "arraybuffer" }), {
1042
1401
  type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
1043
1402
  target: `media/${filename}`
@@ -1050,7 +1409,11 @@ async function addMedia(originalBuffer, filename, data, mimeType) {
1050
1409
  const extensionElement = `<Default Extension="${extension}" ContentType="${getContentTypeForExtension(extension, mimeType)}"/>`;
1051
1410
  const updatedContentTypes = contentTypesXml.replace("</Types>", `${extensionElement}</Types>`);
1052
1411
  const finalZip = await JSZip.loadAsync(relResult.buffer);
1053
- finalZip.file("[Content_Types].xml", updatedContentTypes);
1412
+ writeZipPart({
1413
+ zip: finalZip,
1414
+ path: "[Content_Types].xml",
1415
+ data: updatedContentTypes
1416
+ });
1054
1417
  return {
1055
1418
  buffer: await finalZip.generateAsync({
1056
1419
  type: "arraybuffer",
@@ -1115,11 +1478,19 @@ function findMaxHeaderFooterNum(zip, prefix) {
1115
1478
  * relationship.
1116
1479
  */
1117
1480
  async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1118
- if (!hasUnmaterializedHeaderFooter(doc)) return;
1119
- doc.package.relationships ??= /* @__PURE__ */ new Map();
1120
- const rels = doc.package.relationships;
1481
+ if (!doc.package.headers?.size && !doc.package.footers?.size) return doc;
1482
+ doc = {
1483
+ ...doc,
1484
+ package: {
1485
+ ...doc.package,
1486
+ relationships: new Map(doc.package.relationships),
1487
+ ...doc.package.headers === void 0 ? {} : { headers: new Map(doc.package.headers) },
1488
+ ...doc.package.footers === void 0 ? {} : { footers: new Map(doc.package.footers) }
1489
+ }
1490
+ };
1491
+ const rels = doc.package.relationships ?? panic("The export owns its relationship map.");
1121
1492
  const relEntries = [];
1122
- const overrides = [];
1493
+ const contentTypeParts = [];
1123
1494
  let maxHeaderNum = findMaxHeaderFooterNum(zip, "header");
1124
1495
  let maxFooterNum = findMaxHeaderFooterNum(zip, "footer");
1125
1496
  const relsPath = "word/_rels/document.xml.rels";
@@ -1142,8 +1513,18 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1142
1513
  const materialize = (map, relType, prefix, contentType, isHeader) => {
1143
1514
  if (!map) return;
1144
1515
  for (const rId of [...map.keys()]) {
1145
- const existing = rels.get(rId) ?? zipRels.get(rId);
1146
- if (existing && existing.type === relType && existing.target) continue;
1516
+ const registered = zipRels.get(rId);
1517
+ const existing = registered ?? rels.get(rId);
1518
+ if (existing && existing.type === relType && existing.target) {
1519
+ rels.set(rId, existing);
1520
+ const path = resolveRelativePath(relsPath, existing.target);
1521
+ contentTypeParts.push({
1522
+ partName: `/${path}`,
1523
+ contentType
1524
+ });
1525
+ if (!registered) relEntries.push(`<Relationship Id="${escapeXmlAttribute(rId)}" Type="${relType}" Target="${escapeXmlAttribute(existing.target)}"/>`);
1526
+ continue;
1527
+ }
1147
1528
  let effectiveRId = rId;
1148
1529
  if (existing) {
1149
1530
  effectiveRId = `rId${++maxRId}`;
@@ -1152,6 +1533,12 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1152
1533
  map.delete(rId);
1153
1534
  map.set(effectiveRId, headerFooter);
1154
1535
  }
1536
+ const body = doc.package.document;
1537
+ doc.package.document = {
1538
+ ...body,
1539
+ content: body.content.map((block) => block.type === "paragraph" && block.sectionProperties !== void 0 ? cloneParagraphWithPropertySource(block, { sectionProperties: structuredClone(block.sectionProperties) }) : block),
1540
+ ...body.finalSectionProperties === void 0 ? {} : { finalSectionProperties: structuredClone(body.finalSectionProperties) }
1541
+ };
1155
1542
  for (const block of doc.package.document.content) if (block.type === "paragraph") remapRefs(isHeader ? block.sectionProperties?.headerReferences : block.sectionProperties?.footerReferences, rId, effectiveRId);
1156
1543
  const finalProps = doc.package.document.finalSectionProperties;
1157
1544
  remapRefs(isHeader ? finalProps?.headerReferences : finalProps?.footerReferences, rId, effectiveRId);
@@ -1163,32 +1550,39 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1163
1550
  target: filename
1164
1551
  });
1165
1552
  relEntries.push(`<Relationship Id="${escapeXmlAttribute(effectiveRId)}" Type="${relType}" Target="${filename}"/>`);
1166
- overrides.push(`<Override PartName="/word/${filename}" ContentType="${contentType}"/>`);
1553
+ contentTypeParts.push({
1554
+ partName: `/word/${filename}`,
1555
+ contentType
1556
+ });
1167
1557
  }
1168
1558
  };
1169
1559
  materialize(doc.package.headers, RELATIONSHIP_TYPES.header, "header", HEADER_CONTENT_TYPE, true);
1170
1560
  materialize(doc.package.footers, RELATIONSHIP_TYPES.footer, "footer", FOOTER_CONTENT_TYPE, false);
1171
- if (relEntries.length === 0) return;
1172
1561
  const compressionOptions = { level: compressionLevel };
1173
- zip.file(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), {
1174
- compression: "DEFLATE",
1175
- compressionOptions
1562
+ if (relEntries.length > 0) writeZipPart({
1563
+ zip,
1564
+ path: relsPath,
1565
+ data: relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`),
1566
+ options: {
1567
+ compression: "DEFLATE",
1568
+ compressionOptions
1569
+ }
1176
1570
  });
1177
1571
  const ctFile = zip.file("[Content_Types].xml");
1178
1572
  if (ctFile) {
1179
- let ctXml = await ctFile.async("text");
1180
- const missing = overrides.filter((override) => {
1181
- const partName = /PartName="(?<partName>[^"]+)"/u.exec(override)?.groups?.["partName"];
1182
- return partName ? !ctXml.includes(`PartName="${partName}"`) : true;
1183
- });
1184
- if (missing.length > 0) {
1185
- ctXml = ctXml.replace("</Types>", `${missing.join("")}</Types>`);
1186
- zip.file("[Content_Types].xml", ctXml, {
1573
+ const original = await ctFile.async("text");
1574
+ const updated = registerContentTypeParts(original, contentTypeParts);
1575
+ if (updated !== original) writeZipPart({
1576
+ zip,
1577
+ path: "[Content_Types].xml",
1578
+ data: updated,
1579
+ options: {
1187
1580
  compression: "DEFLATE",
1188
1581
  compressionOptions
1189
- });
1190
- }
1582
+ }
1583
+ });
1191
1584
  }
1585
+ return doc;
1192
1586
  }
1193
1587
  /**
1194
1588
  * A picture watermark is "model-driven" when its raw VML was cleared (so the
@@ -1204,25 +1598,46 @@ function hasModelDrivenPictureWatermark(doc) {
1204
1598
  for (const hf of headers.values()) if (hf.watermark?.kind === "picture" && !hf.rawWatermarkXml) return true;
1205
1599
  return false;
1206
1600
  }
1207
- /**
1208
- * Rebind each picture watermark's `imageRId` so it resolves in its own header
1209
- * part's rels. A watermark propagated across headers (or onto a header created
1210
- * by coverage) carries the source header's rId, which is meaningless in a
1211
- * sibling header's `word/_rels/header*.xml.rels`. Per header: keep the rId if
1212
- * it already resolves; otherwise reuse an existing relationship to the same
1213
- * media target, or mint a new one. Media bytes are materialized before this step. Raw watermark XML
1214
- * changes only when its image relationship must be rebound.
1215
- */
1216
- async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
1601
+ const appendCanonicalRelationship = ({ xml, relationshipXml, partPath }) => {
1602
+ const parsed = parseStreamingXmlWithSourceRanges(xml);
1603
+ if (parsed.status !== "parsed") throw new DocxPackageFidelityError(`Cannot read canonical relationships in ${partPath}`);
1604
+ const root = getChildElements(parsed.value).find((element) => getLocalName(element.name ?? "") === "Relationships" && getNamespaceUri(element) === PACKAGE_RELATIONSHIPS_NAMESPACE_URI);
1605
+ const range = root ? getXmlSourceRange(root) : void 0;
1606
+ if (!root?.name || !range) throw new DocxPackageFidelityError(`Cannot find canonical relationship root in ${partPath}`);
1607
+ const sourceRoot = xml.slice(range.start, range.end);
1608
+ const bound = relationshipXml.replace("<Relationship ", `<Relationship xmlns="${PACKAGE_RELATIONSHIPS_NAMESPACE_URI}" `);
1609
+ const selfClosing = sourceRoot.endsWith("/>");
1610
+ const close = xml.lastIndexOf("</", range.end);
1611
+ if (!selfClosing && close < range.start) throw new DocxPackageFidelityError(`Cannot extend canonical relationships in ${partPath}`);
1612
+ const result = spliceXml(xml, [selfClosing ? {
1613
+ start: range.end - 2,
1614
+ end: range.end,
1615
+ newXml: ">" + bound + `</${root.name}>`
1616
+ } : {
1617
+ start: close,
1618
+ end: close,
1619
+ newXml: bound
1620
+ }]);
1621
+ if (result === null) throw new DocxPackageFidelityError(`Cannot splice canonical relationships in ${partPath}`);
1622
+ return result;
1623
+ };
1624
+ async function rebindWatermarkRelIds({ document: doc, zip, compressionLevel, bodyAuthority }) {
1217
1625
  const rels = doc.package.relationships;
1218
1626
  const headers = doc.package.headers;
1219
- if (!rels || !headers) return;
1627
+ if (!headers) return;
1628
+ if (!rels) {
1629
+ if (bodyAuthority === "canonical" && [...headers.values()].some(({ watermark }) => watermark?.kind === "picture")) throw new DocxPackageFidelityError("A canonical picture watermark has no header relationship map");
1630
+ return;
1631
+ }
1220
1632
  const pending = [];
1221
1633
  for (const [rId, hf] of headers) {
1222
1634
  const watermark = hf.watermark;
1223
1635
  if (!watermark || watermark.kind !== "picture") continue;
1224
1636
  const rel = rels.get(rId);
1225
- if (!rel?.target) continue;
1637
+ if (!rel?.target) {
1638
+ if (bodyAuthority === "canonical") throw new DocxPackageFidelityError(`A canonical picture watermark has no header target for ${rId}`);
1639
+ continue;
1640
+ }
1226
1641
  pending.push({
1227
1642
  watermark,
1228
1643
  header: hf,
@@ -1255,20 +1670,37 @@ async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
1255
1670
  const changedPaths = /* @__PURE__ */ new Set();
1256
1671
  for (const { watermark, header, relsPath, partPath } of pending) {
1257
1672
  let canonical;
1258
- if (watermark.imageTarget !== void 0) if (watermark.imageTargetExternal) canonical = isAllowedExternalWatermarkImageUrl(watermark.imageTarget) ? {
1259
- mode: "external",
1260
- url: watermark.imageTarget
1261
- } : resolveCanonical(watermark.imageRId);
1262
- else canonical = {
1673
+ if (watermark.imageTarget !== void 0) if (watermark.imageTargetExternal) {
1674
+ if (bodyAuthority === "canonical" && !isAllowedExternalWatermarkImageUrl(watermark.imageTarget)) throw new DocxPackageFidelityError("A canonical picture watermark has an unsafe external target");
1675
+ canonical = isAllowedExternalWatermarkImageUrl(watermark.imageTarget) ? {
1676
+ mode: "external",
1677
+ url: watermark.imageTarget
1678
+ } : resolveCanonical(watermark.imageRId);
1679
+ } else canonical = {
1263
1680
  mode: "internal",
1264
1681
  absolute: watermark.imageTarget
1265
1682
  };
1266
1683
  else canonical = resolveCanonical(watermark.imageRId);
1267
- if (!canonical) continue;
1684
+ if (!canonical) {
1685
+ if (bodyAuthority === "canonical") throw new DocxPackageFidelityError("A canonical picture watermark image cannot be resolved");
1686
+ continue;
1687
+ }
1688
+ if (bodyAuthority === "canonical" && canonical.mode === "internal" && !zip.file(canonical.absolute)) throw new DocxPackageFidelityError("A canonical picture watermark image part is missing");
1268
1689
  const relsXml = relsXmlByPath.get(relsPath) ?? EMPTY_RELS_XML;
1269
1690
  const localRels = parseRelationships(relsXml);
1270
1691
  const local = relImage(localRels.get(watermark.imageRId), relsPath);
1271
1692
  if (local && sameImage(local, canonical)) continue;
1693
+ if (bodyAuthority === "canonical") {
1694
+ if (localRels.has(watermark.imageRId)) throw new DocxPackageFidelityError(`A canonical picture watermark relationship ${watermark.imageRId} has a conflicting target`);
1695
+ const relXml = canonical.mode === "external" ? `<Relationship Id="${escapeXmlAttribute(watermark.imageRId)}" Type="${RELATIONSHIP_TYPES.image}" Target="${escapeXmlAttribute(canonical.url)}" TargetMode="External"/>` : `<Relationship Id="${escapeXmlAttribute(watermark.imageRId)}" Type="${RELATIONSHIP_TYPES.image}" Target="${escapeXmlAttribute(relativeTargetForPart(partPath, canonical.absolute))}"/>`;
1696
+ relsXmlByPath.set(relsPath, appendCanonicalRelationship({
1697
+ xml: relsXml,
1698
+ relationshipXml: relXml,
1699
+ partPath: relsPath
1700
+ }));
1701
+ changedPaths.add(relsPath);
1702
+ continue;
1703
+ }
1272
1704
  let resolvedRId;
1273
1705
  for (const [id, rel] of localRels) {
1274
1706
  const image = relImage(rel, relsPath);
@@ -1293,19 +1725,18 @@ async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
1293
1725
  const compressionOptions = { level: compressionLevel };
1294
1726
  for (const path of changedPaths) {
1295
1727
  const xml = relsXmlByPath.get(path);
1296
- if (xml) zip.file(path, xml, {
1297
- compression: "DEFLATE",
1298
- compressionOptions
1728
+ if (xml) writeZipPart({
1729
+ zip,
1730
+ path,
1731
+ data: xml,
1732
+ options: {
1733
+ compression: "DEFLATE",
1734
+ compressionOptions
1735
+ }
1299
1736
  });
1300
1737
  }
1301
1738
  }
1302
- /**
1303
- * Re-serialize every header/footer the model still owns, keyed by part path.
1304
- *
1305
- * `sourceZip` supplies each part as it stands before the save so the rebuilt
1306
- * root can keep any prefix binding only the source document declared.
1307
- */
1308
- async function collectHeaderFooterUpdates(doc, sourceZip) {
1739
+ async function collectHeaderFooterUpdates(doc, { sourceZip, onDiagnostic }) {
1309
1740
  const updates = /* @__PURE__ */ new Map();
1310
1741
  const rels = doc.package.relationships;
1311
1742
  if (!rels) return updates;
@@ -1327,21 +1758,28 @@ async function collectHeaderFooterUpdates(doc, sourceZip) {
1327
1758
  const bindings = sourceFile ? readRootNamespaceBindings(await sourceFile.async("text")) : /* @__PURE__ */ new Map();
1328
1759
  updates.set(path, serializeHeaderFooter(headerFooter, {
1329
1760
  path,
1330
- bindings
1761
+ bindings,
1762
+ onDiagnostic,
1763
+ originalBuffer: doc.originalBuffer
1331
1764
  }));
1332
1765
  }
1333
1766
  }
1334
1767
  }
1335
1768
  return updates;
1336
1769
  }
1337
- /**
1338
- * Serialize modified headers and footers into the ZIP
1339
- */
1340
- async function serializeHeadersFootersToZip(doc, zip, compressionLevel) {
1770
+ async function serializeHeadersFootersToZip({ doc, zip, compressionLevel, onDiagnostic }) {
1341
1771
  const compressionOptions = { level: compressionLevel };
1342
- for (const [filename, xml] of await collectHeaderFooterUpdates(doc, zip)) zip.file(filename, xml, {
1343
- compression: "DEFLATE",
1344
- compressionOptions
1772
+ for (const [filename, xml] of await collectHeaderFooterUpdates(doc, {
1773
+ sourceZip: zip,
1774
+ onDiagnostic
1775
+ })) writeZipPart({
1776
+ zip,
1777
+ path: filename,
1778
+ data: xml,
1779
+ options: {
1780
+ compression: "DEFLATE",
1781
+ compressionOptions
1782
+ }
1345
1783
  });
1346
1784
  }
1347
1785
  async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel, changedNoteParaIds }) {
@@ -1350,7 +1788,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1350
1788
  if (footnotes.length > 0 || originalFootnotes) if (originalFootnotes) await patchNotePartIntoZip({
1351
1789
  conventionalLowerPath: "word/footnotes.xml",
1352
1790
  currentXml: serializeFootnotes(footnotes),
1353
- replacementXml: serializeNewFootnotesPart(footnotes),
1791
+ replacementXml: serializeNewFootnotesPart(footnotes, doc),
1354
1792
  baselineFrom: (xml) => serializeFootnotes(parseFootnotes(xml).getNormalFootnotes()),
1355
1793
  elementName: "footnote",
1356
1794
  changedNoteParaIds,
@@ -1363,7 +1801,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1363
1801
  newZip,
1364
1802
  partPath: "word/footnotes.xml",
1365
1803
  relationshipType: RELATIONSHIP_TYPES.footnotes,
1366
- serializedPart: serializeNewFootnotesPart(footnotes),
1804
+ serializedPart: serializeNewFootnotesPart(footnotes, doc),
1367
1805
  compressionLevel
1368
1806
  });
1369
1807
  const endnotes = doc.package.endnotes ?? [];
@@ -1371,7 +1809,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1371
1809
  if (endnotes.length > 0 || originalEndnotes) if (originalEndnotes) await patchNotePartIntoZip({
1372
1810
  conventionalLowerPath: "word/endnotes.xml",
1373
1811
  currentXml: serializeEndnotes(endnotes),
1374
- replacementXml: serializeNewEndnotesPart(endnotes),
1812
+ replacementXml: serializeNewEndnotesPart(endnotes, doc),
1375
1813
  baselineFrom: (xml) => serializeEndnotes(parseEndnotes(xml).getNormalEndnotes()),
1376
1814
  elementName: "endnote",
1377
1815
  changedNoteParaIds,
@@ -1384,7 +1822,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1384
1822
  newZip,
1385
1823
  partPath: "word/endnotes.xml",
1386
1824
  relationshipType: RELATIONSHIP_TYPES.endnotes,
1387
- serializedPart: serializeNewEndnotesPart(endnotes),
1825
+ serializedPart: serializeNewEndnotesPart(endnotes, doc),
1388
1826
  compressionLevel
1389
1827
  });
1390
1828
  }
@@ -1395,22 +1833,68 @@ async function materializeNewNotePart({ contentType, newZip, partPath, relations
1395
1833
  const compressionOptions = { level: compressionLevel };
1396
1834
  const contentTypesXml = await contentTypesFile.async("text");
1397
1835
  const partName = `/${partPath}`;
1398
- if (!contentTypesXml.toLowerCase().includes(partName.toLowerCase())) newZip.file(contentTypesFile.name, contentTypesXml.replace("</Types>", () => `<Override PartName="${partName}" ContentType="${contentType}"/></Types>`), {
1399
- compression: "DEFLATE",
1400
- compressionOptions
1836
+ if (!contentTypesXml.toLowerCase().includes(partName.toLowerCase())) writeZipPart({
1837
+ zip: newZip,
1838
+ path: contentTypesFile.name,
1839
+ data: contentTypesXml.replace("</Types>", () => `<Override PartName="${partName}" ContentType="${contentType}"/></Types>`),
1840
+ options: {
1841
+ compression: "DEFLATE",
1842
+ compressionOptions
1843
+ }
1401
1844
  });
1402
1845
  const relationshipsXml = await relationshipsFile.async("text");
1403
1846
  if (!relationshipsXml.includes(relationshipType)) {
1404
1847
  const relationshipId = `rId${findMaxRId(relationshipsXml) + 1}`;
1405
1848
  const target = partPath.slice(5);
1406
- newZip.file(relationshipsFile.name, relationshipsXml.replace("</Relationships>", () => `<Relationship Id="${relationshipId}" Type="${relationshipType}" Target="${target}"/></Relationships>`), {
1849
+ writeZipPart({
1850
+ zip: newZip,
1851
+ path: relationshipsFile.name,
1852
+ data: relationshipsXml.replace("</Relationships>", () => `<Relationship Id="${relationshipId}" Type="${relationshipType}" Target="${target}"/></Relationships>`),
1853
+ options: {
1854
+ compression: "DEFLATE",
1855
+ compressionOptions
1856
+ }
1857
+ });
1858
+ }
1859
+ writeZipPart({
1860
+ zip: newZip,
1861
+ path: partPath,
1862
+ data: serializedPart,
1863
+ options: {
1407
1864
  compression: "DEFLATE",
1408
1865
  compressionOptions
1866
+ }
1867
+ });
1868
+ }
1869
+ /** Only this modeled switch changes on story lifecycle edits; preserve all other settings. */
1870
+ async function serializeHeaderFooterSettingsIntoZip(doc, zip, compressionLevel) {
1871
+ if (!doc.package.settings) return;
1872
+ const enabled = doc.package.settings.evenAndOddHeaders;
1873
+ const file = zip.file(SETTINGS_PART);
1874
+ if (!file) {
1875
+ if (enabled === void 0) return;
1876
+ await materializeNewNotePart({
1877
+ contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
1878
+ newZip: zip,
1879
+ partPath: "word/settings.xml",
1880
+ relationshipType: RELATIONSHIP_TYPES.settings,
1881
+ serializedPart: serializeSettingsXml(doc.package.settings),
1882
+ compressionLevel
1409
1883
  });
1884
+ return;
1410
1885
  }
1411
- newZip.file(partPath, serializedPart, {
1412
- compression: "DEFLATE",
1413
- compressionOptions
1886
+ const xml = await file.async("text");
1887
+ if (parseSettings(xml).evenAndOddHeaders === enabled) return;
1888
+ const patched = updateEvenAndOddHeaders(xml, enabled);
1889
+ if (patched === null) throw new DocxPackageFidelityError("Cannot update malformed header/footer settings");
1890
+ writeZipPart({
1891
+ zip,
1892
+ path: file.name,
1893
+ data: patched,
1894
+ options: {
1895
+ compression: "DEFLATE",
1896
+ compressionOptions: { level: compressionLevel }
1897
+ }
1414
1898
  });
1415
1899
  }
1416
1900
  const numberingBaselines = /* @__PURE__ */ new WeakMap();
@@ -1446,9 +1930,14 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
1446
1930
  currentXml
1447
1931
  });
1448
1932
  if (patched === null || patched === baseline.originalXml) return;
1449
- newZip.file(file.name, patched, {
1450
- compression: "DEFLATE",
1451
- compressionOptions: { level: compressionLevel }
1933
+ writeZipPart({
1934
+ zip: newZip,
1935
+ path: file.name,
1936
+ data: patched,
1937
+ options: {
1938
+ compression: "DEFLATE",
1939
+ compressionOptions: { level: compressionLevel }
1940
+ }
1452
1941
  });
1453
1942
  }
1454
1943
  const STYLES_PART_PATH = "word/styles.xml";
@@ -1556,24 +2045,18 @@ const styleDefinitionsToSerialize = (doc) => {
1556
2045
  styles: [...styles.styles, ...missing]
1557
2046
  };
1558
2047
  };
1559
- async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compressionLevel) {
2048
+ /** One append-only style owner for full repack and selective overlays. */
2049
+ async function planAddedStyles(doc, originalZip) {
1560
2050
  const styles = doc.package.styles;
1561
- if (!styles || styles.styles.length === 0) return;
2051
+ if (!styles || styles.styles.length === 0) return { type: "unchanged" };
1562
2052
  const file = findNotePartEntry(originalZip, STYLES_PART_PATH);
1563
2053
  const originalXml = file ? await file.async("text") : null;
1564
2054
  const rootName = originalXml === null ? void 0 : /<(?![?!])(?<name>[^\s/>]+)/u.exec(originalXml)?.groups?.["name"];
1565
2055
  const rootClose = originalXml !== null && rootName !== void 0 && getLocalName(rootName) === "styles" ? originalXml.lastIndexOf(`</${rootName}>`) : -1;
1566
- if (file === null || originalXml === null || rootClose < 0) {
1567
- await materializeNewNotePart({
1568
- contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
1569
- newZip,
1570
- partPath: STYLES_PART_PATH,
1571
- relationshipType: RELATIONSHIP_TYPES.styles,
1572
- serializedPart: serializeStylesXml(styles),
1573
- compressionLevel
1574
- });
1575
- return;
1576
- }
2056
+ if (file === null || originalXml === null || rootClose < 0) return {
2057
+ type: "materialize",
2058
+ styles
2059
+ };
1577
2060
  const existing = new Set(definedStyleIds(originalXml));
1578
2061
  const added = [];
1579
2062
  for (const style of styles.styles) {
@@ -1581,13 +2064,42 @@ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compression
1581
2064
  existing.add(style.styleId);
1582
2065
  added.push(style);
1583
2066
  }
1584
- if (added.length === 0) return;
2067
+ if (added.length === 0) return { type: "unchanged" };
1585
2068
  const canonical = hasCanonicalWordprocessingPrefixes(originalXml);
1586
2069
  const patched = originalXml.slice(0, rootClose) + added.map((style) => canonical ? serializeStyle(style) : selfBoundFragment(serializeStyle(style), file.name)).join("") + originalXml.slice(rootClose);
1587
- newZip.file(file.name, patched, {
1588
- compression: "DEFLATE",
1589
- compressionOptions: { level: compressionLevel }
1590
- });
2070
+ return {
2071
+ type: "patch",
2072
+ path: file.name,
2073
+ xml: patched
2074
+ };
2075
+ }
2076
+ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compressionLevel) {
2077
+ const plan = await planAddedStyles(doc, originalZip);
2078
+ switch (plan.type) {
2079
+ case "unchanged": return;
2080
+ case "materialize":
2081
+ await materializeNewNotePart({
2082
+ contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
2083
+ newZip,
2084
+ partPath: STYLES_PART_PATH,
2085
+ relationshipType: RELATIONSHIP_TYPES.styles,
2086
+ serializedPart: serializeStylesXml(plan.styles),
2087
+ compressionLevel
2088
+ });
2089
+ return;
2090
+ case "patch":
2091
+ writeZipPart({
2092
+ zip: newZip,
2093
+ path: plan.path,
2094
+ data: plan.xml,
2095
+ options: {
2096
+ compression: "DEFLATE",
2097
+ compressionOptions: { level: compressionLevel }
2098
+ }
2099
+ });
2100
+ return;
2101
+ default: return plan;
2102
+ }
1591
2103
  }
1592
2104
  /**
1593
2105
  * The XML to write for a patched note part, or null to keep the original.
@@ -1642,9 +2154,14 @@ async function patchNotePartIntoZip({ conventionalLowerPath, currentXml, replace
1642
2154
  partName: file.name
1643
2155
  });
1644
2156
  if (patchedXml === null || patchedXml === originalXml) return;
1645
- newZip.file(file.name, patchedXml, {
1646
- compression: "DEFLATE",
1647
- compressionOptions: { level: compressionLevel }
2157
+ writeZipPart({
2158
+ zip: newZip,
2159
+ path: file.name,
2160
+ data: patchedXml,
2161
+ options: {
2162
+ compression: "DEFLATE",
2163
+ compressionOptions: { level: compressionLevel }
2164
+ }
1648
2165
  });
1649
2166
  }
1650
2167
  /** `word/Footnotes.xml` -> `word/_rels/Footnotes.xml.rels` (casing preserved). */
@@ -1742,7 +2259,10 @@ function createEmptyDocx(properties = {}) {
1742
2259
  }
1743
2260
  const createEmptyDocxZip = ({ creator, application }) => {
1744
2261
  const zip = new JSZip();
1745
- zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2262
+ writeZipPart({
2263
+ zip,
2264
+ path: "[Content_Types].xml",
2265
+ data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1746
2266
  <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
1747
2267
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
1748
2268
  <Default Extension="xml" ContentType="application/xml"/>
@@ -1750,18 +2270,30 @@ const createEmptyDocxZip = ({ creator, application }) => {
1750
2270
  <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
1751
2271
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
1752
2272
  <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
1753
- </Types>`);
1754
- zip.file("_rels/.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2273
+ </Types>`
2274
+ });
2275
+ writeZipPart({
2276
+ zip,
2277
+ path: "_rels/.rels",
2278
+ data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1755
2279
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
1756
2280
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
1757
2281
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
1758
2282
  <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
1759
- </Relationships>`);
1760
- zip.file("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2283
+ </Relationships>`
2284
+ });
2285
+ writeZipPart({
2286
+ zip,
2287
+ path: "word/_rels/document.xml.rels",
2288
+ data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1761
2289
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
1762
2290
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
1763
- </Relationships>`);
1764
- zip.file("word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2291
+ </Relationships>`
2292
+ });
2293
+ writeZipPart({
2294
+ zip,
2295
+ path: "word/document.xml",
2296
+ data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1765
2297
  <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
1766
2298
  <w:body>
1767
2299
  <w:p>
@@ -1774,19 +2306,30 @@ const createEmptyDocxZip = ({ creator, application }) => {
1774
2306
  <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
1775
2307
  </w:sectPr>
1776
2308
  </w:body>
1777
- </w:document>`);
1778
- zip.file(STYLES_PART_PATH, SEED_STYLES_XML);
2309
+ </w:document>`
2310
+ });
2311
+ writeZipPart({
2312
+ zip,
2313
+ path: STYLES_PART_PATH,
2314
+ data: SEED_STYLES_XML
2315
+ });
1779
2316
  const now = (/* @__PURE__ */ new Date()).toISOString();
1780
- const creatorElement = creator === void 0 ? "" : `\n <dc:creator>${escapeXmlText(creator)}</dc:creator>`;
1781
- zip.file("docProps/core.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1782
- <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">${creatorElement}
2317
+ writeZipPart({
2318
+ zip,
2319
+ path: "docProps/core.xml",
2320
+ data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2321
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">${creator === void 0 ? "" : `\n <dc:creator>${escapeXmlText(creator)}</dc:creator>`}
1783
2322
  <dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created>
1784
2323
  <dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified>
1785
- </cp:coreProperties>`);
1786
- const applicationElements = application === void 0 ? "" : `\n <Application>${escapeXmlText(application)}</Application>\n <AppVersion>${CREATED_APP_VERSION}</AppVersion>`;
1787
- zip.file("docProps/app.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1788
- <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">${applicationElements}
1789
- </Properties>`);
2324
+ </cp:coreProperties>`
2325
+ });
2326
+ writeZipPart({
2327
+ zip,
2328
+ path: "docProps/app.xml",
2329
+ data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2330
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">${application === void 0 ? "" : `\n <Application>${escapeXmlText(application)}</Application>\n <AppVersion>${CREATED_APP_VERSION}</AppVersion>`}
2331
+ </Properties>`
2332
+ });
1790
2333
  return zip;
1791
2334
  };
1792
2335
  /**
@@ -1807,7 +2350,9 @@ async function createDocx(doc, properties = {}) {
1807
2350
  originalDocument: source.document,
1808
2351
  originalCorePropertiesXml: source.corePropertiesXml,
1809
2352
  compressionLevel: 6,
1810
- updateModifiedDate: true
2353
+ updateModifiedDate: true,
2354
+ onDiagnostic: properties.onDiagnostic,
2355
+ bodyAuthority: properties.bodyAuthority
1811
2356
  });
1812
2357
  }
1813
2358
  const zip = await createDocumentSeedZip(doc, properties);
@@ -1818,7 +2363,9 @@ async function createDocx(doc, properties = {}) {
1818
2363
  originalDocument: void 0,
1819
2364
  originalCorePropertiesXml: await zip.file("docProps/core.xml")?.async("text"),
1820
2365
  compressionLevel: 6,
1821
- updateModifiedDate: true
2366
+ updateModifiedDate: true,
2367
+ onDiagnostic: properties.onDiagnostic,
2368
+ bodyAuthority: properties.bodyAuthority
1822
2369
  });
1823
2370
  }
1824
2371
  const createDocumentSeedZip = async (doc, properties) => {
@@ -1829,38 +2376,69 @@ const createDocumentSeedZip = async (doc, properties) => {
1829
2376
  const styleDefinitions = styleDefinitionsToSerialize(doc);
1830
2377
  if (styleDefinitions) {
1831
2378
  assertStyleNumberingReferences(doc);
1832
- zip.file(STYLES_PART_PATH, serializeStylesXml(styleDefinitions));
1833
- } else zip.file(STYLES_PART_PATH, seedStylesXmlWith(missingNoteReferenceStyles(void 0, noteReferenceNeeds(doc.package))));
2379
+ writeZipPart({
2380
+ zip,
2381
+ path: STYLES_PART_PATH,
2382
+ data: serializeStylesXml(styleDefinitions)
2383
+ });
2384
+ } else writeZipPart({
2385
+ zip,
2386
+ path: STYLES_PART_PATH,
2387
+ data: seedStylesXmlWith(missingNoteReferenceStyles(void 0, noteReferenceNeeds(doc.package)))
2388
+ });
1834
2389
  const numbering = doc.package.numbering;
1835
2390
  if (numbering && (numbering.abstractNums.length > 0 || numbering.nums.length > 0)) {
1836
- zip.file("word/numbering.xml", serializeNumberingXml(numbering));
2391
+ writeZipPart({
2392
+ zip,
2393
+ path: "word/numbering.xml",
2394
+ data: serializeNumberingXml(numbering)
2395
+ });
1837
2396
  relationships.push(relationshipXml(nextRelationshipId, "numbering", "numbering.xml"));
1838
2397
  overrides.push(overrideXml("/word/numbering.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"));
1839
2398
  nextRelationshipId += 1;
1840
2399
  }
1841
2400
  if (doc.package.theme) {
1842
- zip.file("word/theme/theme1.xml", serializeThemeXml(doc.package.theme));
2401
+ writeZipPart({
2402
+ zip,
2403
+ path: "word/theme/theme1.xml",
2404
+ data: serializeThemeXml(doc.package.theme)
2405
+ });
1843
2406
  relationships.push(relationshipXml(nextRelationshipId, "theme", "theme/theme1.xml"));
1844
2407
  overrides.push(overrideXml("/word/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml"));
1845
2408
  nextRelationshipId += 1;
1846
2409
  }
1847
2410
  if (doc.package.fontTable && doc.package.fontTable.fonts.length > 0) {
1848
- zip.file("word/fontTable.xml", serializeFontTableXml(doc.package.fontTable));
2411
+ writeZipPart({
2412
+ zip,
2413
+ path: "word/fontTable.xml",
2414
+ data: serializeFontTableXml(doc.package.fontTable)
2415
+ });
1849
2416
  relationships.push(relationshipXml(nextRelationshipId, "fontTable", "fontTable.xml"));
1850
2417
  overrides.push(overrideXml("/word/fontTable.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"));
1851
2418
  nextRelationshipId += 1;
1852
2419
  }
1853
2420
  if (doc.package.settings) {
1854
- zip.file("word/settings.xml", serializeSettingsXml(doc.package.settings));
2421
+ writeZipPart({
2422
+ zip,
2423
+ path: "word/settings.xml",
2424
+ data: serializeSettingsXml(doc.package.settings)
2425
+ });
1855
2426
  relationships.push(relationshipXml(nextRelationshipId, "settings", "settings.xml"));
1856
2427
  overrides.push(overrideXml("/word/settings.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"));
1857
2428
  }
1858
- zip.file("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${relationships.join("")}</Relationships>`);
2429
+ writeZipPart({
2430
+ zip,
2431
+ path: "word/_rels/document.xml.rels",
2432
+ data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${relationships.join("")}</Relationships>`
2433
+ });
1859
2434
  if (overrides.length > 0) {
1860
2435
  const contentTypesFile = zip.file("[Content_Types].xml");
1861
2436
  if (!contentTypesFile) panic("Fresh DOCX seed is missing [Content_Types].xml");
1862
- const contentTypes = await contentTypesFile.async("string");
1863
- zip.file("[Content_Types].xml", contentTypes.replace("</Types>", `${overrides.join("")}\n</Types>`));
2437
+ writeZipPart({
2438
+ zip,
2439
+ path: "[Content_Types].xml",
2440
+ data: (await contentTypesFile.async("string")).replace("</Types>", `${overrides.join("")}\n</Types>`)
2441
+ });
1864
2442
  }
1865
2443
  return zip;
1866
2444
  };
@@ -1894,4 +2472,4 @@ const assertStyleNumberingReferences = (doc) => {
1894
2472
  }
1895
2473
  };
1896
2474
  //#endregion
1897
- export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxDuplicateSectionCarrierError, DocxPackageFidelityError, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };
2475
+ export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, CanonicalImageRelationshipRefusalError, DocxDuplicateSectionCarrierError, DocxPackageFidelityError, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, assertSectionCarriersMatchModel, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedHyperlinkBindings, hasUnmaterializedInlineResources, isDocxBuffer, notePartRelsPath, planAddedStyles, publishCanonicalImageResources, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx, withoutAttachedTemplate };