@stll/folio-core 0.55.0 → 0.57.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 (225) hide show
  1. package/dist/ai-edits/apply.js +214 -52
  2. package/dist/ai-edits/batch-claims.d.ts +3 -1
  3. package/dist/ai-edits/batch-claims.js +25 -1
  4. package/dist/ai-edits/headless.js +2 -2
  5. package/dist/ai-edits/table-row-column-mutations.js +17 -3
  6. package/dist/ai-edits/types.d.ts +7 -1
  7. package/dist/ai-suggestions/apply.js +20 -1
  8. package/dist/compare/compare.js +1 -0
  9. package/dist/compare/inline-atoms.js +40 -16
  10. package/dist/compare/inline-provenance.js +12 -5
  11. package/dist/compare/reproducible-package.d.ts +0 -17
  12. package/dist/compare/reproducible-package.js +12 -18
  13. package/dist/compat/eigenpal.d.ts +2 -1
  14. package/dist/compat/eigenpal.js +2 -1
  15. package/dist/controller/canonicalClipboard.d.ts +23 -0
  16. package/dist/controller/canonicalClipboard.js +482 -0
  17. package/dist/controller/canonicalClipboardResources.d.ts +21 -0
  18. package/dist/controller/canonicalClipboardResources.js +382 -0
  19. package/dist/controller/canonicalComments.d.ts +41 -0
  20. package/dist/controller/canonicalComments.js +152 -0
  21. package/dist/controller/canonicalComposition.d.ts +24 -3
  22. package/dist/controller/canonicalComposition.js +208 -48
  23. package/dist/controller/canonicalInlineProjection.d.ts +118 -0
  24. package/dist/controller/canonicalInlineProjection.js +476 -0
  25. package/dist/controller/canonicalInput.d.ts +13 -4
  26. package/dist/controller/canonicalInput.js +62 -14
  27. package/dist/controller/canonicalInputTimer.d.ts +16 -0
  28. package/dist/controller/canonicalInputTimer.js +34 -0
  29. package/dist/controller/canonicalPublicOperations.d.ts +48 -0
  30. package/dist/controller/canonicalPublicOperations.js +648 -0
  31. package/dist/controller/canonicalSelection.d.ts +18 -0
  32. package/dist/controller/canonicalSelection.js +32 -0
  33. package/dist/controller/canonicalSession.d.ts +27 -6
  34. package/dist/controller/canonicalSession.js +298 -116
  35. package/dist/controller/canonicalStoryEditor.d.ts +7 -3
  36. package/dist/controller/canonicalStoryEditor.js +27 -2
  37. package/dist/controller/canonicalStructure.js +57 -4
  38. package/dist/controller/canonicalTocSelection.d.ts +11 -0
  39. package/dist/controller/canonicalTocSelection.js +43 -0
  40. package/dist/controller/folioEditor.d.ts +2 -9
  41. package/dist/controller/folioEditor.js +18 -4
  42. package/dist/controller/headerFooterEditorManager.d.ts +2 -1
  43. package/dist/controller/headerFooterEditorManager.js +26 -11
  44. package/dist/controller/hiddenEditorApi.d.ts +24 -3
  45. package/dist/controller/hiddenEditorApi.js +8 -0
  46. package/dist/controller/hiddenEditorManager.d.ts +5 -1
  47. package/dist/controller/hiddenEditorManager.js +378 -43
  48. package/dist/controller/noteEditorManager.d.ts +2 -1
  49. package/dist/controller/noteEditorManager.js +29 -13
  50. package/dist/document-operations.d.ts +8 -1
  51. package/dist/document-operations.js +6 -4
  52. package/dist/docx/blockPlainText.d.ts +1 -1
  53. package/dist/docx/blockPlainText.js +11 -3
  54. package/dist/docx/builtInStyles.js +13 -6
  55. package/dist/docx/canonicalResourceSave.d.ts +16 -0
  56. package/dist/docx/canonicalResourceSave.js +27 -0
  57. package/dist/docx/canonicalSave.d.ts +29 -0
  58. package/dist/docx/canonicalSave.js +101 -0
  59. package/dist/docx/canonicalStoryRepack.d.ts +1 -1
  60. package/dist/docx/canonicalStoryRepack.js +38 -5
  61. package/dist/docx/containerChildren.gen.d.ts +3 -1
  62. package/dist/docx/containerChildren.gen.js +33 -1
  63. package/dist/docx/documentParser.d.ts +1 -13
  64. package/dist/docx/documentParser.js +1 -47
  65. package/dist/docx/ensureParaIds.js +10 -4
  66. package/dist/docx/fieldParser.js +14 -2
  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 +26 -1
  72. package/dist/docx/headerFooterVerbatim.js +81 -3
  73. package/dist/docx/hyperlinkParser.d.ts +1 -1
  74. package/dist/docx/hyperlinkParser.js +20 -1
  75. package/dist/docx/imageParser.d.ts +13 -1
  76. package/dist/docx/imageParser.js +20 -11
  77. package/dist/docx/inlineWrapperContent.d.ts +4 -4
  78. package/dist/docx/inlineWrapperContent.js +4 -4
  79. package/dist/docx/linkedSdtPreservation.d.ts +10 -0
  80. package/dist/docx/linkedSdtPreservation.js +37 -0
  81. package/dist/docx/metadataPrivacy.js +6 -1
  82. package/dist/docx/packageParts.js +9 -3
  83. package/dist/docx/paragraphParser.js +88 -67
  84. package/dist/docx/paragraphPropertySource.d.ts +22 -1
  85. package/dist/docx/paragraphPropertySource.js +109 -2
  86. package/dist/docx/parser.js +2 -1
  87. package/dist/docx/preservedRunContent.js +2 -1
  88. package/dist/docx/relsParser.js +26 -2
  89. package/dist/docx/removeHeaderFooterParts.js +17 -6
  90. package/dist/docx/rezip.d.ts +49 -5
  91. package/dist/docx/rezip.js +754 -235
  92. package/dist/docx/saveDiagnostics.d.ts +11 -0
  93. package/dist/docx/selectiveSave.d.ts +2 -1
  94. package/dist/docx/selectiveSave.js +42 -11
  95. package/dist/docx/selectiveXmlPatch.js +7 -5
  96. package/dist/docx/serializer/commentSerializer.d.ts +4 -1
  97. package/dist/docx/serializer/commentSerializer.js +7 -3
  98. package/dist/docx/serializer/documentSerializer.d.ts +9 -2
  99. package/dist/docx/serializer/documentSerializer.js +29 -10
  100. package/dist/docx/serializer/paragraphSerializer.js +207 -94
  101. package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
  102. package/dist/docx/server/materializeYjsDocx.js +14 -6
  103. package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
  104. package/dist/docx/storyBlockReplay.d.ts +21 -1
  105. package/dist/docx/storyBlockReplay.js +113 -9
  106. package/dist/docx/storyPlainText.d.ts +20 -0
  107. package/dist/docx/storyPlainText.js +23 -0
  108. package/dist/docx/structuralXmlPatch.js +10 -22
  109. package/dist/docx/styleParser.d.ts +5 -1
  110. package/dist/docx/styleParser.js +1 -1
  111. package/dist/docx/tableParser.d.ts +1 -8
  112. package/dist/docx/tableParser.js +1 -28
  113. package/dist/docx/textBoxParser.d.ts +1 -5
  114. package/dist/docx/textBoxParser.js +1 -20
  115. package/dist/docx/unzip.js +6 -1
  116. package/dist/i18n/messages/ar.gen.js +4 -0
  117. package/dist/i18n/messages/catalogs.gen.d.ts +68 -0
  118. package/dist/i18n/messages/catalogs.gen.js +68 -0
  119. package/dist/i18n/messages/cs.gen.js +4 -0
  120. package/dist/i18n/messages/de.gen.js +4 -0
  121. package/dist/i18n/messages/en.gen.js +4 -0
  122. package/dist/i18n/messages/es.gen.js +4 -0
  123. package/dist/i18n/messages/et.gen.js +4 -0
  124. package/dist/i18n/messages/fr.gen.js +4 -0
  125. package/dist/i18n/messages/he.gen.js +4 -0
  126. package/dist/i18n/messages/hi.gen.js +4 -0
  127. package/dist/i18n/messages/hu.gen.js +4 -0
  128. package/dist/i18n/messages/lt.gen.js +4 -0
  129. package/dist/i18n/messages/lv.gen.js +4 -0
  130. package/dist/i18n/messages/messages.gen.d.ts +4 -0
  131. package/dist/i18n/messages/pl.gen.js +4 -0
  132. package/dist/i18n/messages/pt-BR.gen.js +4 -0
  133. package/dist/i18n/messages/sk.gen.js +4 -0
  134. package/dist/i18n/messages/tr.gen.js +4 -0
  135. package/dist/i18n/messages/zh-CN.gen.js +4 -0
  136. package/dist/index.d.ts +2 -1
  137. package/dist/index.js +2 -1
  138. package/dist/internal/acceptedBlockProjection.d.ts +12 -0
  139. package/dist/internal/acceptedBlockProjection.js +45 -0
  140. package/dist/internal/revisionResolutionInline.js +25 -3
  141. package/dist/managers/DocumentLoaderManager.js +2 -1
  142. package/dist/markdown/escape.d.ts +3 -1
  143. package/dist/markdown/escape.js +9 -2
  144. package/dist/markdown/renderBlock.js +5 -120
  145. package/dist/markdown/renderParagraph.js +4 -2
  146. package/dist/markdown/renderRuns.d.ts +7 -1
  147. package/dist/markdown/renderRuns.js +33 -24
  148. package/dist/panel-layout.d.ts +11 -9
  149. package/dist/panel-layout.js +13 -9
  150. package/dist/prosemirror/addTrackedDeletionMark.d.ts +18 -0
  151. package/dist/prosemirror/addTrackedDeletionMark.js +87 -0
  152. package/dist/prosemirror/attrs/index.js +5 -0
  153. package/dist/prosemirror/canonicalCommands.d.ts +28 -4
  154. package/dist/prosemirror/canonicalCommands.js +11 -4
  155. package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
  156. package/dist/prosemirror/canonicalSelectionRange.js +15 -0
  157. package/dist/prosemirror/clipboardIntent.d.ts +7 -0
  158. package/dist/prosemirror/clipboardIntent.js +9 -0
  159. package/dist/prosemirror/commands/comments.js +60 -35
  160. package/dist/prosemirror/commands/pageBreak.js +2 -2
  161. package/dist/prosemirror/commands/pastePlainText.js +2 -1
  162. package/dist/prosemirror/commands/propertyChangeScope.js +3 -6
  163. package/dist/prosemirror/commands/resolveParagraphProperties.js +22 -13
  164. package/dist/prosemirror/commentOps.js +15 -1
  165. package/dist/prosemirror/conversion/fromProseDoc.js +22 -3
  166. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
  167. package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
  168. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -1
  169. package/dist/prosemirror/conversion/toProseDoc.js +124 -22
  170. package/dist/prosemirror/extensions/core/ParagraphExtension.js +61 -35
  171. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +74 -25
  172. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +11 -0
  173. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +65 -29
  174. package/dist/prosemirror/extensions/marks/markUtils.js +11 -8
  175. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
  176. package/dist/prosemirror/fieldRepresentation.d.ts +8 -0
  177. package/dist/prosemirror/fieldRepresentation.js +18 -0
  178. package/dist/prosemirror/hyperlinkRemoval.d.ts +13 -1
  179. package/dist/prosemirror/hyperlinkRemoval.js +28 -9
  180. package/dist/prosemirror/insertOperations.js +2 -1
  181. package/dist/prosemirror/listNumbering.js +11 -5
  182. package/dist/prosemirror/noteReferenceOccurrenceId.d.ts +5 -0
  183. package/dist/prosemirror/noteReferenceOccurrenceId.js +5 -0
  184. package/dist/prosemirror/noteReferenceOccurrences.d.ts +21 -0
  185. package/dist/prosemirror/noteReferenceOccurrences.js +185 -0
  186. package/dist/prosemirror/noteReferenceReview.js +14 -1
  187. package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
  188. package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
  189. package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
  190. package/dist/prosemirror/plugins/documentStyles.js +17 -10
  191. package/dist/prosemirror/plugins/suggestionMode.js +48 -48
  192. package/dist/prosemirror/preservedXmlReview.d.ts +6 -0
  193. package/dist/prosemirror/preservedXmlReview.js +35 -0
  194. package/dist/prosemirror/runFormattingFromMarks.js +2 -2
  195. package/dist/prosemirror/runFormattingReconciliation.js +5 -4
  196. package/dist/prosemirror/runPropertyChangeResolution.d.ts +8 -1
  197. package/dist/prosemirror/runPropertyChangeResolution.js +24 -2
  198. package/dist/prosemirror/runStyleFormatting.d.ts +3 -1
  199. package/dist/prosemirror/runStyleFormatting.js +3 -1
  200. package/dist/prosemirror/schema/marks.d.ts +1 -0
  201. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  202. package/dist/prosemirror/styles/paragraphStyleCascade.js +4 -1
  203. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +15 -15
  204. package/dist/prosemirror/styles/resolvedStyleAttrs.js +69 -28
  205. package/dist/prosemirror/textInput.d.ts +5 -1
  206. package/dist/prosemirror/textInput.js +3 -3
  207. package/dist/prosemirror/trackedRevisionPath.d.ts +5 -1
  208. package/dist/prosemirror/trackedRevisionPath.js +39 -1
  209. package/dist/prosemirror/validation.js +3 -5
  210. package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
  211. package/dist/prosemirror/yjsDocumentMetadata.js +37 -8
  212. package/dist/types/canonicalCapabilities.d.ts +201 -0
  213. package/dist/types/canonicalCapabilities.js +198 -0
  214. package/dist/types/canonicalComments.d.ts +51 -0
  215. package/dist/types/canonicalComments.js +0 -0
  216. package/dist/types/canonicalSave.d.ts +11 -0
  217. package/dist/types/canonicalSave.js +0 -0
  218. package/dist/types/canonicalSections.d.ts +12 -0
  219. package/dist/types/canonicalSections.js +0 -0
  220. package/dist/types/docxSerialization.d.ts +14 -0
  221. package/dist/types/docxSerialization.js +7 -0
  222. package/dist/utils/headingCollector.js +45 -4
  223. package/dist/utils/outlineDepth.d.ts +9 -0
  224. package/dist/utils/outlineDepth.js +9 -0
  225. package/package.json +4 -4
@@ -1,4 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { CanonicalGap } from "../types/canonicalCapabilities.js";
2
3
  import { HiddenEditorApi } from "./hiddenEditorApi.js";
3
4
  import { NoteStoryKey } from "../types/editor-story.js";
4
5
  import { Plugin } from "prosemirror-state";
@@ -15,7 +16,7 @@ type NoteEditorManagerDeps = {
15
16
  getDocument: () => document_d_exports.Document | null;
16
17
  getCanonicalApi?: (() => HiddenEditorApi | null) | undefined;
17
18
  getExperimentalSession?: (() => "canonical" | undefined) | undefined;
18
- onSessionRefusal?: ((reason: string) => void) | undefined;
19
+ onSessionRefusal?: ((reason: string, gap: CanonicalGap, error?: Error) => void) | undefined;
19
20
  getHost: () => HTMLElement | null;
20
21
  getPlugins?: (() => Plugin[]) | undefined;
21
22
  getStyles: () => document_d_exports.StyleDefinitions | null | undefined;
@@ -7,10 +7,13 @@ import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js"
7
7
  import { createStarterKit } from "../prosemirror/extensions/StarterKit.js";
8
8
  import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
9
9
  import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
10
+ import { noteReferenceTransactionIssue } from "../prosemirror/noteReferenceOccurrences.js";
10
11
  import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNumbering.js";
11
- import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
12
+ import { createDocumentStyleContextPlugin, createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
12
13
  import { schema } from "../prosemirror/schema/index.js";
13
14
  import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
15
+ import { CANONICAL_GAP, usesCanonicalSession } from "../types/canonicalCapabilities.js";
16
+ import { CanonicalSessionError } from "./canonicalSession.js";
14
17
  import { createCanonicalStoryEditor } from "./canonicalStoryEditor.js";
15
18
  import { OP_STORIES } from "@stll/docx-core/ops";
16
19
  import { EditorState, TextSelection } from "prosemirror-state";
@@ -87,11 +90,12 @@ const createNoteEditorManager = (deps) => {
87
90
  mounted.clear();
88
91
  active = null;
89
92
  };
93
+ const getOwnedDocument = () => usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) ? deps.getCanonicalApi?.()?.getCanonicalDocument() ?? null : deps.getDocument();
90
94
  const sync = () => {
91
95
  const host = deps.getHost();
92
96
  if (!host) return;
93
- if (deps.getExperimentalSession?.() === "canonical" && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
94
- const document = deps.getExperimentalSession?.() === "canonical" ? deps.getCanonicalApi?.()?.getCanonicalDocument() ?? deps.getDocument() : deps.getDocument();
97
+ if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
98
+ const document = getOwnedDocument();
95
99
  const styles = deps.getStyles();
96
100
  const theme = deps.getTheme();
97
101
  const numbering = document?.package.numbering;
@@ -115,7 +119,7 @@ const createNoteEditorManager = (deps) => {
115
119
  const existing = mounted.get(key);
116
120
  if (existing) {
117
121
  if (existing.mountNode.parentElement !== host) host.append(existing.mountNode);
118
- if (deps.getExperimentalSession?.() === "canonical") {
122
+ if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) {
119
123
  const projected = deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
120
124
  kind: storyKey.kind,
121
125
  id: storyKey.noteId
@@ -175,7 +179,7 @@ const createNoteEditorManager = (deps) => {
175
179
  },
176
180
  getView: () => view,
177
181
  getApi: () => deps.getCanonicalApi?.() ?? null,
178
- enabled: () => deps.getExperimentalSession?.() === "canonical",
182
+ enabled: () => usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting),
179
183
  onRefusal: deps.onSessionRefusal,
180
184
  onSelectionChange: () => deps.onTransaction?.({
181
185
  ...storyKey,
@@ -184,11 +188,11 @@ const createNoteEditorManager = (deps) => {
184
188
  selectionChanged: true
185
189
  })
186
190
  });
187
- const canonicalProjection = deps.getExperimentalSession?.() === "canonical" ? deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
191
+ const canonicalProjection = usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) ? deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
188
192
  kind: storyKey.kind,
189
193
  id: storyKey.noteId
190
194
  }) : null;
191
- if (deps.getExperimentalSession?.() === "canonical" && !canonicalProjection) {
195
+ if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) && !canonicalProjection) {
192
196
  manager.destroy();
193
197
  mountNode.remove();
194
198
  continue;
@@ -197,16 +201,20 @@ const createNoteEditorManager = (deps) => {
197
201
  ...canonical.props,
198
202
  state: canonicalProjection ? EditorState.create({
199
203
  doc: canonicalProjection,
200
- plugins: [createDocumentStylesPlugin(styles), createDocumentNumberingPlugin(numbering)]
204
+ plugins: [createDocumentStyleContextPlugin(styles), createDocumentNumberingPlugin(numbering)]
201
205
  }) : buildInitialState(proseDocument, styles, numbering, manager, externalPlugins),
202
206
  dispatchTransaction(transaction) {
203
207
  if (canonical.dispatch(transaction)) return;
204
- view.updateState(view.state.apply(transaction));
208
+ const nextState = view.state.apply(transaction);
209
+ const docChanged = !nextState.doc.eq(view.state.doc);
210
+ view.updateState(nextState);
211
+ const noteIssue = noteReferenceTransactionIssue(transaction);
212
+ if (noteIssue) deps.onSessionRefusal?.(noteIssue.message, CANONICAL_GAP.dispatch, noteIssue);
205
213
  const mountedStory = mounted.get(key);
206
- if (mountedStory && transaction.docChanged) mountedStory.dirty = true;
214
+ if (mountedStory && docChanged) mountedStory.dirty = true;
207
215
  deps.onTransaction?.({
208
216
  ...storyKey,
209
- docChanged: transaction.docChanged,
217
+ docChanged,
210
218
  selectionChanged: transaction.selectionSet,
211
219
  view
212
220
  });
@@ -244,9 +252,17 @@ const createNoteEditorManager = (deps) => {
244
252
  destroy,
245
253
  getActive: () => active,
246
254
  getView: (story) => mounted.get(storyMapKey(story))?.view ?? null,
247
- listStories: () => enumerateDocumentNoteStories(deps.getDocument()),
255
+ listStories: () => enumerateDocumentNoteStories(getOwnedDocument()),
248
256
  snapshotDocument: (document) => {
249
- if (deps.getExperimentalSession?.() === "canonical") return deps.getCanonicalApi?.()?.getCanonicalDocument() ?? document;
257
+ if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) {
258
+ const canonical = deps.getCanonicalApi?.()?.getCanonicalDocument();
259
+ if (canonical) return canonical;
260
+ throw new CanonicalSessionError({
261
+ gap: CANONICAL_GAP.authorityRouting,
262
+ reason: "refused",
263
+ message: "Canonical story snapshot is unavailable."
264
+ });
265
+ }
250
266
  let footnotes = document.package.footnotes;
251
267
  let endnotes = document.package.endnotes;
252
268
  let footnotesChanged = false;
@@ -132,6 +132,7 @@ type FolioDocumentOperationIssue = {
132
132
  code: FolioAIEditSkippedOperation["reason"];
133
133
  retryable: boolean;
134
134
  recovery: FolioDocumentOperationRecovery;
135
+ canonicalRefusal?: FolioAIEditSkippedOperation["canonicalRefusal"];
135
136
  /** The skip's detail, when it has one; see {@link FolioAIEditSkippedOperation.message}. */
136
137
  message?: string;
137
138
  };
@@ -264,6 +265,12 @@ type FolioDocumentOperationResult = (FolioDocumentOperationResultBase & {
264
265
  declare const getFolioDocumentOperationIssues: (operations: readonly FolioDocumentOperation[], skipped: readonly FolioAIEditSkippedOperation[]) => FolioDocumentOperationIssue[];
265
266
  /** Build deterministic affected-target receipts from operations and their applied entries. */
266
267
  declare const getFolioDocumentOperationReceipts: (operations: readonly FolioDocumentOperation[], applied: readonly FolioAIEditAppliedOperation[]) => FolioDocumentOperationReceipt[];
268
+ type GetFolioDocumentOperationReceiptsForStoryOptions = {
269
+ operations: readonly FolioDocumentOperation[];
270
+ applied: readonly FolioAIEditAppliedOperation[];
271
+ story: FolioDocumentOperationStory;
272
+ };
273
+ declare const getFolioDocumentOperationReceiptsForStory: ({ operations, applied, story }: GetFolioDocumentOperationReceiptsForStoryOptions) => FolioDocumentOperationReceipt[];
267
274
  type ApplyFolioDocumentOperationsOptions = {
268
275
  view: FolioAIEditView;
269
276
  snapshot: FolioAIEditSnapshot;
@@ -301,4 +308,4 @@ type ApplyFolioDocumentOperationsOptions = {
301
308
  };
302
309
  declare const applyFolioDocumentOperations: ({ view, snapshot, batch, story, author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates, replacementBackground, undefinedStyles }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
303
310
  //#endregion
304
- export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PAGE_BREAK_CLEAR_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
311
+ export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PAGE_BREAK_CLEAR_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOperationReceiptsForStory, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -1072,22 +1072,24 @@ const recoveryByReason = {
1072
1072
  missingStyle: "refreshDocument",
1073
1073
  missingNumbering: "refreshDocument",
1074
1074
  invalidResult: "refreshDocument",
1075
- pendingDeletion: "resolveTrackedChange"
1075
+ pendingDeletion: "resolveTrackedChange",
1076
+ pendingParagraphMarkDeletion: "resolveTrackedChange"
1076
1077
  };
1077
1078
  const getFolioDocumentOperationIssues = (operations, skipped) => {
1078
1079
  const indexById = /* @__PURE__ */ new Map();
1079
1080
  operations.forEach(({ id }, index) => {
1080
1081
  indexById.set(id, index);
1081
1082
  });
1082
- return skipped.map(({ id, reason, message }) => {
1083
+ return skipped.map(({ id, reason, message, canonicalRefusal }) => {
1083
1084
  const operationIndex = indexById.get(id) ?? -1;
1084
1085
  return {
1085
1086
  operationId: id,
1086
1087
  operationIndex,
1087
1088
  path: `$.operations[${operationIndex}]`,
1088
1089
  code: reason,
1089
- retryable: reason !== "emptyOperation" && reason !== "noopOperation" && reason !== "pendingParagraphPropertyChange" && reason !== "pendingRunPropertyChange" && reason !== "pendingDeletion" && reason !== "payloadDoesNotFit" && reason !== "protectedReference" && reason !== "invalidResult",
1090
+ retryable: reason !== "emptyOperation" && reason !== "noopOperation" && reason !== "pendingParagraphPropertyChange" && reason !== "pendingRunPropertyChange" && reason !== "pendingDeletion" && reason !== "pendingParagraphMarkDeletion" && reason !== "payloadDoesNotFit" && reason !== "protectedReference" && reason !== "invalidResult",
1090
1091
  recovery: recoveryByReason[reason],
1092
+ ...canonicalRefusal === void 0 ? {} : { canonicalRefusal },
1091
1093
  ...message !== void 0 && { message }
1092
1094
  };
1093
1095
  });
@@ -1468,4 +1470,4 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
1468
1470
  };
1469
1471
  };
1470
1472
  //#endregion
1471
- export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PAGE_BREAK_CLEAR_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
1473
+ export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PAGE_BREAK_CLEAR_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOperationReceiptsForStory, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -1,6 +1,6 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  //#region src/docx/blockPlainText.d.ts
3
- /** One entry per block, so callers can join or count lines as they need. */
3
+ /** One entry per accepted block; deleted breaks and table structure resolve together. */
4
4
  declare const collectBlockTexts: (blocks: readonly document_d_exports.BlockContent[]) => string[];
5
5
  declare const blockPlainText: (blocks: readonly document_d_exports.BlockContent[]) => string;
6
6
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { projectAcceptedBlocks } from "../internal/acceptedBlockProjection.js";
1
2
  import { ALT_CHUNK_READER_DIAGNOSTIC, isAltChunkMarkup } from "./altChunk.js";
2
3
  import { OPAQUE_REVISION_CARRIER_READER_DIAGNOSTIC, isOpaqueNestedRowMarkup, opaqueRevisionCarrierName } from "./opaqueCarrier.js";
3
4
  import { getParagraphText } from "./paragraphParser.js";
@@ -18,7 +19,7 @@ import { panic } from "better-result";
18
19
  * everything below block level and reads the accepted tracked-change view.
19
20
  */
20
21
  /** One entry per block, so callers can join or count lines as they need. */
21
- const collectBlockTexts = (blocks) => {
22
+ const collectResolvedBlockTexts = (blocks) => {
22
23
  const texts = [];
23
24
  for (const block of blocks) switch (block.type) {
24
25
  case "paragraph":
@@ -27,12 +28,12 @@ const collectBlockTexts = (blocks) => {
27
28
  case "table":
28
29
  for (const row of block.rows) {
29
30
  if (row.formatting?.hidden === true) continue;
30
- texts.push(row.cells.map((cell) => collectBlockTexts(cell.content).join("\n")).join(" "));
31
+ texts.push(row.cells.map((cell) => collectResolvedBlockTexts(cell.content).join("\n")).join(" "));
31
32
  }
32
33
  break;
33
34
  case "blockSdt":
34
35
  case "blockCustomXml":
35
- texts.push(...collectBlockTexts(block.content));
36
+ texts.push(...collectResolvedBlockTexts(block.content));
36
37
  break;
37
38
  case "preservedBlock":
38
39
  if (isAltChunkMarkup(block.xml)) texts.push(block.readerText === void 0 ? ALT_CHUNK_READER_DIAGNOSTIC : `${ALT_CHUNK_READER_DIAGNOSTIC}\n${block.readerText}`);
@@ -45,6 +46,13 @@ const collectBlockTexts = (blocks) => {
45
46
  }
46
47
  return texts;
47
48
  };
49
+ /** One entry per accepted block; deleted breaks and table structure resolve together. */
50
+ const collectBlockTexts = (blocks) => {
51
+ const projection = projectAcceptedBlocks(blocks, void 0);
52
+ const texts = collectResolvedBlockTexts(projection?.blocks ?? blocks);
53
+ if (projection?.completeness === "partial") texts.push("[Unresolved tracked structural changes]");
54
+ return texts;
55
+ };
48
56
  const blockPlainText = (blocks) => collectBlockTexts(blocks).join("\n");
49
57
  //#endregion
50
58
  export { blockPlainText, collectBlockTexts };
@@ -1,5 +1,5 @@
1
1
  import { BUILT_IN_DEFAULT_PARAGRAPH_STYLE_NAME, resolveDefaultParagraphStyle } from "./defaultParagraphStyle.js";
2
- import { headingLevelOf } from "@stll/docx-core/model";
2
+ import { headingLevelOf, headingOutlineLevel } from "@stll/docx-core/model";
3
3
  //#region src/docx/builtInStyles.ts
4
4
  /**
5
5
  * Whether a paragraph is a heading, and of which level, without reading the
@@ -134,20 +134,27 @@ const headingOutlineLevelFromStyleName = (name) => {
134
134
  const CANONICAL_BY_NORMALIZED = new Map(Object.values(BUILT_IN_STYLE_NAME).map((name) => [normalizeStyleName(name), name]));
135
135
  const asBuiltInName = (normalized) => CANONICAL_BY_NORMALIZED.get(normalized);
136
136
  /**
137
- * The outline level a style chain sets: the style's own `w:outlineLvl`, else
138
- * the nearest ancestor's (17.7.1). `styleParser` already flattens `w:basedOn`
139
- * for a parsed package, but a style set built in memory carries the raw chain,
140
- * so the walk keeps both kinds of input on the same answer. `seen` guards the
141
- * circular `basedOn` a malformed package can contain.
137
+ * The effective outline contribution of a style chain: the nearest explicit
138
+ * `w:outlineLvl` wins across the chain; only when none exists does the nearest
139
+ * built-in heading name supply its implicit level. `styleParser` already flattens
140
+ * `w:basedOn` for a parsed package, but a style set built in memory carries the
141
+ * raw chain, so the walk keeps both kinds of input on the same answer. `seen`
142
+ * guards the circular `basedOn` a malformed package can contain.
142
143
  */
143
144
  const inheritedOutlineLevel = (style, styleById) => {
144
145
  const seen = /* @__PURE__ */ new Set();
145
146
  let current = style;
147
+ let nearestImplicitHeadingLevel;
146
148
  while (current && !seen.has(current.styleId)) {
147
149
  seen.add(current.styleId);
148
150
  if (current.pPr?.outlineLevel !== void 0) return current.pPr.outlineLevel;
151
+ if (nearestImplicitHeadingLevel === void 0) {
152
+ const namedLevel = headingOutlineLevelFromStyleName(current.name);
153
+ if (namedLevel !== void 0) nearestImplicitHeadingLevel = headingOutlineLevel(namedLevel);
154
+ }
149
155
  current = current.basedOn === void 0 ? void 0 : styleById.get(current.basedOn);
150
156
  }
157
+ return nearestImplicitHeadingLevel;
151
158
  };
152
159
  /** The level a heading style is indexed under, or undefined if it is not one. */
153
160
  const headingLevelForIndex = (namedLevel, statedLevel) => {
@@ -0,0 +1,16 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
3
+ import { SaveDiagnostic } from "./saveDiagnostics.js";
4
+ //#region src/docx/canonicalResourceSave.d.ts
5
+ declare const CanonicalResourceSaveRefusalError_base: import("better-result").TaggedErrorClass<"CanonicalResourceSaveRefusalError">;
6
+ declare class CanonicalResourceSaveRefusalError extends CanonicalResourceSaveRefusalError_base<{
7
+ message: string;
8
+ gap: typeof CANONICAL_GAP.resourceReplacement;
9
+ diagnostic: Extract<SaveDiagnostic, {
10
+ type: "canonicalResourceReplacement";
11
+ }>;
12
+ }> {}
13
+ /** Full and selective package writers preserve existing style/media source entries. */
14
+ declare const canonicalResourceReplacementOf: (document: document_d_exports.Document) => string | undefined;
15
+ //#endregion
16
+ export { CanonicalResourceSaveRefusalError, canonicalResourceReplacementOf };
@@ -0,0 +1,27 @@
1
+ import { canonicalJson } from "../utils/canonicalJson.js";
2
+ import { getDocumentSourceBaseline } from "./headerFooterVerbatim.js";
3
+ import { TaggedError } from "better-result";
4
+ //#region src/docx/canonicalResourceSave.ts
5
+ var CanonicalResourceSaveRefusalError = class extends TaggedError("CanonicalResourceSaveRefusalError") {};
6
+ /** Full and selective package writers preserve existing style/media source entries. */
7
+ const canonicalResourceReplacementOf = (document) => {
8
+ const source = getDocumentSourceBaseline(document);
9
+ if (source.type !== "captured" || !document.originalBuffer) return void 0;
10
+ const baseline = source.resourceStyles;
11
+ const current = document.package.styles;
12
+ if (baseline) {
13
+ const { styles: sourceStyles, ...sourceDefaults } = baseline;
14
+ const { styles: currentStyles, ...currentDefaults } = current ?? { styles: [] };
15
+ const currentById = new Map(currentStyles.map((style) => [style.styleId, style]));
16
+ if (canonicalJson(sourceDefaults) !== canonicalJson(currentDefaults) || sourceStyles.some((style) => canonicalJson(style) !== canonicalJson(currentById.get(style.styleId)))) return "word/styles.xml";
17
+ }
18
+ for (const [path, media] of source.resourceMedia) {
19
+ const currentMedia = document.package.media?.get(path);
20
+ if (!currentMedia || media.mimeType !== currentMedia.mimeType) return path;
21
+ const sourceBytes = new Uint8Array(media.data);
22
+ const currentBytes = new Uint8Array(currentMedia.data);
23
+ if (sourceBytes.length !== currentBytes.length || sourceBytes.some((byte, index) => byte !== currentBytes[index])) return path;
24
+ }
25
+ };
26
+ //#endregion
27
+ export { CanonicalResourceSaveRefusalError, canonicalResourceReplacementOf };
@@ -0,0 +1,29 @@
1
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
2
+ import { SaveDiagnostic } from "./saveDiagnostics.js";
3
+ import { CanonicalSaveSnapshot } from "../types/canonicalSave.js";
4
+ import { FolioGetDocxOptions } from "../types/docxSerialization.js";
5
+ import { TripwireResult } from "./selectiveSaveTripwire.js";
6
+ import { FolioSelectiveSaveFlags } from "./selectiveSaveFlags.js";
7
+ //#region src/docx/canonicalSave.d.ts
8
+ declare const CanonicalSaveDiagnosticError_base: import("better-result").TaggedErrorClass<"CanonicalSaveDiagnosticError">;
9
+ /** Forward a fidelity fallback through the adapters' existing error channel. */
10
+ declare class CanonicalSaveDiagnosticError extends CanonicalSaveDiagnosticError_base<{
11
+ message: string;
12
+ gap: typeof CANONICAL_GAP.save;
13
+ diagnostic: SaveDiagnostic;
14
+ }> {}
15
+ type SerializeCanonicalSaveOptions = {
16
+ snapshot: CanonicalSaveSnapshot;
17
+ options?: FolioGetDocxOptions | undefined;
18
+ featureFlags?: FolioSelectiveSaveFlags | undefined;
19
+ };
20
+ /** Model and change signals come from one committed snapshot, never from PM. */
21
+ declare const serializeCanonicalSave: ({ snapshot, options, featureFlags }: SerializeCanonicalSaveOptions) => Promise<{
22
+ buffer: ArrayBuffer;
23
+ document: import("@stll/docx-core").Document;
24
+ version: number;
25
+ diagnostics: SaveDiagnostic[];
26
+ tripwireResult: TripwireResult | null;
27
+ }>;
28
+ //#endregion
29
+ export { CanonicalSaveDiagnosticError, serializeCanonicalSave };
@@ -0,0 +1,101 @@
1
+ import { withoutUnreferencedNotes } from "../prosemirror/noteReferenceReview.js";
2
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
3
+ import { FOLIO_DOCX_SERIALIZATION_MODE } from "../types/docxSerialization.js";
4
+ import { CanonicalResourceSaveRefusalError, canonicalResourceReplacementOf } from "./canonicalResourceSave.js";
5
+ import { repackWithCanonicalStoryRemovals } from "./canonicalStoryRepack.js";
6
+ import { getDocumentSourceBaseline } from "./headerFooterVerbatim.js";
7
+ import { resolveSelectiveSaveFlags } from "./selectiveSaveFlags.js";
8
+ import { Result, TaggedError } from "better-result";
9
+ //#region src/docx/canonicalSave.ts
10
+ /** Forward a fidelity fallback through the adapters' existing error channel. */
11
+ var CanonicalSaveDiagnosticError = class extends TaggedError("CanonicalSaveDiagnosticError") {};
12
+ /** Model and change signals come from one committed snapshot, never from PM. */
13
+ const serializeCanonicalSave = async ({ snapshot, options, featureFlags }) => {
14
+ const document = withoutUnreferencedNotes(snapshot.document);
15
+ const replacementPart = snapshot.structure === "changed" ? canonicalResourceReplacementOf(document) : void 0;
16
+ if (replacementPart) {
17
+ const diagnostic = {
18
+ type: "canonicalResourceReplacement",
19
+ gap: CANONICAL_GAP.resourceReplacement,
20
+ part: replacementPart
21
+ };
22
+ options?.onDiagnostic?.(diagnostic);
23
+ throw new CanonicalResourceSaveRefusalError({
24
+ message: "Canonical save cannot preserve this package resource replacement.",
25
+ gap: CANONICAL_GAP.resourceReplacement,
26
+ diagnostic
27
+ });
28
+ }
29
+ const baseline = document.originalBuffer;
30
+ const flags = resolveSelectiveSaveFlags(featureFlags);
31
+ const diagnostics = [];
32
+ const onDiagnostic = (diagnostic) => {
33
+ if (!diagnostics.some((existing) => existing.type === diagnostic.type && existing.part === diagnostic.part)) diagnostics.push(diagnostic);
34
+ };
35
+ const { repackDocx, createDocx } = await import("./rezip.js");
36
+ const useSelective = flags.selectiveSave && options?.mode !== FOLIO_DOCX_SERIALIZATION_MODE.full;
37
+ let selectiveBuffer = null;
38
+ if (baseline && (useSelective || flags.selectiveSaveTripwire)) {
39
+ const { attemptSelectiveSave } = await import("./selectiveSave.js");
40
+ selectiveBuffer = await attemptSelectiveSave(document, baseline, {
41
+ bodyAuthority: "canonical",
42
+ changedParaIds: new Set(snapshot.changedBlockIds),
43
+ structuralChange: snapshot.structure === "changed",
44
+ hasUntrackedChanges: false,
45
+ maxBytes: flags.selectiveSaveMaxBytes,
46
+ onDiagnostic
47
+ });
48
+ if (useSelective && !selectiveBuffer) onDiagnostic({
49
+ type: "selectiveSaveRefused",
50
+ part: "word/document.xml"
51
+ });
52
+ }
53
+ const repack = () => {
54
+ if (baseline && getDocumentSourceBaseline(document).type === "missing") onDiagnostic({
55
+ type: "sourceReplayUnavailable",
56
+ part: "word/document.xml"
57
+ });
58
+ return baseline ? repackWithCanonicalStoryRemovals({
59
+ document,
60
+ repack: () => repackDocx(document, {
61
+ onDiagnostic,
62
+ bodyAuthority: "canonical"
63
+ })
64
+ }) : createDocx(document, {
65
+ onDiagnostic,
66
+ bodyAuthority: "canonical"
67
+ });
68
+ };
69
+ let buffer = useSelective ? selectiveBuffer : null;
70
+ let fullBuffer = null;
71
+ if (!buffer) {
72
+ fullBuffer = await repack();
73
+ buffer = fullBuffer;
74
+ } else if (flags.selectiveSaveTripwire) {
75
+ const full = await Result.tryPromise({
76
+ try: repack,
77
+ catch: (error) => error
78
+ });
79
+ if (full.isOk()) fullBuffer = full.value;
80
+ }
81
+ let tripwireResult = null;
82
+ if (flags.selectiveSaveTripwire && fullBuffer) {
83
+ const { compareSelectiveVsFull } = await import("./selectiveSaveTripwire.js");
84
+ const capturedFullBuffer = fullBuffer;
85
+ const compared = await Result.tryPromise({
86
+ try: () => compareSelectiveVsFull(selectiveBuffer, capturedFullBuffer),
87
+ catch: (error) => error
88
+ });
89
+ if (compared.isOk()) tripwireResult = compared.value;
90
+ }
91
+ for (const diagnostic of diagnostics) options?.onDiagnostic?.(diagnostic);
92
+ return {
93
+ buffer,
94
+ document,
95
+ version: snapshot.version,
96
+ diagnostics,
97
+ tripwireResult
98
+ };
99
+ };
100
+ //#endregion
101
+ export { CanonicalSaveDiagnosticError, serializeCanonicalSave };
@@ -4,7 +4,7 @@ type CanonicalStoryRepackOptions<T> = {
4
4
  document: document_d_exports.Document;
5
5
  repack: () => Promise<T>;
6
6
  };
7
- /** Authorize only references missing from the committed canonical snapshot, with exact multiplicity. */
7
+ /** Authorize exact canonical section removals and references missing from the committed snapshot. */
8
8
  declare const repackWithCanonicalStoryRemovals: <T>({ document, repack }: CanonicalStoryRepackOptions<T>) => Promise<T>;
9
9
  //#endregion
10
10
  export { repackWithCanonicalStoryRemovals };
@@ -1,17 +1,28 @@
1
+ import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
1
2
  import { withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
2
3
  import { readDocumentSectionFacts } from "./documentSectionFacts.js";
4
+ import { parseDocx } from "./parser.js";
5
+ import { assertSectionCarriersMatchModel } from "./rezip.js";
3
6
  import { serializeDocument } from "./serializer/documentSerializer.js";
4
7
  import JSZip from "jszip";
5
8
  //#region src/docx/canonicalStoryRepack.ts
6
9
  /** Canonical snapshots own explicit section references; the PM projection does not. */
7
10
  const referenceKey = ({ element, type, rId }) => `${element}:${type}:${rId}`;
8
- /** Authorize only references missing from the committed canonical snapshot, with exact multiplicity. */
11
+ const xmlFingerprint = async (xml) => {
12
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(xml));
13
+ let fingerprint = "";
14
+ for (const byte of new Uint8Array(digest)) fingerprint += byte.toString(16).padStart(2, "0");
15
+ return fingerprint;
16
+ };
17
+ /** Authorize exact canonical section removals and references missing from the committed snapshot. */
9
18
  const repackWithCanonicalStoryRemovals = async ({ document, repack }) => {
10
- if (!document.originalBuffer) return repack();
11
- const xml = await (await JSZip.loadAsync(document.originalBuffer)).file("word/document.xml")?.async("text");
19
+ const originalBuffer = document.originalBuffer;
20
+ if (!originalBuffer) return repack();
21
+ const xml = await (await JSZip.loadAsync(originalBuffer)).file("word/document.xml")?.async("text");
12
22
  if (!xml) return repack();
13
23
  const before = readDocumentSectionFacts(xml);
14
- const after = readDocumentSectionFacts(serializeDocument(document));
24
+ const serializedXml = serializeDocument(document);
25
+ const after = readDocumentSectionFacts(serializedXml);
15
26
  const remaining = /* @__PURE__ */ new Map();
16
27
  for (const reference of after.headerFooterReferences) {
17
28
  const key = referenceKey(reference);
@@ -34,7 +45,29 @@ const repackWithCanonicalStoryRemovals = async ({ document, repack }) => {
34
45
  return withSectionReferenceResolution({
35
46
  document,
36
47
  removedReferences,
37
- repack
48
+ repack: async () => {
49
+ if (after.sectionCount >= before.sectionCount) return repack();
50
+ const baseline = await parseDocx(originalBuffer, { preloadFonts: false });
51
+ const baselineXml = serializeDocument(baseline);
52
+ const baselineFacts = readDocumentSectionFacts(baselineXml);
53
+ if (baselineFacts.sectionCount !== before.sectionCount) return repack();
54
+ assertSectionCarriersMatchModel({
55
+ doc: baseline,
56
+ serializedSectionCount: baselineFacts.sectionCount
57
+ });
58
+ return withTrackedSectionEndpointRemoval({
59
+ document,
60
+ resolution: {
61
+ type: "tracked-section-endpoint-removal",
62
+ sourceParagraphEndpointCount: before.sectionCount,
63
+ expectedParagraphEndpointCount: after.sectionCount,
64
+ sourceEndpointFingerprint: await xmlFingerprint(baselineXml),
65
+ expectedEndpointFingerprint: await xmlFingerprint(serializedXml),
66
+ removedReferences
67
+ },
68
+ repack
69
+ });
70
+ }
38
71
  });
39
72
  };
40
73
  //#endregion
@@ -35,9 +35,11 @@ declare const CONTAINER_CHILDREN: {
35
35
  readonly "w:fldSimple": readonly ["bdo", "bookmarkEnd", "bookmarkStart", "commentRangeEnd", "commentRangeStart", "customXml", "customXmlDelRangeEnd", "customXmlDelRangeStart", "customXmlInsRangeEnd", "customXmlInsRangeStart", "customXmlMoveFromRangeEnd", "customXmlMoveFromRangeStart", "customXmlMoveToRangeEnd", "customXmlMoveToRangeStart", "del", "dir", "fldData", "fldSimple", "hyperlink", "ins", "moveFrom", "moveFromRangeEnd", "moveFromRangeStart", "moveTo", "moveToRangeEnd", "moveToRangeStart", "permEnd", "permStart", "proofErr", "r", "sdt", "smartTag", "subDoc"];
36
36
  readonly "block-content": readonly ["altChunk", "bookmarkEnd", "bookmarkStart", "commentRangeEnd", "commentRangeStart", "customXml", "customXmlDelRangeEnd", "customXmlDelRangeStart", "customXmlInsRangeEnd", "customXmlInsRangeStart", "customXmlMoveFromRangeEnd", "customXmlMoveFromRangeStart", "customXmlMoveToRangeEnd", "customXmlMoveToRangeStart", "del", "ins", "moveFrom", "moveFromRangeEnd", "moveFromRangeStart", "moveTo", "moveToRangeEnd", "moveToRangeStart", "p", "permEnd", "permStart", "proofErr", "sdt", "sectPr", "tbl", "tcPr"];
37
37
  };
38
+ /** WordprocessingML children permitted directly inside CT_RunTrackChange. */
39
+ declare const TRACKED_RUN_WORDPROCESSING_CHILDREN: readonly ["bdo", "bookmarkEnd", "bookmarkStart", "commentRangeEnd", "commentRangeStart", "customXml", "customXmlDelRangeEnd", "customXmlDelRangeStart", "customXmlInsRangeEnd", "customXmlInsRangeStart", "customXmlMoveFromRangeEnd", "customXmlMoveFromRangeStart", "customXmlMoveToRangeEnd", "customXmlMoveToRangeStart", "del", "dir", "ins", "moveFrom", "moveFromRangeEnd", "moveFromRangeStart", "moveTo", "moveToRangeEnd", "moveToRangeStart", "permEnd", "permStart", "proofErr", "r", "sdt", "smartTag"];
38
40
  /** A container the shared child dispatcher covers. */
39
41
  type DispatchedContainer = keyof typeof CONTAINER_CHILDREN;
40
42
  /** Every child name the schema declares for `Container`. */
41
43
  type DeclaredChild<Container extends DispatchedContainer> = (typeof CONTAINER_CHILDREN)[Container][number];
42
44
  //#endregion
43
- export { CONTAINER_CHILDREN, DeclaredChild, DispatchedContainer };
45
+ export { CONTAINER_CHILDREN, DeclaredChild, DispatchedContainer, TRACKED_RUN_WORDPROCESSING_CHILDREN };
@@ -260,5 +260,37 @@ const CONTAINER_CHILDREN = {
260
260
  ],
261
261
  ...SEQUENCE_CHILDREN
262
262
  };
263
+ /** WordprocessingML children permitted directly inside CT_RunTrackChange. */
264
+ const TRACKED_RUN_WORDPROCESSING_CHILDREN = [
265
+ "bdo",
266
+ "bookmarkEnd",
267
+ "bookmarkStart",
268
+ "commentRangeEnd",
269
+ "commentRangeStart",
270
+ "customXml",
271
+ "customXmlDelRangeEnd",
272
+ "customXmlDelRangeStart",
273
+ "customXmlInsRangeEnd",
274
+ "customXmlInsRangeStart",
275
+ "customXmlMoveFromRangeEnd",
276
+ "customXmlMoveFromRangeStart",
277
+ "customXmlMoveToRangeEnd",
278
+ "customXmlMoveToRangeStart",
279
+ "del",
280
+ "dir",
281
+ "ins",
282
+ "moveFrom",
283
+ "moveFromRangeEnd",
284
+ "moveFromRangeStart",
285
+ "moveTo",
286
+ "moveToRangeEnd",
287
+ "moveToRangeStart",
288
+ "permEnd",
289
+ "permStart",
290
+ "proofErr",
291
+ "r",
292
+ "sdt",
293
+ "smartTag"
294
+ ];
263
295
  //#endregion
264
- export { CONTAINER_CHILDREN };
296
+ export { CONTAINER_CHILDREN, TRACKED_RUN_WORDPROCESSING_CHILDREN };
@@ -54,22 +54,10 @@ declare function getAllParagraphs(body: document_d_exports.DocumentBody): docume
54
54
  * Get all tables from document body
55
55
  */
56
56
  declare function getAllTables(body: document_d_exports.DocumentBody): document_d_exports.Table[];
57
- /**
58
- * Get plain text from entire document body
59
- */
60
- declare function getDocumentText(body: document_d_exports.DocumentBody): string;
61
57
  /**
62
58
  * Count total paragraphs in document
63
59
  */
64
60
  declare function getParagraphCount(body: document_d_exports.DocumentBody): number;
65
- /**
66
- * Count total words in document (approximate)
67
- */
68
- declare function getWordCount(body: document_d_exports.DocumentBody): number;
69
- /**
70
- * Count total characters in document
71
- */
72
- declare function getCharacterCount(body: document_d_exports.DocumentBody): number;
73
61
  /**
74
62
  * Get section count
75
63
  */
@@ -79,4 +67,4 @@ declare function getSectionCount(body: document_d_exports.DocumentBody): number;
79
67
  */
80
68
  declare function hasTemplateVariables(body: document_d_exports.DocumentBody): boolean;
81
69
  //#endregion
82
- export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getCharacterCount, getDocumentText, getParagraphCount, getSectionCount, getWordCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
70
+ export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };