@stll/folio-core 0.33.0 → 0.33.2

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 (98) hide show
  1. package/dist/ai-edits/apply.d.ts +15 -0
  2. package/dist/ai-edits/apply.js +135 -24
  3. package/dist/ai-edits/headless.d.ts +47 -3
  4. package/dist/ai-edits/headless.js +53 -3
  5. package/dist/ai-edits/index.d.ts +1 -1
  6. package/dist/ai-edits/snapshot.d.ts +19 -1
  7. package/dist/ai-edits/snapshot.js +25 -3
  8. package/dist/ai-edits/table-geometry.d.ts +56 -0
  9. package/dist/ai-edits/table-geometry.js +217 -0
  10. package/dist/ai-edits/table-row-column-mutations.d.ts +34 -16
  11. package/dist/ai-edits/table-row-column-mutations.js +12 -4
  12. package/dist/ai-edits/table-template.d.ts +60 -0
  13. package/dist/ai-edits/table-template.js +176 -0
  14. package/dist/ai-edits/types.d.ts +7 -0
  15. package/dist/compare/__fixtures__/body-sequence.d.ts +86 -4
  16. package/dist/compare/__fixtures__/body-sequence.js +169 -12
  17. package/dist/compare/compare.d.ts +15 -8
  18. package/dist/compare/compare.js +138 -34
  19. package/dist/compare/plan.d.ts +31 -1
  20. package/dist/compare/plan.js +277 -28
  21. package/dist/compare/reproducible-package.d.ts +7 -3
  22. package/dist/compare/reproducible-package.js +20 -3
  23. package/dist/compare/types.d.ts +20 -3
  24. package/dist/compare/types.js +13 -1
  25. package/dist/compare/verification.d.ts +49 -2
  26. package/dist/compare/verification.js +115 -1
  27. package/dist/compat/eigenpal.d.ts +5 -5
  28. package/dist/compat/eigenpal.js +3 -3
  29. package/dist/document-operations.d.ts +10 -1
  30. package/dist/document-operations.js +3 -2
  31. package/dist/docx/appVersionNormalization.d.ts +49 -0
  32. package/dist/docx/appVersionNormalization.js +74 -0
  33. package/dist/docx/blockContentParser.js +3 -2
  34. package/dist/docx/drawingUtils.js +4 -3
  35. package/dist/docx/hyperlinkParser.d.ts +9 -1
  36. package/dist/docx/hyperlinkParser.js +19 -13
  37. package/dist/docx/paraIdRangeNormalization.d.ts +40 -0
  38. package/dist/docx/paraIdRangeNormalization.js +64 -0
  39. package/dist/docx/paragraphParser.js +82 -6
  40. package/dist/docx/revisionIdNormalization.d.ts +15 -1
  41. package/dist/docx/revisionIdNormalization.js +22 -4
  42. package/dist/docx/rezip.d.ts +13 -1
  43. package/dist/docx/rezip.js +80 -21
  44. package/dist/docx/runParser.js +9 -8
  45. package/dist/docx/sdtProperties.js +4 -3
  46. package/dist/docx/selectiveSave.js +7 -6
  47. package/dist/docx/serializer/commentSerializer.d.ts +1 -1
  48. package/dist/docx/serializer/commentSerializer.js +43 -19
  49. package/dist/docx/serializer/documentSerializer.d.ts +3 -1
  50. package/dist/docx/serializer/documentSerializer.js +42 -78
  51. package/dist/docx/serializer/fontTableSerializer.js +8 -5
  52. package/dist/docx/serializer/headerFooterSerializer.d.ts +4 -1
  53. package/dist/docx/serializer/headerFooterSerializer.js +34 -29
  54. package/dist/docx/serializer/noteSerializer.js +34 -30
  55. package/dist/docx/serializer/numberingSerializer.js +8 -4
  56. package/dist/docx/serializer/paragraphSerializer.js +45 -21
  57. package/dist/docx/serializer/partNamespaces.d.ts +78 -0
  58. package/dist/docx/serializer/partNamespaces.js +324 -0
  59. package/dist/docx/serializer/runSerializer.js +1 -1
  60. package/dist/docx/serializer/settingsSerializer.js +8 -2
  61. package/dist/docx/serializer/stylesSerializer.js +8 -5
  62. package/dist/docx/serializer/tableSerializer.js +107 -27
  63. package/dist/docx/serializer/themeSerializer.js +10 -2
  64. package/dist/docx/server/build.d.ts +1 -1
  65. package/dist/docx/strictValueEncodings.gen.d.ts +15 -0
  66. package/dist/docx/strictValueEncodings.gen.js +275 -0
  67. package/dist/docx/tableParser.d.ts +0 -6
  68. package/dist/docx/tableParser.js +52 -7
  69. package/dist/docx/transitionalSpelling.d.ts +23 -0
  70. package/dist/docx/transitionalSpelling.js +36 -0
  71. package/dist/docx/universalMeasure.d.ts +20 -0
  72. package/dist/docx/universalMeasure.js +32 -0
  73. package/dist/docx/verbatimCapture.d.ts +20 -0
  74. package/dist/docx/verbatimCapture.js +131 -0
  75. package/dist/docx/vmlImageParser.js +4 -3
  76. package/dist/docx/watermarkParser.js +4 -3
  77. package/dist/docx/xmlParser.d.ts +23 -2
  78. package/dist/docx/xmlParser.js +43 -11
  79. package/dist/index.d.ts +5 -5
  80. package/dist/index.js +3 -3
  81. package/dist/model.d.ts +3 -3
  82. package/dist/model.js +2 -2
  83. package/dist/prosemirror/commands/comments.js +8 -2
  84. package/dist/prosemirror/containerFinalParagraph.d.ts +47 -0
  85. package/dist/prosemirror/containerFinalParagraph.js +92 -0
  86. package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -1
  87. package/dist/prosemirror/conversion/fromProseDoc.js +15 -4
  88. package/dist/prosemirror/conversion/toProseDoc.js +12 -3
  89. package/dist/prosemirror/extensions/nodes/TableExtension.js +5 -0
  90. package/dist/prosemirror/schema/marks.d.ts +1 -1
  91. package/dist/prosemirror/schema/nodes.d.ts +22 -0
  92. package/dist/server.d.ts +1 -1
  93. package/dist/types/block-id.d.ts +12 -1
  94. package/dist/types/block-id.js +17 -1
  95. package/dist/utils/canonicalJson.d.ts +14 -0
  96. package/dist/utils/canonicalJson.js +19 -0
  97. package/dist/utils/formatToStyle.d.ts +1 -1
  98. package/package.json +2 -2
@@ -1,4 +1,19 @@
1
+ import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
1
2
  //#region src/compare/verification.ts
3
+ /**
4
+ * What the round-trip self-check found, and how to say it safely.
5
+ *
6
+ * The check compares two block projections: what accepting the generated
7
+ * revisions leaves against the target, and what rejecting them leaves against
8
+ * the base. A projection carries each block's container, style, list level and
9
+ * text, so WHICH field diverged names which part of the pipeline lost the
10
+ * difference — and that is worth reporting as a typed cause rather than as one
11
+ * opaque "did not reproduce".
12
+ *
13
+ * Every `detail` string here is structural: counts, offsets, container kinds.
14
+ * Never a phrase of either document, because a caller may log it, put it in a
15
+ * report, or quote it in a review.
16
+ */
2
17
  /** The two directions of the round trip, each an invariant of its own. */
3
18
  const COMPARE_VERIFICATION_INVARIANTS = Object.freeze(["accept-reproduces-target", "reject-reproduces-base"]);
4
19
  /**
@@ -10,6 +25,7 @@ const COMPARE_VERIFICATION_CAUSES = Object.freeze([
10
25
  "invisible-structure",
11
26
  "block-count",
12
27
  "container",
28
+ "table-geometry",
13
29
  "style",
14
30
  "list-level",
15
31
  "inline-formatting",
@@ -116,6 +132,21 @@ const firstDivergence = (actual, expected) => {
116
132
  };
117
133
  };
118
134
  /**
135
+ * The failure two table-geometry projections describe, or `null` when they
136
+ * agree. One line per table, so the detail names which table diverged and
137
+ * whether the count itself did — never a property value, which could carry a
138
+ * style name either document chose.
139
+ */
140
+ const classifyGeometryMismatch = ({ invariant, story, actual, expected }) => {
141
+ if (sameProjection(actual, expected)) return null;
142
+ return {
143
+ invariant,
144
+ cause: "table-geometry",
145
+ story,
146
+ detail: actual.length === expected.length ? `table ${String(actual.findIndex((entry, index) => entry !== expected[index]))} of ${String(actual.length)} carries different properties` : `${String(actual.length)} tables against ${String(expected.length)}`
147
+ };
148
+ };
149
+ /**
119
150
  * The failure two projections describe, or `null` when they agree.
120
151
  *
121
152
  * Total over the causes by construction: the last branch is unconditional, so
@@ -142,5 +173,88 @@ const classifyProjectionMismatch = ({ invariant, story, actual, expected }) => {
142
173
  if (actual.length !== expected.length) return failure("block-count", `${counts}, first differing ${at}`);
143
174
  return failure("text", `a ${containerKind(left.container)} block's text does not match ${at}, length ${String(left.text.length)} against ${String(right.text.length)} (${counts})`);
144
175
  };
176
+ const isAParagraphMarkChangeKind = (value) => PARAGRAPH_MARK_CHANGE_KINDS.some((kind) => kind === value);
177
+ const isRecord = (value) => typeof value === "object" && value !== null;
178
+ const isParagraph = (value) => isRecord(value) && value["type"] === "paragraph";
179
+ /**
180
+ * A row the package marks deleted, whose cells' marks go with it.
181
+ *
182
+ * A deleted table row is written as `w:trPr/w:del` PLUS a deletion on every
183
+ * mark the row's cells end with: accepting takes the whole row away, so those
184
+ * marks never have to join anything and are the shape the format asks for.
185
+ */
186
+ const isADeletedTableRow = (value) => {
187
+ const change = value["structuralChange"];
188
+ return isRecord(change) && change["type"] === "tableRowDeletion";
189
+ };
190
+ /**
191
+ * Every container in a package whose final paragraph mark carries a revision.
192
+ *
193
+ * A deleted paragraph mark says "join this paragraph with the one after it",
194
+ * and an inserted one says that break was ADDED, so rejecting it closes the
195
+ * paragraph back over the next one. The last paragraph of a body, a table
196
+ * cell, a header or footer, a note or a text box has no paragraph after it, so
197
+ * neither direction states an edit that can be carried out: a consumer refuses
198
+ * the package, or opens it and leaves a revision standing that neither
199
+ * accepting nor rejecting everything can clear. The exception is a cell of a
200
+ * row the package is DELETING: there the mark leaves with its row.
201
+ *
202
+ * The walk is over the package model rather than over a list of the containers
203
+ * known today: a container is any sequence that ends in a paragraph, so a part
204
+ * the model grows later is covered the day it arrives instead of the day
205
+ * someone remembers this function.
206
+ *
207
+ * `since` scopes it to the revisions a comparison MINTED: a base may arrive
208
+ * carrying one of these on a paragraph in a part no story mounts, which folio
209
+ * preserves the way it preserves everything else it parses. What this proves
210
+ * is that the comparison writes none of its own.
211
+ */
212
+ const revisedFinalParagraphMarks = (packageModel, { since = 0 } = {}) => {
213
+ const found = [];
214
+ const trail = ["package"];
215
+ const pathOf = () => {
216
+ let path = "";
217
+ for (const segment of trail) path += typeof segment === "number" ? `[${String(segment)}]` : `.${segment}`;
218
+ return path.slice(1);
219
+ };
220
+ const visit = (value, insideADeletedRow) => {
221
+ if (Array.isArray(value)) {
222
+ const last = value.at(-1);
223
+ const mark = isParagraph(last) ? last["pPrMark"] : void 0;
224
+ const kind = isRecord(mark) ? mark["kind"] : void 0;
225
+ const info = isRecord(mark) ? mark["info"] : void 0;
226
+ const revisionId = isRecord(info) ? info["id"] : void 0;
227
+ const isOurs = typeof revisionId === "number" ? revisionId >= since : true;
228
+ if (isAParagraphMarkChangeKind(kind) && isOurs && !insideADeletedRow) found.push({
229
+ container: pathOf(),
230
+ paragraphIndex: value.length - 1,
231
+ kind
232
+ });
233
+ for (const [index, item] of value.entries()) {
234
+ trail.push(index);
235
+ visit(item, insideADeletedRow);
236
+ trail.pop();
237
+ }
238
+ return;
239
+ }
240
+ if (value instanceof Map) {
241
+ for (const [key, item] of value) {
242
+ trail.push(String(key));
243
+ visit(item, insideADeletedRow);
244
+ trail.pop();
245
+ }
246
+ return;
247
+ }
248
+ if (!isRecord(value) || value instanceof Date || ArrayBuffer.isView(value)) return;
249
+ const inADeletedRow = insideADeletedRow || isADeletedTableRow(value);
250
+ for (const [key, item] of Object.entries(value)) {
251
+ trail.push(key);
252
+ visit(item, inADeletedRow);
253
+ trail.pop();
254
+ }
255
+ };
256
+ visit(packageModel, false);
257
+ return found;
258
+ };
145
259
  //#endregion
146
- export { COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, classifyProjectionMismatch, projectSupportedInlineFormatting, sameProjection };
260
+ export { COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, classifyGeometryMismatch, classifyProjectionMismatch, projectSupportedInlineFormatting, revisedFinalParagraphMarks, sameProjection };
@@ -1,16 +1,16 @@
1
+ import { document_d_exports } from "../types/document.js";
1
2
  import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAISignatureParty } from "../ai-edits/types.js";
2
3
  import { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, diffWordSegments } from "../ai-edits/word-diff.js";
3
4
  import { FolioAIEditApplyOutcome, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations } from "../ai-edits/apply.js";
4
- import { document_d_exports } from "../types/document.js";
5
5
  import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
6
6
  import { createFolioAIEditSnapshot, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "../ai-edits/snapshot.js";
7
- import { DeriveBlockIdInput, FolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "../types/block-id.js";
7
+ import { DeriveBlockIdInput, FolioBlockId, currentFolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "../types/block-id.js";
8
8
  import { AIBarStatus, AIChatMode, AICitation, AICitationSource, AIGenerateInput, AISuggestion, AISuggestionApplyMode, AISuggestionPreset, AISuggestionSeverity, AISuggestionStatus, DEFAULT_AI_SUGGESTION_PRESETS } from "../ai-suggestions/types.js";
9
9
  import { ApplyResult, applySuggestions } from "../ai-suggestions/apply.js";
10
10
  import { ResolvedAnchor, isSuggestionStale, resolveSuggestionAnchor } from "../ai-suggestions/conflict.js";
11
11
  import { PositionalText, buildPositionalText } from "../ai-suggestions/text-positions.js";
12
- import { COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareVerification, CompareVerificationCause, CompareVerificationFailure, CompareVerificationInvariant } from "../compare/verification.js";
13
- import { COMPARE_UNSUPPORTED_REASONS, CompareChange, CompareChangeLocation, CompareDocxApplyError, CompareDocxError, CompareDocxOperationLimitError, CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, CompareFormatRange, CompareResult, CompareUnsupportedPart, CompareUnsupportedReason, InvalidCompareDocxOptionsError } from "../compare/types.js";
12
+ import { COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareVerification, CompareVerificationCause, CompareVerificationFailure, CompareVerificationInvariant, FinalParagraphMarkRevision } from "../compare/verification.js";
13
+ import { COMPARE_UNSUPPORTED_REASONS, CompareChange, CompareChangeLocation, CompareDocxApplyError, CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, CompareFormatRange, CompareResult, CompareUnsupportedPart, CompareUnsupportedReason, InvalidCompareDocxOptionsError } from "../compare/types.js";
14
14
  import { MAX_COMPARE_OPERATIONS, compareDocx } from "../compare/compare.js";
15
15
  import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocumentPreset, DocumentStyleSet } from "../style-sets/types.js";
16
16
  import { CreateEmptyDocumentOptions, createEmptyDocument } from "../utils/createDocument.js";
@@ -37,4 +37,4 @@ import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled
37
37
  import { DOCX_CONFORMANCE_CLASSES } from "../index.js";
38
38
  type Document = document_d_exports.Document;
39
39
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
40
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, CompareDocxApplyError, type CompareDocxError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFormatRange, type CompareResult, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
40
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFormatRange, type CompareResult, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -6,7 +6,7 @@ import { isSuggestionStale, resolveSuggestionAnchor } from "../ai-suggestions/co
6
6
  import { buildPositionalText } from "../ai-suggestions/text-positions.js";
7
7
  import { DEFAULT_AI_SUGGESTION_PRESETS } from "../ai-suggestions/types.js";
8
8
  import { MAX_COMPARE_OPERATIONS, compareDocx } from "../compare/compare.js";
9
- import { COMPARE_UNSUPPORTED_REASONS, CompareDocxApplyError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, InvalidCompareDocxOptionsError } from "../compare/types.js";
9
+ import { COMPARE_UNSUPPORTED_REASONS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, InvalidCompareDocxOptionsError } from "../compare/types.js";
10
10
  import { COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS } from "../compare/verification.js";
11
11
  import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
12
12
  import { inspectDocxCompatibility } from "../docx/compatibility.js";
@@ -27,8 +27,8 @@ import { clearTemplateSlashMenu, consumeTemplateSlashQuery, getTemplateSlashMenu
27
27
  import { extractDocumentStyleSet, extractDocumentStyleSetFromDocx, inspectDocumentStyles, inspectDocumentStylesFromDocx } from "../style-sets/extract.js";
28
28
  import { STELLA_STYLE_SET_NAME, createStellaStyleDocumentPreset, createStellaStyleSet } from "../style-sets/stellaStyle.js";
29
29
  import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION } from "../style-sets/types.js";
30
- import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "../types/block-id.js";
30
+ import { currentFolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "../types/block-id.js";
31
31
  import { createEmptyDocument } from "../utils/createDocument.js";
32
32
  import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "../utils/fontResolver.js";
33
33
  import { mergeDocumentContent } from "../utils/mergeDocumentContent.js";
34
- export { COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
34
+ export { COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -1,3 +1,4 @@
1
+ import { FolioTableTemplates } from "./ai-edits/table-template.js";
1
2
  import { FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditNormalization, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAITextRangeHandle } from "./ai-edits/types.js";
2
3
  import { FolioAIEditView, FolioRevisionStamp, FolioWordDiffOptions } from "./ai-edits/apply.js";
3
4
  //#region src/document-operations.d.ts
@@ -266,7 +267,15 @@ type ApplyFolioDocumentOperationsOptions = {
266
267
  revisionStamp?: FolioRevisionStamp;
267
268
  /** Token size a replacement's redline is cut at. Word-level by default. */
268
269
  wordDiff?: FolioWordDiffOptions;
270
+ /**
271
+ * Tables and rows an `insertTable` / `insertTableRow` in the batch places
272
+ * verbatim, by operation id. Outside the serialized batch because a table
273
+ * node is not JSON: the contract still describes a table by its cell texts,
274
+ * and an in-process caller that already holds the table — a comparison
275
+ * copying the target document's — hands the whole thing over instead.
276
+ */
277
+ tableTemplates?: FolioTableTemplates;
269
278
  };
270
- declare const applyFolioDocumentOperations: ({ view, snapshot, batch, story, author, createCommentId, createUndoHandle, revisionStamp, wordDiff }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
279
+ declare const applyFolioDocumentOperations: ({ view, snapshot, batch, story, author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
271
280
  //#endregion
272
281
  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, 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 };
@@ -879,7 +879,7 @@ const getFolioDocumentOperationReceiptsForStory = ({ operations, applied, story
879
879
  });
880
880
  return receipts;
881
881
  };
882
- const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", author, createCommentId, createUndoHandle, revisionStamp, wordDiff }) => {
882
+ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates }) => {
883
883
  const parsedBatch = parseFolioDocumentOperationBatch(batch);
884
884
  const apply = ({ targetView, targetCreateCommentId = createCommentId, preview = false }) => {
885
885
  return (preview ? previewFolioAIEditOperations : applyFolioAIEditOperations)({
@@ -890,7 +890,8 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
890
890
  ...author !== void 0 && { author },
891
891
  ...targetCreateCommentId !== void 0 && { createCommentId: targetCreateCommentId },
892
892
  ...revisionStamp !== void 0 && { revisionStamp },
893
- ...wordDiff !== void 0 && { wordDiff }
893
+ ...wordDiff !== void 0 && { wordDiff },
894
+ ...tableTemplates !== void 0 && { tableTemplates }
894
895
  });
895
896
  };
896
897
  const preview = () => apply({
@@ -0,0 +1,49 @@
1
+ //#region src/docx/appVersionNormalization.d.ts
2
+ /**
3
+ * Keep the application version a package states about itself in the form the
4
+ * schema gives it.
5
+ *
6
+ * `AppVersion` in the extended-properties part is `XX.YYYY`: a one- or
7
+ * two-digit integer, a dot, and four digits. Producers exist that write a
8
+ * three-part version there instead, and folio copies `docProps/app.xml`
9
+ * through verbatim when it saves a document it did not create — so a package
10
+ * can carry a value with two dots in, and a save that copies it out hands a
11
+ * consumer a package it refuses to open at all.
12
+ *
13
+ * {@link appVersionInSchemaForm} is the one mapping, and it is a pure function
14
+ * of the value alone: it keeps the leading integer where the value opens with
15
+ * one the form allows, and writes the build digits the form requires. Nothing
16
+ * else in the part is touched, and a package that carries no extended
17
+ * properties keeps carrying none — synthesizing metadata a document never
18
+ * stated is a different decision.
19
+ */
20
+ declare const AppVersionSchemaError_base: import("better-result").TaggedErrorClass<"AppVersionSchemaError">;
21
+ /**
22
+ * A value reached the package that the schema form does not accept.
23
+ *
24
+ * The normalization hands every value it writes to one choke point, which
25
+ * throws this rather than letting the package go out carrying a version a
26
+ * consumer refuses.
27
+ */
28
+ declare class AppVersionSchemaError extends AppVersionSchemaError_base<{
29
+ message: string;
30
+ appVersion: string;
31
+ }> {}
32
+ /**
33
+ * `value` when it already fits, and a value derived from it when it does not.
34
+ *
35
+ * The derivation reads the value and nothing else — not the package, not the
36
+ * clock, not folio's own version — so two saves of one document state the same
37
+ * application version.
38
+ */
39
+ declare const appVersionInSchemaForm: (value: string) => string;
40
+ /**
41
+ * Rewrite the application version an extended-properties part states.
42
+ *
43
+ * Returns the part unchanged when the value already fits and when the part
44
+ * states no version at all, so a save of a document that never carried a
45
+ * malformed one is byte-identical.
46
+ */
47
+ declare const normalizeAppVersionInExtendedProperties: (xml: string) => string;
48
+ //#endregion
49
+ export { AppVersionSchemaError, appVersionInSchemaForm, normalizeAppVersionInExtendedProperties };
@@ -0,0 +1,74 @@
1
+ import { TaggedError } from "better-result";
2
+ //#region src/docx/appVersionNormalization.ts
3
+ /**
4
+ * Keep the application version a package states about itself in the form the
5
+ * schema gives it.
6
+ *
7
+ * `AppVersion` in the extended-properties part is `XX.YYYY`: a one- or
8
+ * two-digit integer, a dot, and four digits. Producers exist that write a
9
+ * three-part version there instead, and folio copies `docProps/app.xml`
10
+ * through verbatim when it saves a document it did not create — so a package
11
+ * can carry a value with two dots in, and a save that copies it out hands a
12
+ * consumer a package it refuses to open at all.
13
+ *
14
+ * {@link appVersionInSchemaForm} is the one mapping, and it is a pure function
15
+ * of the value alone: it keeps the leading integer where the value opens with
16
+ * one the form allows, and writes the build digits the form requires. Nothing
17
+ * else in the part is touched, and a package that carries no extended
18
+ * properties keeps carrying none — synthesizing metadata a document never
19
+ * stated is a different decision.
20
+ */
21
+ /** `XX.YYYY`: the only form the extended-properties `AppVersion` may take. */
22
+ const SCHEMA_FORM = /^\d{1,2}\.\d{4}$/u;
23
+ /** The leading integer, when the value opens with one the form allows. */
24
+ const LEADING_MAJOR = /^\d{1,2}(?!\d)/u;
25
+ /** Major version for a value that does not open with one. */
26
+ const DEFAULT_MAJOR = "1";
27
+ /**
28
+ * The build digits a rewritten value gets. A value the form rejects states no
29
+ * build this pass could carry over, so every rewrite lands on the same one.
30
+ */
31
+ const CANONICAL_BUILD = "0000";
32
+ /**
33
+ * A value reached the package that the schema form does not accept.
34
+ *
35
+ * The normalization hands every value it writes to one choke point, which
36
+ * throws this rather than letting the package go out carrying a version a
37
+ * consumer refuses.
38
+ */
39
+ var AppVersionSchemaError = class extends TaggedError("AppVersionSchemaError") {};
40
+ /**
41
+ * `value` when it already fits, and a value derived from it when it does not.
42
+ *
43
+ * The derivation reads the value and nothing else — not the package, not the
44
+ * clock, not folio's own version — so two saves of one document state the same
45
+ * application version.
46
+ */
47
+ const appVersionInSchemaForm = (value) => {
48
+ if (SCHEMA_FORM.test(value)) return value;
49
+ const normalized = `${LEADING_MAJOR.exec(value.trim())?.[0] ?? DEFAULT_MAJOR}.${CANONICAL_BUILD}`;
50
+ if (!SCHEMA_FORM.test(normalized)) throw new AppVersionSchemaError({
51
+ message: `Derived application version ${normalized} is not of the form XX.YYYY`,
52
+ appVersion: value
53
+ });
54
+ return normalized;
55
+ };
56
+ /**
57
+ * The element and its content, with the prefix and attributes it was written
58
+ * with preserved: the replacement rewrites the value between the tags and
59
+ * leaves the rest of the part byte-identical.
60
+ */
61
+ const APP_VERSION_ELEMENT = /(<(?:[^\s<>/:]+:)?AppVersion(?:\s[^<>]*)?>)([^<]*)(<\/(?:[^\s<>/:]+:)?AppVersion>)/u;
62
+ /**
63
+ * Rewrite the application version an extended-properties part states.
64
+ *
65
+ * Returns the part unchanged when the value already fits and when the part
66
+ * states no version at all, so a save of a document that never carried a
67
+ * malformed one is byte-identical.
68
+ */
69
+ const normalizeAppVersionInExtendedProperties = (xml) => xml.replace(APP_VERSION_ELEMENT, (whole, open, value, close) => {
70
+ const replacement = appVersionInSchemaForm(value);
71
+ return replacement === value ? whole : `${open}${replacement}${close}`;
72
+ });
73
+ //#endregion
74
+ export { AppVersionSchemaError, appVersionInSchemaForm, normalizeAppVersionInExtendedProperties };
@@ -6,7 +6,8 @@ import { parseParagraph } from "./paragraphParser.js";
6
6
  import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
7
7
  import { parseSdtProperties } from "./sdtProperties.js";
8
8
  import { parseTable } from "./tableParser.js";
9
- import { elementToXml, findChild, getChildElements, getLocalName, mergeXmlnsDeclarations } from "./xmlParser.js";
9
+ import { captureVerbatimXml } from "./verbatimCapture.js";
10
+ import { findChild, getChildElements, getLocalName, mergeXmlnsDeclarations } from "./xmlParser.js";
10
11
  //#region src/docx/blockContentParser.ts
11
12
  const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList, siblingNumIdsByAbstractNumId }) => {
12
13
  const listRendering = paragraph.listRendering;
@@ -192,7 +193,7 @@ const captureSdtSiblingMarkers = (sdt) => {
192
193
  sawContent = true;
193
194
  continue;
194
195
  }
195
- const xml = elementToXml(ch);
196
+ const xml = captureVerbatimXml(ch);
196
197
  if (sawContent) afterParts.push(xml);
197
198
  else beforeParts.push(xml);
198
199
  }
@@ -1,5 +1,6 @@
1
1
  import { ImageHorizontalAlignmentSchema, ImageHorizontalRelativeToSchema, ImageVerticalAlignmentSchema, ImageVerticalRelativeToSchema, ImageWrapTextSchema, ShapeOutlineStyleSchema, narrowEnum } from "./parserEnums.js";
2
- import { elementToXml, findByFullName, findChildByLocalName, findChildrenByLocalName, getAttribute, getChildElements, getTextContent, parseNumericAttribute } from "./xmlParser.js";
2
+ import { captureVerbatimXml } from "./verbatimCapture.js";
3
+ import { findByFullName, findChildByLocalName, findChildrenByLocalName, getAttribute, getChildElements, getTextContent, parseNumericAttribute } from "./xmlParser.js";
3
4
  //#region src/docx/drawingUtils.ts
4
5
  /**
5
6
  * Map OOXML scheme names to standard theme color slots.
@@ -162,7 +163,7 @@ function parseGradientFill(gradientFill) {
162
163
  }
163
164
  return {
164
165
  type: "gradient",
165
- rawXml: elementToXml(gradientFill),
166
+ rawXml: captureVerbatimXml(gradientFill),
166
167
  gradient: {
167
168
  type,
168
169
  ...angle !== void 0 ? { angle } : {},
@@ -177,7 +178,7 @@ function parseOutline(spPr) {
177
178
  const ln = spPr ? findChildByLocalName(spPr, "ln") : null;
178
179
  if (!ln) return;
179
180
  if (findChildByLocalName(ln, "noFill")) return;
180
- const outline = { rawXml: elementToXml(ln) };
181
+ const outline = { rawXml: captureVerbatimXml(ln) };
181
182
  const w = getAttribute(ln, null, "w");
182
183
  if (w) {
183
184
  const parsed = Number.parseInt(w, 10);
@@ -16,6 +16,14 @@ import { XmlElement } from "./xmlParser.js";
16
16
  * @returns Parsed Hyperlink object
17
17
  */
18
18
  declare function parseHyperlink(node: XmlElement, rels: document_d_exports.RelationshipMap | null, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, media?: Map<string, document_d_exports.MediaFile> | null, rootXmlns?: Record<string, string>): document_d_exports.Hyperlink;
19
+ /**
20
+ * One `w:hyperlink` child, or `null` for markup the model does not carry.
21
+ *
22
+ * Exposed so a caller that has to segment a hyperlink — one holding revision
23
+ * wrappers, which the model nests the other way round — parses its plain
24
+ * children exactly as {@link parseHyperlink} does.
25
+ */
26
+ declare function parseHyperlinkChild(node: XmlElement, styles: StyleMap | null, theme: document_d_exports.Theme | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, inScopeXmlns: Record<string, string>): document_d_exports.Hyperlink["children"][number] | null;
19
27
  /**
20
28
  * Get the display text of a hyperlink
21
29
  *
@@ -104,4 +112,4 @@ declare function createExternalHyperlink(url: string, children: document_d_expor
104
112
  target?: string;
105
113
  }): document_d_exports.Hyperlink;
106
114
  //#endregion
107
- export { createExternalHyperlink, createInternalHyperlink, getHyperlinkRuns, getHyperlinkText, getHyperlinkUrl, hasContent, isExternalLink, isInternalLink, parseHyperlink, resolveHyperlinkUrl };
115
+ export { createExternalHyperlink, createInternalHyperlink, getHyperlinkRuns, getHyperlinkText, getHyperlinkUrl, hasContent, isExternalLink, isInternalLink, parseHyperlink, parseHyperlinkChild, resolveHyperlinkUrl };
@@ -71,22 +71,28 @@ function parseHyperlink(node, rels, styles = null, theme = null, media = null, r
71
71
  const docLocation = getAttribute(node, "w", "docLocation");
72
72
  if (docLocation) hyperlink.docLocation = docLocation;
73
73
  const inScopeXmlns = mergeXmlnsDeclarations(rootXmlns, node);
74
- const children = getChildElements(node);
75
- for (const child of children) switch (getLocalName(child.name)) {
76
- case "r":
77
- hyperlink.children.push(parseRun(child, styles, theme, rels, media, inScopeXmlns));
78
- break;
79
- case "bookmarkStart":
80
- hyperlink.children.push(parseBookmarkStart(child));
81
- break;
82
- case "bookmarkEnd":
83
- hyperlink.children.push(parseBookmarkEnd(child));
84
- break;
85
- default: break;
74
+ for (const child of getChildElements(node)) {
75
+ const parsed = parseHyperlinkChild(child, styles, theme, rels, media, inScopeXmlns);
76
+ if (parsed) hyperlink.children.push(parsed);
86
77
  }
87
78
  return hyperlink;
88
79
  }
89
80
  /**
81
+ * One `w:hyperlink` child, or `null` for markup the model does not carry.
82
+ *
83
+ * Exposed so a caller that has to segment a hyperlink — one holding revision
84
+ * wrappers, which the model nests the other way round — parses its plain
85
+ * children exactly as {@link parseHyperlink} does.
86
+ */
87
+ function parseHyperlinkChild(node, styles, theme, rels, media, inScopeXmlns) {
88
+ switch (getLocalName(node.name)) {
89
+ case "r": return parseRun(node, styles, theme, rels, media, inScopeXmlns);
90
+ case "bookmarkStart": return parseBookmarkStart(node);
91
+ case "bookmarkEnd": return parseBookmarkEnd(node);
92
+ default: return null;
93
+ }
94
+ }
95
+ /**
90
96
  * Get the display text of a hyperlink
91
97
  *
92
98
  * Concatenates text from all child runs.
@@ -219,4 +225,4 @@ function createExternalHyperlink(url, children, options) {
219
225
  };
220
226
  }
221
227
  //#endregion
222
- export { createExternalHyperlink, createInternalHyperlink, getHyperlinkRuns, getHyperlinkText, getHyperlinkUrl, hasContent, isExternalLink, isInternalLink, parseHyperlink, resolveHyperlinkUrl };
228
+ export { createExternalHyperlink, createInternalHyperlink, getHyperlinkRuns, getHyperlinkText, getHyperlinkUrl, hasContent, isExternalLink, isInternalLink, parseHyperlink, parseHyperlinkChild, resolveHyperlinkUrl };
@@ -0,0 +1,40 @@
1
+ //#region src/docx/paraIdRangeNormalization.d.ts
2
+ /**
3
+ * Keep every paragraph id a package carries inside the range the schema gives
4
+ * it.
5
+ *
6
+ * `w14:paraId`, `w14:textId` and the comment-part ids that reference a
7
+ * paragraph are `ST_LongHexNumber` with a maximum: the value has to be below
8
+ * `0x80000000`, so the ids are 31-bit. Producers exist that write eight hex
9
+ * digits without that bound, and folio preserves the ids a document arrives
10
+ * with — so a package can carry an out-of-range id in, and a save that copies
11
+ * it through hands a consumer a package it will refuse.
12
+ *
13
+ * {@link paraIdInRange} is the one mapping, and it is a pure function of the
14
+ * value alone. That is what lets the parser and the save agree without
15
+ * consulting each other: a paragraph's id in the model is the id the file gets,
16
+ * so bringing an id into range does not make a document's own identity move
17
+ * under it between reading and writing. The package pass below is the same
18
+ * mapping applied to every attribute that carries such an id, so a paragraph
19
+ * and every reference to it move together.
20
+ */
21
+ /**
22
+ * `value` when it already fits, and a value derived from it when it does not.
23
+ *
24
+ * The replacement is derived from the id being replaced and from nothing else.
25
+ * A package-aware search for a free id would read better here and would be
26
+ * wrong: the parser has no package to search, and an id that means one
27
+ * paragraph while reading and another while writing is worse than the
28
+ * vanishing chance of two rewritten ids landing on one value, which is a
29
+ * duplicate rather than a package a consumer refuses.
30
+ */
31
+ declare const paraIdInRange: (value: string) => string;
32
+ /**
33
+ * Rewrite out-of-range paragraph ids across a whole package.
34
+ *
35
+ * Returns the parts unchanged when every id already fits, so a save of a
36
+ * document that never carried one is byte-identical.
37
+ */
38
+ declare const normalizeParaIdRangeInXmlParts: (parts: ReadonlyMap<string, string>) => Map<string, string>;
39
+ //#endregion
40
+ export { normalizeParaIdRangeInXmlParts, paraIdInRange };
@@ -0,0 +1,64 @@
1
+ import { deterministicHexId } from "../utils/hexId.js";
2
+ //#region src/docx/paraIdRangeNormalization.ts
3
+ /**
4
+ * Keep every paragraph id a package carries inside the range the schema gives
5
+ * it.
6
+ *
7
+ * `w14:paraId`, `w14:textId` and the comment-part ids that reference a
8
+ * paragraph are `ST_LongHexNumber` with a maximum: the value has to be below
9
+ * `0x80000000`, so the ids are 31-bit. Producers exist that write eight hex
10
+ * digits without that bound, and folio preserves the ids a document arrives
11
+ * with — so a package can carry an out-of-range id in, and a save that copies
12
+ * it through hands a consumer a package it will refuse.
13
+ *
14
+ * {@link paraIdInRange} is the one mapping, and it is a pure function of the
15
+ * value alone. That is what lets the parser and the save agree without
16
+ * consulting each other: a paragraph's id in the model is the id the file gets,
17
+ * so bringing an id into range does not make a document's own identity move
18
+ * under it between reading and writing. The package pass below is the same
19
+ * mapping applied to every attribute that carries such an id, so a paragraph
20
+ * and every reference to it move together.
21
+ */
22
+ /** Exclusive upper bound on a paragraph id: the values are 31-bit. */
23
+ const MAX_PARA_ID_EXCLUSIVE = 2147483648;
24
+ /**
25
+ * Every attribute that carries a paragraph id or the text-revision marker
26
+ * written beside one: the paragraph's own `w14:paraId` / `w14:textId` (the
27
+ * parser accepts a `w:` prefix too), the comment part's `w15:paraId` and the
28
+ * `w15:paraIdParent` that links a reply to its thread, and the durable-comment
29
+ * part's `w16cid:paraId`.
30
+ */
31
+ const PARA_ID_ATTRIBUTE = /\b(w|w14|w15|w16cid):(paraId|paraIdParent|textId)=(?<quote>["'])([0-9A-Fa-f]{8})\k<quote>/gu;
32
+ /** A candidate part is one that mentions any of those attributes at all. */
33
+ const PARA_ID_CANDIDATE = /\b(?:w|w14|w15|w16cid):(?:paraId|paraIdParent|textId)=/u;
34
+ /**
35
+ * `value` when it already fits, and a value derived from it when it does not.
36
+ *
37
+ * The replacement is derived from the id being replaced and from nothing else.
38
+ * A package-aware search for a free id would read better here and would be
39
+ * wrong: the parser has no package to search, and an id that means one
40
+ * paragraph while reading and another while writing is worse than the
41
+ * vanishing chance of two rewritten ids landing on one value, which is a
42
+ * duplicate rather than a package a consumer refuses.
43
+ */
44
+ const paraIdInRange = (value) => Number.parseInt(value, 16) < MAX_PARA_ID_EXCLUSIVE ? value : deterministicHexId(value);
45
+ /**
46
+ * Rewrite out-of-range paragraph ids across a whole package.
47
+ *
48
+ * Returns the parts unchanged when every id already fits, so a save of a
49
+ * document that never carried one is byte-identical.
50
+ */
51
+ const normalizeParaIdRangeInXmlParts = (parts) => {
52
+ const normalized = new Map(parts);
53
+ for (const [path, xml] of parts) {
54
+ if (!PARA_ID_CANDIDATE.test(xml)) continue;
55
+ const rewritten = xml.replaceAll(PARA_ID_ATTRIBUTE, (whole, prefix, name, quote, value) => {
56
+ const replacement = paraIdInRange(value);
57
+ return replacement === value ? whole : `${prefix}:${name}=${quote}${replacement}${quote}`;
58
+ });
59
+ if (rewritten !== xml) normalized.set(path, rewritten);
60
+ }
61
+ return normalized;
62
+ };
63
+ //#endregion
64
+ export { normalizeParaIdRangeInXmlParts, paraIdInRange };