@stll/folio-core 0.54.2 → 0.55.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 (199) hide show
  1. package/dist/ai-edits/apply.js +22 -1
  2. package/dist/ai-edits/headless.js +1 -0
  3. package/dist/compare/content-types.d.ts +3 -15
  4. package/dist/controller/canonicalComposition.d.ts +36 -0
  5. package/dist/controller/canonicalComposition.js +168 -0
  6. package/dist/controller/canonicalInput.d.ts +45 -0
  7. package/dist/controller/canonicalInput.js +338 -0
  8. package/dist/controller/canonicalOperations.d.ts +22 -0
  9. package/dist/controller/canonicalOperations.js +85 -0
  10. package/dist/controller/canonicalReview.d.ts +12 -0
  11. package/dist/controller/canonicalReview.js +19 -0
  12. package/dist/controller/canonicalSession.d.ts +149 -0
  13. package/dist/controller/canonicalSession.js +1038 -0
  14. package/dist/controller/canonicalStoryEditor.d.ts +34 -0
  15. package/dist/controller/canonicalStoryEditor.js +82 -0
  16. package/dist/controller/canonicalStructure.d.ts +16 -0
  17. package/dist/controller/canonicalStructure.js +269 -0
  18. package/dist/controller/folioEditor.d.ts +1 -1
  19. package/dist/controller/folioEditor.js +9 -0
  20. package/dist/controller/headerFooterEditorManager.d.ts +4 -0
  21. package/dist/controller/headerFooterEditorManager.js +71 -3
  22. package/dist/controller/hiddenEditorApi.d.ts +35 -0
  23. package/dist/controller/hiddenEditorApi.js +25 -8
  24. package/dist/controller/hiddenEditorManager.d.ts +11 -2
  25. package/dist/controller/hiddenEditorManager.js +456 -30
  26. package/dist/controller/layoutPipeline.d.ts +1 -1
  27. package/dist/controller/layoutPipeline.js +2 -0
  28. package/dist/controller/noteEditorManager.d.ts +4 -0
  29. package/dist/controller/noteEditorManager.js +113 -15
  30. package/dist/docx/attributeRemainder.js +1 -0
  31. package/dist/docx/canonicalSessionInput.d.ts +11 -0
  32. package/dist/docx/canonicalSessionInput.js +27 -0
  33. package/dist/docx/canonicalStoryRepack.d.ts +10 -0
  34. package/dist/docx/canonicalStoryRepack.js +41 -0
  35. package/dist/docx/contentTypeRegistry.d.ts +8 -0
  36. package/dist/docx/contentTypeRegistry.js +64 -0
  37. package/dist/docx/ensureParaIds.d.ts +7 -1
  38. package/dist/docx/ensureParaIds.js +163 -211
  39. package/dist/docx/foldedListNumberFields.d.ts +85 -0
  40. package/dist/docx/foldedListNumberFields.js +267 -0
  41. package/dist/docx/headerFooterVerbatim.d.ts +14 -1
  42. package/dist/docx/headerFooterVerbatim.js +95 -2
  43. package/dist/docx/noteIds.d.ts +5 -0
  44. package/dist/docx/noteIds.js +6 -2
  45. package/dist/docx/noteReferenceMark.d.ts +3 -1
  46. package/dist/docx/noteReferenceMark.js +15 -14
  47. package/dist/docx/numberingParser.d.ts +3 -1
  48. package/dist/docx/numberingParser.js +17 -3
  49. package/dist/docx/packageParts.d.ts +3 -1
  50. package/dist/docx/packageParts.js +65 -16
  51. package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
  52. package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
  53. package/dist/docx/paragraphParser.js +101 -53
  54. package/dist/docx/paragraphPropertySource.d.ts +15 -1
  55. package/dist/docx/paragraphPropertySource.js +76 -22
  56. package/dist/docx/parser.js +2 -1
  57. package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
  58. package/dist/docx/reviewHistoryNamespace.js +4 -0
  59. package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
  60. package/dist/docx/reviewResolutionProvenance.js +153 -0
  61. package/dist/docx/rezip.d.ts +7 -3
  62. package/dist/docx/rezip.js +107 -45
  63. package/dist/docx/runConsolidator.d.ts +2 -58
  64. package/dist/docx/runConsolidator.js +3 -175
  65. package/dist/docx/runParser.d.ts +2 -1
  66. package/dist/docx/runParser.js +156 -116
  67. package/dist/docx/saveDiagnostics.d.ts +11 -0
  68. package/dist/docx/saveDiagnostics.js +0 -0
  69. package/dist/docx/selectiveSave.d.ts +2 -1
  70. package/dist/docx/selectiveSave.js +4 -1
  71. package/dist/docx/selectiveXmlPatch.d.ts +16 -8
  72. package/dist/docx/selectiveXmlPatch.js +74 -2
  73. package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
  74. package/dist/docx/serializer/headerFooterSerializer.js +19 -11
  75. package/dist/docx/serializer/noteSerializer.d.ts +2 -2
  76. package/dist/docx/serializer/noteSerializer.js +24 -8
  77. package/dist/docx/serializer/paragraphSerializer.js +41 -4
  78. package/dist/docx/serializer/partNamespaces.js +2 -1
  79. package/dist/docx/serializer/runSerializer.js +3 -1
  80. package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
  81. package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
  82. package/dist/docx/settingsParser.d.ts +1 -2
  83. package/dist/docx/settingsParser.js +5 -6
  84. package/dist/docx/storyBlockReplay.d.ts +11 -0
  85. package/dist/docx/storyBlockReplay.js +367 -0
  86. package/dist/docx/streamingXmlParser.d.ts +22 -1
  87. package/dist/docx/streamingXmlParser.js +49 -3
  88. package/dist/docx/structuralXmlPatch.js +8 -1
  89. package/dist/docx/verbatimCapture.d.ts +3 -1
  90. package/dist/docx/verbatimCapture.js +49 -6
  91. package/dist/i18n/messages/ar.gen.d.ts +5 -0
  92. package/dist/i18n/messages/ar.gen.js +656 -0
  93. package/dist/i18n/messages/cs.gen.d.ts +5 -0
  94. package/dist/i18n/messages/cs.gen.js +656 -0
  95. package/dist/i18n/messages/de.gen.d.ts +5 -0
  96. package/dist/i18n/messages/de.gen.js +656 -0
  97. package/dist/i18n/messages/en.gen.d.ts +5 -0
  98. package/dist/i18n/messages/en.gen.js +656 -0
  99. package/dist/i18n/messages/es.gen.d.ts +5 -0
  100. package/dist/i18n/messages/es.gen.js +656 -0
  101. package/dist/i18n/messages/et.gen.d.ts +5 -0
  102. package/dist/i18n/messages/et.gen.js +656 -0
  103. package/dist/i18n/messages/fr.gen.d.ts +5 -0
  104. package/dist/i18n/messages/fr.gen.js +656 -0
  105. package/dist/i18n/messages/he.gen.d.ts +5 -0
  106. package/dist/i18n/messages/he.gen.js +656 -0
  107. package/dist/i18n/messages/hi.gen.d.ts +5 -0
  108. package/dist/i18n/messages/hi.gen.js +656 -0
  109. package/dist/i18n/messages/hu.gen.d.ts +5 -0
  110. package/dist/i18n/messages/hu.gen.js +656 -0
  111. package/dist/i18n/messages/locales.d.ts +5 -0
  112. package/dist/i18n/messages/locales.js +22 -0
  113. package/dist/i18n/messages/lt.gen.d.ts +5 -0
  114. package/dist/i18n/messages/lt.gen.js +656 -0
  115. package/dist/i18n/messages/lv.gen.d.ts +5 -0
  116. package/dist/i18n/messages/lv.gen.js +656 -0
  117. package/dist/i18n/messages/pl.gen.d.ts +5 -0
  118. package/dist/i18n/messages/pl.gen.js +656 -0
  119. package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
  120. package/dist/i18n/messages/pt-BR.gen.js +656 -0
  121. package/dist/i18n/messages/sk.gen.d.ts +5 -0
  122. package/dist/i18n/messages/sk.gen.js +656 -0
  123. package/dist/i18n/messages/tr.gen.d.ts +5 -0
  124. package/dist/i18n/messages/tr.gen.js +656 -0
  125. package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
  126. package/dist/i18n/messages/zh-CN.gen.js +656 -0
  127. package/dist/i18n/messages.d.ts +2 -3
  128. package/dist/i18n/messages.js +1 -19
  129. package/dist/internal/noteMarkerAttrs.d.ts +8 -0
  130. package/dist/internal/noteMarkerAttrs.js +16 -0
  131. package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
  132. package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
  133. package/dist/internal/prosemirrorAttrBoundary.js +50 -0
  134. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  135. package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
  136. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  137. package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
  138. package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
  139. package/dist/layout-bridge/convert/noteReferences.js +1 -6
  140. package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
  141. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
  142. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
  143. package/dist/managers/DocumentLoaderManager.d.ts +1 -0
  144. package/dist/managers/DocumentLoaderManager.js +8 -1
  145. package/dist/markdown/renderBlock.js +15 -1
  146. package/dist/markdown/renderParagraph.js +9 -13
  147. package/dist/markdown/renderRuns.js +3 -2
  148. package/dist/markdown/renderTable.js +2 -0
  149. package/dist/markdown/types.d.ts +3 -9
  150. package/dist/prosemirror/attrs/index.d.ts +2 -12
  151. package/dist/prosemirror/attrs/index.js +22 -48
  152. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
  153. package/dist/prosemirror/canonicalCommands.d.ts +97 -0
  154. package/dist/prosemirror/canonicalCommands.js +28 -0
  155. package/dist/prosemirror/commands/pageBreak.js +15 -1
  156. package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
  157. package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
  158. package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
  159. package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
  160. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -3
  161. package/dist/prosemirror/conversion/toProseDoc.js +51 -11
  162. package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
  163. package/dist/prosemirror/executeEditorCommand.js +26 -0
  164. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  165. package/dist/prosemirror/extensions/core/ParagraphExtension.js +50 -19
  166. package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
  167. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
  168. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
  169. package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
  170. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  171. package/dist/prosemirror/extensions/marks/markUtils.js +70 -8
  172. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
  173. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
  174. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
  175. package/dist/prosemirror/listMarker.d.ts +1 -0
  176. package/dist/prosemirror/listMarker.js +16 -7
  177. package/dist/prosemirror/listNumbering.js +26 -5
  178. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  179. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  180. package/dist/prosemirror/markupViewNotes.js +3 -2
  181. package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
  182. package/dist/prosemirror/noteReferenceReview.js +5 -3
  183. package/dist/prosemirror/plugins/suggestionMode.js +50 -2
  184. package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
  185. package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
  186. package/dist/prosemirror/runFormattingFromMarks.js +15 -1
  187. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
  188. package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
  189. package/dist/prosemirror/schema/marks.d.ts +2 -0
  190. package/dist/prosemirror/schema/nodes.d.ts +10 -2
  191. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
  192. package/dist/prosemirror/textInput.js +5 -1
  193. package/dist/prosemirror/trackedRevisionPath.js +2 -1
  194. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
  195. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
  196. package/dist/prosemirror/validation.js +4 -0
  197. package/dist/types/content.d.ts +2 -2
  198. package/dist/utils/createDocument.js +2 -2
  199. package/package.json +14 -2
@@ -1,11 +1,28 @@
1
1
  import { modelParagraphFormattingEmission } from "../internal/paragraphFormattingSerialization.js";
2
2
  import { canonicalJson } from "../utils/canonicalJson.js";
3
+ import { copyHeaderFooterPackageBaselines } from "./headerFooterVerbatim.js";
3
4
  import { visitDocxParagraphs } from "./paragraphTraversal.js";
4
5
  import { TaggedError, panic } from "better-result";
5
6
  import { PluginKey } from "prosemirror-state";
6
7
  //#region src/docx/paragraphPropertySource.ts
7
8
  const paragraphPropertySourceEmissionFingerprint = Symbol("paragraphPropertySourceEmissionFingerprint");
8
- const paragraphPropertySources = /* @__PURE__ */ new WeakMap();
9
+ const paragraphPropertyCapture = Symbol("paragraphPropertyCapture");
10
+ const paragraphPropertyCaptures = /* @__PURE__ */ new WeakMap();
11
+ const paragraphSourceRecordKinds = /* @__PURE__ */ new WeakMap();
12
+ const captureForParagraph = (paragraph) => {
13
+ const handle = Object.getOwnPropertyDescriptor(paragraph, paragraphPropertyCapture)?.value;
14
+ if (!Object.hasOwn(paragraph, paragraphPropertyCapture)) return paragraphPropertyCaptures.get(paragraph);
15
+ return typeof handle === "object" && handle !== null ? paragraphPropertyCaptures.get(handle) : void 0;
16
+ };
17
+ const attachParagraphCapture = (paragraph, capture) => {
18
+ const handle = Object.freeze({});
19
+ paragraphPropertyCaptures.set(handle, capture);
20
+ Object.defineProperty(paragraph, paragraphPropertyCapture, {
21
+ value: handle,
22
+ enumerable: true,
23
+ configurable: true
24
+ });
25
+ };
9
26
  const paragraphFormattingEmissionFingerprint = (emission) => canonicalJson(emission);
10
27
  const ownedParagraphPropertySource = (paragraph, xml) => {
11
28
  return Object.freeze({
@@ -13,7 +30,6 @@ const ownedParagraphPropertySource = (paragraph, xml) => {
13
30
  [paragraphPropertySourceEmissionFingerprint]: paragraphFormattingEmissionFingerprint(modelParagraphFormattingEmission(paragraph.formatting))
14
31
  });
15
32
  };
16
- const paragraphPropertySourceOwners = /* @__PURE__ */ new WeakMap();
17
33
  const proseParagraphSourceOwners = /* @__PURE__ */ new WeakMap();
18
34
  const paragraphPropertySourceCandidates = /* @__PURE__ */ new WeakMap();
19
35
  const paragraphPropertySourceTransferIds = /* @__PURE__ */ new WeakMap();
@@ -91,19 +107,23 @@ const visitDocumentStoryParagraphs = (content, visit) => {
91
107
  visitDocxParagraphs({ documentBody: { content } }, visit);
92
108
  };
93
109
  const assignParagraphPropertySource = (paragraph, xml) => {
94
- paragraphPropertySources.set(paragraph, ownedParagraphPropertySource(paragraph, xml));
95
- paragraphPropertySourceOwners.set(paragraph, paragraph);
110
+ attachParagraphCapture(paragraph, {
111
+ source: ownedParagraphPropertySource(paragraph, xml),
112
+ owner: paragraph
113
+ });
96
114
  };
97
- const getParagraphPropertySource = (paragraph) => paragraphPropertySources.get(paragraph);
115
+ const getParagraphPropertySource = (paragraph) => captureForParagraph(paragraph)?.source;
98
116
  const paragraphPropertySourceMatchesEmission = (source, emission) => source[paragraphPropertySourceEmissionFingerprint] === paragraphFormattingEmissionFingerprint(emission);
99
117
  /** Copy the captured `w:pPr` without claiming the source paragraph's durable identity. */
100
118
  const copyParagraphPropertyCapture = (target, source) => {
101
- const propertySource = paragraphPropertySources.get(source);
102
- if (propertySource) {
103
- const copiedSource = Object.freeze({ ...propertySource });
104
- paragraphPropertySources.set(target, copiedSource);
105
- paragraphPropertySourceOwners.set(target, paragraphPropertySourceOwners.get(source) ?? source);
106
- }
119
+ const capture = captureForParagraph(source);
120
+ if (capture) attachParagraphCapture(target, capture);
121
+ const kind = paragraphPropertySourceTokens.has(source) || paragraphSourceRecordKinds.get(source) === "durable" ? "durable" : "capture-only";
122
+ if (!paragraphSourceRecordKinds.has(target)) paragraphSourceRecordKinds.set(target, kind);
123
+ };
124
+ const copyParagraphCaptureForTransport = (target, source) => {
125
+ const capture = captureForParagraph(source);
126
+ if (capture) paragraphPropertyCaptures.set(target, capture);
107
127
  };
108
128
  const copyParagraphPropertySource = (target, source) => {
109
129
  copyParagraphPropertyCapture(target, source);
@@ -122,6 +142,7 @@ const assignDocumentParagraphPropertySourceContract = (document, sourceDigest) =
122
142
  visitDocumentStoryParagraphs(document.package.document.content, (paragraph) => {
123
143
  const token = tokenForOrdinal(contract, ordinal);
124
144
  paragraphPropertySourceTokens.set(paragraph, token);
145
+ paragraphSourceRecordKinds.set(paragraph, "durable");
125
146
  sources.set(token, paragraph);
126
147
  ordinal += 1;
127
148
  });
@@ -144,7 +165,7 @@ const copyDocumentParagraphPropertySources = (document) => {
144
165
  return sources ? new Map(sources) : void 0;
145
166
  };
146
167
  const paragraphPropertySourceBelongsToDocument = (paragraph, document) => {
147
- const owner = paragraphPropertySourceOwners.get(paragraph);
168
+ const owner = captureForParagraph(paragraph)?.owner;
148
169
  const binding = getDocumentParagraphPropertySourceBinding(document);
149
170
  return owner !== void 0 && binding !== void 0 && binding.sourceOwners.has(owner);
150
171
  };
@@ -158,6 +179,26 @@ const copyDocumentParagraphPropertySourceContract = (target, source) => {
158
179
  if (binding) setDocumentParagraphPropertySourceBinding(target, binding);
159
180
  };
160
181
  const getParagraphPropertySourceToken = (paragraph) => paragraphPropertySourceTokens.get(paragraph);
182
+ /** Byte captures can follow derivations; only explicit copies carry durable tokens. */
183
+ const inspectParagraphSourceIdentity = (paragraph, document) => {
184
+ const binding = getDocumentParagraphPropertySourceBinding(document);
185
+ if (!binding) return { type: "invalid" };
186
+ const capture = captureForParagraph(paragraph);
187
+ if (Object.hasOwn(paragraph, paragraphPropertyCapture) && !capture) return { type: "invalid" };
188
+ const token = getParagraphPropertySourceToken(paragraph);
189
+ if (token !== void 0) {
190
+ if (capture && !binding.sourceOwners.has(capture.owner)) return { type: "invalid" };
191
+ return {
192
+ type: "durable",
193
+ token
194
+ };
195
+ }
196
+ const kind = paragraphSourceRecordKinds.get(paragraph);
197
+ if (kind === "durable" || binding.sourceOwners.has(paragraph)) return { type: "missing-token" };
198
+ if (!capture) return { type: "authored" };
199
+ if (kind === "capture-only" || binding.sourceOwners.has(capture.owner)) return { type: "capture-only" };
200
+ return { type: "invalid" };
201
+ };
161
202
  /** Clone a paragraph while deliberately retaining its parsed `w:pPr` owner. */
162
203
  const cloneParagraphWithPropertySource = (paragraph, overrides) => {
163
204
  const cloned = {
@@ -168,10 +209,14 @@ const cloneParagraphWithPropertySource = (paragraph, overrides) => {
168
209
  return cloned;
169
210
  };
170
211
  /** Clone a paragraph whose formatting provenance is deliberately no longer applicable. */
171
- const cloneParagraphWithoutPropertySource = (paragraph, overrides) => ({
172
- ...paragraph,
173
- ...overrides
174
- });
212
+ const cloneParagraphWithoutPropertySource = (paragraph, overrides) => {
213
+ const cloned = {
214
+ ...paragraph,
215
+ ...overrides
216
+ };
217
+ Reflect.deleteProperty(cloned, paragraphPropertyCapture);
218
+ return cloned;
219
+ };
175
220
  const paragraphsIn = (document) => {
176
221
  const paragraphs = [];
177
222
  visitDocxParagraphs({
@@ -199,6 +244,7 @@ const cloneDocumentWithParagraphPropertySources = (document) => {
199
244
  copyParagraphPropertySource(target, source);
200
245
  }
201
246
  copyDocumentParagraphPropertySourceContract(cloned, document);
247
+ copyHeaderFooterPackageBaselines(cloned, document);
202
248
  return cloned;
203
249
  };
204
250
  const paragraphsInTableCells = (cells) => {
@@ -299,6 +345,7 @@ const tableCellRunContentTraversalByType = {
299
345
  footnoteRef: "leaf",
300
346
  instrText: "leaf",
301
347
  noBreakHyphen: "leaf",
348
+ noteMarker: "leaf",
302
349
  preservedXml: "leaf",
303
350
  renderedPageBreak: "leaf",
304
351
  shape: "shape",
@@ -549,7 +596,7 @@ const transportTableCellsWithParagraphPropertySources = (cells) => {
549
596
  for (const [index, source] of sources.entries()) {
550
597
  const target = targets.at(index);
551
598
  if (!target) panic("The cloned table cells lost a paragraph owner.");
552
- copyParagraphPropertyCapture(target, source);
599
+ copyParagraphCaptureForTransport(target, source);
553
600
  const inspection = inspectTableCellParagraphPropertySourceBinding(source);
554
601
  setTableCellParagraphPropertySourceBinding(target, tableCellParagraphPropertySourceBindingForTransport(source, inspection));
555
602
  }
@@ -568,8 +615,11 @@ const restoreTableCellsWithParagraphPropertySources = (payload) => {
568
615
  copyParagraphPropertyCapture(target, source);
569
616
  if (!Reflect.deleteProperty(target, "_docxParagraphSourceBinding")) panic("Cannot detach paragraph-property transport identity from a table cell.");
570
617
  switch (sourceEntry.binding.type) {
571
- case "authored": break;
618
+ case "authored":
619
+ paragraphSourceRecordKinds.set(target, "capture-only");
620
+ break;
572
621
  case "source":
622
+ paragraphSourceRecordKinds.set(target, "durable");
573
623
  paragraphPropertySourceTokens.set(target, sourceEntry.binding.token);
574
624
  break;
575
625
  default: return sourceEntry.binding;
@@ -593,13 +643,14 @@ const cloneTableCellsWithParagraphPropertyCaptures = (payload) => {
593
643
  for (const [index, sourceEntry] of sourceEntries.entries()) {
594
644
  const target = targets.at(index);
595
645
  if (!target) panic("The cloned table cells lost a paragraph owner.");
596
- copyParagraphPropertyCapture(target, sourceEntry.paragraph);
646
+ copyParagraphCaptureForTransport(target, sourceEntry.paragraph);
597
647
  setTableCellParagraphPropertySourceBinding(target, { type: "authored" });
598
648
  }
599
649
  return cloned;
600
650
  };
601
651
  const linkProseParagraphPropertySource = (proseParagraph, sourceParagraph) => {
602
- if (paragraphPropertySources.has(sourceParagraph)) proseParagraphSourceOwners.set(proseParagraph, paragraphPropertySourceOwners.get(sourceParagraph) ?? sourceParagraph);
652
+ const capture = captureForParagraph(sourceParagraph);
653
+ if (capture) proseParagraphSourceOwners.set(proseParagraph, capture.owner);
603
654
  };
604
655
  /** Create a parsed paragraph with both its same-process owner and durable body token. */
605
656
  const createProseParagraphWithPropertySource = (nodeType, sourceParagraph, options = {}) => {
@@ -704,11 +755,14 @@ const transferProseParagraphPropertySource = (target, source, paraId) => {
704
755
  };
705
756
  const linkParagraphPropertySourceCandidate = (target, source) => {
706
757
  const token = source.attrs[PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR];
707
- if (typeof token === "string") paragraphPropertySourceTokens.set(target, token);
758
+ if (typeof token === "string") {
759
+ paragraphPropertySourceTokens.set(target, token);
760
+ paragraphSourceRecordKinds.set(target, "durable");
761
+ } else paragraphSourceRecordKinds.set(target, "capture-only");
708
762
  const sourceOwner = proseParagraphSourceOwners.get(source);
709
763
  if (sourceOwner) paragraphPropertySourceCandidates.set(target, sourceOwner);
710
764
  };
711
765
  const getParagraphPropertySourceCandidate = (paragraph) => paragraphPropertySourceCandidates.get(paragraph);
712
766
  const getParagraphPropertySourceTransferId = (paragraph) => paragraphPropertySourceTransferIds.get(paragraph);
713
767
  //#endregion
714
- export { PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, assignDocumentParagraphPropertySourceContract, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, markParagraphPropertySourceTransfers, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
768
+ export { PARAGRAPH_PROPERTY_SOURCE_VALIDATION_CODES, PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, PROSE_PARAGRAPH_SOURCE_TOKEN_ATTR, ParagraphPropertySourceValidationError, TABLE_CELL_PARAGRAPH_SOURCE_BINDING_ATTR, TABLE_CELL_PARAGRAPH_SOURCE_PAYLOAD_ERROR_CLASSIFICATIONS, assignDocumentParagraphPropertySourceContract, assignParagraphPropertySource, cloneDocumentWithParagraphPropertySources, cloneParagraphWithPropertySource, cloneParagraphWithoutPropertySource, cloneTableCellsWithParagraphPropertyCaptures, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, createProseParagraphWithPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getExplicitParagraphPropertySourceTransfers, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, inspectParagraphSourceIdentity, isParagraphPropertySourceToken, joinProseParagraphsWithRightPropertySource, linkParagraphPropertySourceCandidate, linkProseParagraphPropertySource, markParagraphPropertySourceTransfers, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceMatchesEmission, paragraphPropertySourceTokenMatchesContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithDetachedParagraphPropertySource, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, setProseParagraphMarkupWithPropertySource, transferProseParagraphPropertySource, transportTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings };
@@ -15,7 +15,7 @@ import { parseFontTable } from "./fontTableParser.js";
15
15
  import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
16
16
  import { parseFooter, parseHeader } from "./headerFooterParser.js";
17
17
  import { DANGLING_FOOTER_REFERENCE_WARNING, DANGLING_HEADER_REFERENCE_WARNING, assignHeaderFooterRoles, normalizeHeaderFooterReferences } from "./headerFooterReferenceNormalization.js";
18
- import { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, refreshHeaderFooterVerbatimFingerprint } from "./headerFooterVerbatim.js";
18
+ import { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, captureHeaderFooterPackageBaselines, refreshHeaderFooterVerbatimFingerprint } from "./headerFooterVerbatim.js";
19
19
  import { extractMetafileRaster, isMetafileMimeType } from "./metafileRaster.js";
20
20
  import { renderEmfSvg } from "./metafileSvg.js";
21
21
  import { DocxModelValidationError, formatDocumentModelIssues, validateFolioDocumentModel } from "./modelValidation.js";
@@ -473,6 +473,7 @@ async function parseDocxWithPreviewBudget(input, options, previewBudget) {
473
473
  document.warnings = formatParseWarnings(parseWarnings);
474
474
  }
475
475
  onProgress("Complete", 100);
476
+ captureHeaderFooterPackageBaselines(document);
476
477
  return document;
477
478
  } catch (error) {
478
479
  if (error instanceof DocxEncryptionError) throw error;
@@ -0,0 +1,4 @@
1
+ //#region src/docx/reviewHistoryNamespace.d.ts
2
+ declare const FOLIO_REVIEW_HISTORY_NAMESPACE = "urn:stella:folio:review-history:1";
3
+ //#endregion
4
+ export { FOLIO_REVIEW_HISTORY_NAMESPACE };
@@ -0,0 +1,4 @@
1
+ //#region src/docx/reviewHistoryNamespace.ts
2
+ const FOLIO_REVIEW_HISTORY_NAMESPACE = "urn:stella:folio:review-history:1";
3
+ //#endregion
4
+ export { FOLIO_REVIEW_HISTORY_NAMESPACE };
@@ -0,0 +1,25 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { XmlElement } from "./xmlParser.js";
3
+ //#region src/docx/reviewResolutionProvenance.d.ts
4
+ type ResolutionJoins = NonNullable<document_d_exports.Insertion["resolutionJoins"]>;
5
+ type BoundaryJoins = NonNullable<document_d_exports.RunPropertyChange["boundaryJoins"]>;
6
+ declare const isResolutionJoins: (value: unknown) => value is ResolutionJoins;
7
+ declare const isParagraphMarkResolutionJoin: (value: unknown) => value is NonNullable<document_d_exports.ParagraphMarkChange["resolutionJoin"]>;
8
+ declare const isBoundaryJoins: (value: unknown) => value is BoundaryJoins;
9
+ declare const ReviewResolutionProvenanceError_base: import("better-result").TaggedErrorClass<"ReviewResolutionProvenanceError">;
10
+ declare class ReviewResolutionProvenanceError extends ReviewResolutionProvenanceError_base<{
11
+ message: string;
12
+ attribute: string;
13
+ reason: "invalid" | "unsupportedVersion" | "unrepresentable";
14
+ }> {}
15
+ declare const parseResolutionJoins: (node: XmlElement) => ResolutionJoins | undefined;
16
+ declare const parseBoundaryJoins: (node: XmlElement) => BoundaryJoins | undefined;
17
+ declare const serializeResolutionJoins: (value: document_d_exports.Insertion["resolutionJoins"]) => string;
18
+ declare const serializeBoundaryJoins: (value: document_d_exports.RunPropertyChange["boundaryJoins"]) => string;
19
+ /** Null carries an owned undefined depth; a missing attribute carries no field. */
20
+ declare const parseParagraphMarkResolutionJoin: (node: XmlElement) => Pick<document_d_exports.ParagraphMarkChange, "resolutionJoin">;
21
+ declare const serializeParagraphMarkResolutionJoin: (mark: document_d_exports.ParagraphMarkChange) => string;
22
+ /** Captured paragraph properties compose the same envelope into a parsed XML element. */
23
+ declare const paragraphMarkResolutionJoinAttributes: (mark: document_d_exports.ParagraphMarkChange) => Record<string, string>;
24
+ //#endregion
25
+ export { ReviewResolutionProvenanceError, isBoundaryJoins, isParagraphMarkResolutionJoin, isResolutionJoins, paragraphMarkResolutionJoinAttributes, parseBoundaryJoins, parseParagraphMarkResolutionJoin, parseResolutionJoins, serializeBoundaryJoins, serializeParagraphMarkResolutionJoin, serializeResolutionJoins };
@@ -0,0 +1,153 @@
1
+ import { FOLIO_REVIEW_HISTORY_NAMESPACE } from "./reviewHistoryNamespace.js";
2
+ import { getAttributeByNamespaceUri } from "./xmlParser.js";
3
+ import { Result, TaggedError } from "better-result";
4
+ import { escapeXmlAttribute } from "@stll/docx-core";
5
+ import { MAX_REVISION_ID } from "@stll/docx-core/model";
6
+ //#region src/docx/reviewResolutionProvenance.ts
7
+ const NAMESPACES = /* @__PURE__ */ new Set([FOLIO_REVIEW_HISTORY_NAMESPACE]);
8
+ const VERSION = 1;
9
+ const MAX_ATTRIBUTE_LENGTH = 32768;
10
+ const SLOT_FIELDS = {
11
+ space: true,
12
+ id: true
13
+ };
14
+ const RETAINED_FIELDS = {
15
+ depth: true,
16
+ source: true,
17
+ target: true
18
+ };
19
+ const JOIN_FIELDS = {
20
+ before: true,
21
+ after: true,
22
+ remove: true,
23
+ retainedAfter: true,
24
+ deferredRemove: true,
25
+ acceptance: true
26
+ };
27
+ const ACCEPTANCE_POLICIES = { "merge-plain-runs": true };
28
+ const PARAGRAPH_MARK_FIELDS = {
29
+ kind: { type: "ooxml" },
30
+ info: { type: "ooxml" },
31
+ resolutionJoin: {
32
+ type: "private",
33
+ attribute: "resolutionJoin"
34
+ }
35
+ };
36
+ const DEFERRED_REMOVAL_FIELDS = {
37
+ depth: true,
38
+ blockers: true
39
+ };
40
+ const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
41
+ const natural = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
42
+ const slots = (value) => Array.isArray(value) && value.every((slot) => record(slot) && Object.keys(slot).every((key) => Object.hasOwn(SLOT_FIELDS, key)) && (slot["space"] === "revision" || slot["space"] === "control") && natural(slot["id"]) && slot["id"] <= MAX_REVISION_ID);
43
+ const retained = (value) => Array.isArray(value) && value.every((item) => {
44
+ if (!record(item)) return false;
45
+ const source = item["source"];
46
+ const target = item["target"];
47
+ return Object.keys(item).every((key) => Object.hasOwn(RETAINED_FIELDS, key)) && natural(item["depth"]) && slots(source) && slots(target) && source.length === target.length && source.every((slot, index) => slot.space === target.at(index)?.space);
48
+ });
49
+ const deferredRemoval = (value) => {
50
+ if (!record(value) || Object.keys(value).some((key) => !Object.hasOwn(DEFERRED_REMOVAL_FIELDS, key))) return false;
51
+ const depth = value["depth"];
52
+ const blockers = value["blockers"];
53
+ return natural(depth) && depth <= MAX_REVISION_ID && Array.isArray(blockers) && blockers.length > 0 && !blockers.includes(void 0) && new Set(blockers).size === blockers.length && blockers.every((blocker) => natural(blocker) && blocker <= MAX_REVISION_ID);
54
+ };
55
+ const deferredRemovals = (value) => {
56
+ if (!Array.isArray(value) || value.length === 0 || value.includes(void 0) || !value.every(deferredRemoval)) return false;
57
+ const groups = value.map(({ depth, blockers }) => JSON.stringify({
58
+ depth,
59
+ blockers
60
+ }));
61
+ return new Set(groups).size === groups.length;
62
+ };
63
+ const isResolutionJoins = (value) => record(value) && Object.keys(value).every((key) => Object.hasOwn(JOIN_FIELDS, key)) && natural(value["before"]) && natural(value["after"]) && natural(value["remove"]) && (!Object.hasOwn(value, "acceptance") || typeof value["acceptance"] === "string" && Object.hasOwn(ACCEPTANCE_POLICIES, value["acceptance"])) && (value["retainedAfter"] === void 0 || retained(value["retainedAfter"])) && (!Object.hasOwn(value, "deferredRemove") || deferredRemovals(value["deferredRemove"]));
64
+ const isParagraphMarkResolutionJoin = (value) => natural(value) && value <= MAX_REVISION_ID;
65
+ const isEncodedParagraphMarkResolutionJoin = (value) => value === null || isParagraphMarkResolutionJoin(value);
66
+ const isBoundaryJoins = (value) => Array.isArray(value) && value.length <= 2 && new Set(value).size === value.length && value.every((side) => side === "before" || side === "after");
67
+ var ReviewResolutionProvenanceError = class extends TaggedError("ReviewResolutionProvenanceError") {};
68
+ const parseAttribute = ({ node, attribute, valid }) => {
69
+ const encoded = getAttributeByNamespaceUri(node, NAMESPACES, attribute);
70
+ if (encoded === null) return void 0;
71
+ const failed = (reason = "invalid") => new ReviewResolutionProvenanceError({
72
+ attribute,
73
+ message: `Invalid or unsupported ${attribute} review provenance.`,
74
+ reason
75
+ });
76
+ if (encoded.length > MAX_ATTRIBUTE_LENGTH) throw failed();
77
+ const parsed = Result.try({
78
+ try: () => JSON.parse(encoded),
79
+ catch: () => failed()
80
+ });
81
+ if (parsed.isErr()) throw parsed.error;
82
+ const decoded = parsed.value;
83
+ if (!record(decoded) || Object.keys(decoded).some((key) => key !== "version" && key !== "value")) throw failed();
84
+ const value = decoded["value"];
85
+ if (!valid(value)) throw failed();
86
+ if (decoded["version"] !== VERSION) throw failed("unsupportedVersion");
87
+ return value;
88
+ };
89
+ const encodeAttribute = ({ attribute, value, valid }) => {
90
+ if (value === void 0) return void 0;
91
+ if (!valid(value)) throw new ReviewResolutionProvenanceError({
92
+ attribute,
93
+ reason: "invalid",
94
+ message: `Invalid or oversized ${attribute} review provenance.`
95
+ });
96
+ const encoded = JSON.stringify({
97
+ version: VERSION,
98
+ value
99
+ });
100
+ if (encoded.length > MAX_ATTRIBUTE_LENGTH) throw new ReviewResolutionProvenanceError({
101
+ attribute,
102
+ reason: "invalid",
103
+ message: `Oversized ${attribute} review provenance.`
104
+ });
105
+ return encoded;
106
+ };
107
+ const serializeAttribute = (options) => {
108
+ const encoded = encodeAttribute(options);
109
+ return encoded === void 0 ? "" : ` folio:${options.attribute}="${escapeXmlAttribute(encoded)}"`;
110
+ };
111
+ const parseResolutionJoins = (node) => parseAttribute({
112
+ node,
113
+ attribute: "resolutionJoins",
114
+ valid: isResolutionJoins
115
+ });
116
+ const parseBoundaryJoins = (node) => parseAttribute({
117
+ node,
118
+ attribute: "boundaryJoins",
119
+ valid: isBoundaryJoins
120
+ });
121
+ const serializeResolutionJoins = (value) => serializeAttribute({
122
+ attribute: "resolutionJoins",
123
+ value,
124
+ valid: isResolutionJoins
125
+ });
126
+ const serializeBoundaryJoins = (value) => serializeAttribute({
127
+ attribute: "boundaryJoins",
128
+ value,
129
+ valid: isBoundaryJoins
130
+ });
131
+ /** Null carries an owned undefined depth; a missing attribute carries no field. */
132
+ const parseParagraphMarkResolutionJoin = (node) => {
133
+ const value = parseAttribute({
134
+ node,
135
+ attribute: PARAGRAPH_MARK_FIELDS.resolutionJoin.attribute,
136
+ valid: isEncodedParagraphMarkResolutionJoin
137
+ });
138
+ return value === void 0 ? {} : { resolutionJoin: value === null ? void 0 : value };
139
+ };
140
+ const paragraphMarkResolutionJoinOptions = (mark) => ({
141
+ attribute: PARAGRAPH_MARK_FIELDS.resolutionJoin.attribute,
142
+ value: Object.hasOwn(mark, "resolutionJoin") ? mark.resolutionJoin ?? null : void 0,
143
+ valid: isEncodedParagraphMarkResolutionJoin
144
+ });
145
+ const serializeParagraphMarkResolutionJoin = (mark) => serializeAttribute(paragraphMarkResolutionJoinOptions(mark));
146
+ /** Captured paragraph properties compose the same envelope into a parsed XML element. */
147
+ const paragraphMarkResolutionJoinAttributes = (mark) => {
148
+ const options = paragraphMarkResolutionJoinOptions(mark);
149
+ const encoded = encodeAttribute(options);
150
+ return encoded === void 0 ? {} : { [`folio:${options.attribute}`]: encoded };
151
+ };
152
+ //#endregion
153
+ export { ReviewResolutionProvenanceError, isBoundaryJoins, isParagraphMarkResolutionJoin, isResolutionJoins, paragraphMarkResolutionJoinAttributes, parseBoundaryJoins, parseParagraphMarkResolutionJoin, parseResolutionJoins, serializeBoundaryJoins, serializeParagraphMarkResolutionJoin, serializeResolutionJoins };
@@ -1,4 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { SaveDiagnosticOptions } from "./saveDiagnostics.js";
2
3
  import { BlockContent, Hyperlink } from "../types/content.js";
3
4
  import { RawDocxContent } from "./unzip.js";
4
5
  import JSZip from "jszip";
@@ -46,7 +47,7 @@ declare const hasUnmaterializedInlineResources: (blocks: BlockContent[]) => bool
46
47
  /**
47
48
  * Options for repacking DOCX
48
49
  */
49
- type RepackOptions = {
50
+ type RepackOptions = SaveDiagnosticOptions & {
50
51
  /** Compression level (0-9, default: 6) */
51
52
  compressionLevel?: number;
52
53
  /** Whether to update modification date in docProps/core.xml */
@@ -185,7 +186,10 @@ declare function hasModelDrivenPictureWatermark(doc: document_d_exports.Document
185
186
  * `sourceZip` supplies each part as it stands before the save so the rebuilt
186
187
  * root can keep any prefix binding only the source document declared.
187
188
  */
188
- declare function collectHeaderFooterUpdates(doc: document_d_exports.Document, sourceZip: JSZip): Promise<Map<string, string>>;
189
+ type CollectHeaderFooterUpdatesOptions = SaveDiagnosticOptions & {
190
+ sourceZip: JSZip;
191
+ };
192
+ declare function collectHeaderFooterUpdates(doc: document_d_exports.Document, { sourceZip, onDiagnostic }: CollectHeaderFooterUpdatesOptions): Promise<Map<string, string>>;
189
193
  /** `word/Footnotes.xml` -> `word/_rels/Footnotes.xml.rels` (casing preserved). */
190
194
  declare function notePartRelsPath(partPath: string): string;
191
195
  /** Update existing core-property values without synthesizing absent metadata. */
@@ -219,7 +223,7 @@ declare function isDocxBuffer(buffer: ArrayBuffer): boolean;
219
223
  * ignored for a document that carries a source package, which keeps the
220
224
  * properties that package already states.
221
225
  */
222
- type DocumentPropertiesOptions = {
226
+ type DocumentPropertiesOptions = SaveDiagnosticOptions & {
223
227
  /** `dc:creator` in `docProps/core.xml`. Omitted when absent. */
224
228
  creator?: string;
225
229
  /** `Application` in `docProps/app.xml`. Omitted, with `AppVersion`, when absent. */