@stll/folio-core 0.4.0 → 0.6.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 (98) hide show
  1. package/dist/ai-edits/apply.d.ts +3 -9
  2. package/dist/ai-edits/apply.js +94 -14
  3. package/dist/ai-edits/blockRange.d.ts +33 -0
  4. package/dist/ai-edits/blockRange.js +58 -0
  5. package/dist/ai-edits/headless.d.ts +37 -4
  6. package/dist/ai-edits/headless.js +134 -16
  7. package/dist/ai-edits/index.d.ts +5 -3
  8. package/dist/ai-edits/index.js +3 -2
  9. package/dist/ai-edits/snapshot.d.ts +14 -2
  10. package/dist/ai-edits/snapshot.js +12 -1
  11. package/dist/ai-edits/types.d.ts +42 -3
  12. package/dist/compat/eigenpal.d.ts +3 -2
  13. package/dist/compat/eigenpal.js +6 -5
  14. package/dist/controller/layoutPipeline.d.ts +1 -0
  15. package/dist/controller/layoutPipeline.js +35 -137
  16. package/dist/controller/layoutScheduler.d.ts +2 -1
  17. package/dist/controller/layoutScheduler.js +6 -0
  18. package/dist/document-operations.d.ts +139 -0
  19. package/dist/document-operations.js +578 -0
  20. package/dist/docx/blockContentParser.js +17 -7
  21. package/dist/docx/encryption/agileDecryption.js +2 -1
  22. package/dist/docx/encryption/compoundFile.js +4 -2
  23. package/dist/docx/footnoteParser.d.ts +1 -17
  24. package/dist/docx/footnoteParser.js +1 -27
  25. package/dist/docx/groupDrawingParser.d.ts +8 -0
  26. package/dist/docx/groupDrawingParser.js +131 -0
  27. package/dist/docx/numberingParser.d.ts +3 -1
  28. package/dist/docx/numberingParser.js +8 -2
  29. package/dist/docx/paragraphParser.js +9 -3
  30. package/dist/docx/runParser.js +8 -1
  31. package/dist/docx/selectiveXmlPatch.js +4 -10
  32. package/dist/docx/serializer/numberingSerializer.js +3 -1
  33. package/dist/docx/settingsParser.d.ts +6 -3
  34. package/dist/docx/settingsParser.js +2 -0
  35. package/dist/docx/xmlParser.js +11 -4
  36. package/dist/i18n/messages/catalogs.gen.d.ts +1054 -34
  37. package/dist/i18n/messages/catalogs.gen.js +1122 -68
  38. package/dist/i18n/messages/messages.gen.d.ts +62 -2
  39. package/dist/index.d.ts +3 -2
  40. package/dist/index.js +6 -5
  41. package/dist/layout-bridge/convert/footnoteLayout.d.ts +37 -1
  42. package/dist/layout-bridge/convert/footnoteLayout.js +138 -14
  43. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -7
  44. package/dist/layout-bridge/convert/headerFooterLayout.js +5 -7
  45. package/dist/layout-bridge/convert/toFlowBlocks.js +56 -19
  46. package/dist/layout-engine/index.d.ts +6 -5
  47. package/dist/layout-engine/index.js +143 -30
  48. package/dist/layout-engine/keep-together.d.ts +13 -8
  49. package/dist/layout-engine/keep-together.js +40 -25
  50. package/dist/layout-engine/layoutInstrumentation.d.ts +11 -1
  51. package/dist/layout-engine/layoutInstrumentation.js +10 -1
  52. package/dist/layout-engine/measure/measureBlocks.js +50 -18
  53. package/dist/layout-engine/measure/measureParagraph.js +61 -15
  54. package/dist/layout-engine/paginator.d.ts +2 -1
  55. package/dist/layout-engine/paginator.js +9 -3
  56. package/dist/layout-engine/textBoxGroup.d.ts +7 -0
  57. package/dist/layout-engine/textBoxGroup.js +8 -0
  58. package/dist/layout-engine/types.d.ts +30 -9
  59. package/dist/layout-engine/types.js +20 -2
  60. package/dist/layout-painter/renderPage.d.ts +15 -1
  61. package/dist/layout-painter/renderPage.js +27 -11
  62. package/dist/layout-painter/renderParagraph.js +18 -8
  63. package/dist/layout-painter/renderTable.js +10 -7
  64. package/dist/managers/DocumentLoaderManager.js +12 -5
  65. package/dist/model.js +1 -1
  66. package/dist/paged-layout/pageText.d.ts +14 -0
  67. package/dist/paged-layout/pageText.js +21 -0
  68. package/dist/prosemirror/attrs/index.js +2 -0
  69. package/dist/prosemirror/commands/comments.js +75 -5
  70. package/dist/prosemirror/commands/hyperlink.js +4 -2
  71. package/dist/prosemirror/commands/image.js +2 -1
  72. package/dist/prosemirror/commands/propertyChangeScope.d.ts +68 -0
  73. package/dist/prosemirror/commands/propertyChangeScope.js +242 -0
  74. package/dist/prosemirror/conversion/fromProseDoc.js +14 -3
  75. package/dist/prosemirror/conversion/toProseDoc.js +29 -8
  76. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
  77. package/dist/prosemirror/extensions/features/ListExtension.js +6 -0
  78. package/dist/prosemirror/extensions/nodes/TableExtension.js +10 -11
  79. package/dist/prosemirror/findReplaceSelection.js +2 -1
  80. package/dist/prosemirror/schema/nodes.d.ts +10 -5
  81. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  82. package/dist/prosemirror/utils/tabCalculator.js +4 -2
  83. package/dist/redline.d.ts +20 -0
  84. package/dist/redline.js +127 -0
  85. package/dist/server.d.ts +7 -3
  86. package/dist/server.js +6 -2
  87. package/dist/symbols.d.ts +35 -0
  88. package/dist/symbols.js +512 -0
  89. package/dist/utils/clipboard.js +3 -3
  90. package/dist/utils/fontLoader.js +3 -0
  91. package/dist/utils/fontResolver.js +48 -1
  92. package/dist/utils/zoom.d.ts +21 -0
  93. package/dist/utils/zoom.js +21 -0
  94. package/dist/version-comparison.d.ts +92 -0
  95. package/dist/version-comparison.js +436 -0
  96. package/package.json +3 -3
  97. package/dist/paged-layout/headerFooterMargins.d.ts +0 -75
  98. package/dist/paged-layout/headerFooterMargins.js +0 -122
@@ -0,0 +1,578 @@
1
+ import { applyFolioAIEditOperations, previewFolioAIEditOperations } from "./ai-edits/apply.js";
2
+ import { TaggedError } from "better-result";
3
+ //#region src/document-operations.ts
4
+ const FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION = 1;
5
+ const FOLIO_DOCUMENT_OPERATION_TYPES = Object.freeze([
6
+ "replaceInBlock",
7
+ "replaceRange",
8
+ "commentOnRange",
9
+ "formatRange",
10
+ "insertAfterBlock",
11
+ "insertBeforeBlock",
12
+ "replaceBlock",
13
+ "deleteBlock",
14
+ "commentOnBlock",
15
+ "insertSignatureTable"
16
+ ]);
17
+ const FOLIO_DOCUMENT_OPERATION_MODES = Object.freeze(["direct", "tracked-changes"]);
18
+ const FOLIO_DOCUMENT_OPERATION_STORIES = Object.freeze(["main"]);
19
+ const FOLIO_DOCUMENT_OPERATION_PRECONDITIONS = Object.freeze(["blockTextHash"]);
20
+ const FOLIO_DOCUMENT_OPERATION_BATCH_MODES = Object.freeze(["best-effort", "atomic"]);
21
+ const DIRECT_AND_TRACKED_MODES = FOLIO_DOCUMENT_OPERATION_MODES;
22
+ const DIRECT_ONLY_MODES = Object.freeze(["direct"]);
23
+ const FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE = Object.freeze({
24
+ replaceInBlock: DIRECT_AND_TRACKED_MODES,
25
+ replaceRange: DIRECT_AND_TRACKED_MODES,
26
+ commentOnRange: DIRECT_AND_TRACKED_MODES,
27
+ formatRange: DIRECT_ONLY_MODES,
28
+ insertAfterBlock: DIRECT_AND_TRACKED_MODES,
29
+ insertBeforeBlock: DIRECT_AND_TRACKED_MODES,
30
+ replaceBlock: DIRECT_AND_TRACKED_MODES,
31
+ deleteBlock: DIRECT_AND_TRACKED_MODES,
32
+ commentOnBlock: DIRECT_AND_TRACKED_MODES,
33
+ insertSignatureTable: DIRECT_ONLY_MODES
34
+ });
35
+ const DOCUMENT_OPERATION_CAPABILITIES = Object.freeze({
36
+ version: 1,
37
+ operationTypes: FOLIO_DOCUMENT_OPERATION_TYPES,
38
+ modes: FOLIO_DOCUMENT_OPERATION_MODES,
39
+ modesByOperationType: FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE,
40
+ batchModes: FOLIO_DOCUMENT_OPERATION_BATCH_MODES,
41
+ dryRun: true,
42
+ preconditions: FOLIO_DOCUMENT_OPERATION_PRECONDITIONS,
43
+ stories: FOLIO_DOCUMENT_OPERATION_STORIES
44
+ });
45
+ const getFolioDocumentOperationCapabilities = () => DOCUMENT_OPERATION_CAPABILITIES;
46
+ const includesDocumentOperationMode = (supportedModes, mode) => supportedModes.includes(mode);
47
+ const isFolioDocumentOperationModeSupported = (operationType, mode) => {
48
+ const supportedModes = FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE[operationType];
49
+ if (supportedModes === void 0) return false;
50
+ return includesDocumentOperationMode(supportedModes, mode);
51
+ };
52
+ const isSupportedFolioDocumentOperationVersion = (value) => value === 1;
53
+ var UnsupportedFolioDocumentOperationVersionError = class extends TaggedError("UnsupportedFolioDocumentOperationVersionError")() {};
54
+ var InvalidFolioDocumentOperationBatchError = class extends TaggedError("InvalidFolioDocumentOperationBatchError")() {};
55
+ const assertSupportedFolioDocumentOperationVersion = (value) => {
56
+ if (isSupportedFolioDocumentOperationVersion(value)) return value;
57
+ throw new UnsupportedFolioDocumentOperationVersionError({
58
+ message: "Unsupported document operation contract version.",
59
+ receivedVersion: value
60
+ });
61
+ };
62
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
63
+ const invalidBatch = (path, reason) => {
64
+ throw new InvalidFolioDocumentOperationBatchError({
65
+ message: `Invalid document operation batch at ${path}: ${reason}.`,
66
+ path,
67
+ reason
68
+ });
69
+ };
70
+ const assertAllowedKeys = (value, path, allowedKeys) => {
71
+ const unexpected = Object.keys(value).find((key) => !allowedKeys.includes(key));
72
+ if (unexpected !== void 0) invalidBatch(`${path}.${unexpected}`, "unexpected property");
73
+ };
74
+ const readString = (value, key, path) => {
75
+ const candidate = value[key];
76
+ if (typeof candidate === "string") return candidate;
77
+ return invalidBatch(`${path}.${key}`, "expected a string");
78
+ };
79
+ const readOptionalString = (value, key, path) => {
80
+ const candidate = value[key];
81
+ if (candidate === void 0) return;
82
+ if (typeof candidate === "string") return candidate;
83
+ return invalidBatch(`${path}.${key}`, "expected a string when provided");
84
+ };
85
+ const readOptionalBoolean = (value, key, path) => {
86
+ const candidate = value[key];
87
+ if (candidate === void 0) return;
88
+ if (typeof candidate === "boolean") return candidate;
89
+ return invalidBatch(`${path}.${key}`, "expected a boolean when provided");
90
+ };
91
+ const readNonNegativeInteger = (value, key, path) => {
92
+ const candidate = value[key];
93
+ if (typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 0) return candidate;
94
+ return invalidBatch(`${path}.${key}`, "expected a non-negative integer");
95
+ };
96
+ const readTextRange = (value, path) => {
97
+ const candidate = value["range"];
98
+ const rangePath = `${path}.range`;
99
+ if (!isPlainObject(candidate)) return invalidBatch(rangePath, "expected an object");
100
+ assertAllowedKeys(candidate, rangePath, [
101
+ "type",
102
+ "story",
103
+ "blockId",
104
+ "startOffset",
105
+ "endOffset",
106
+ "selectedTextHash"
107
+ ]);
108
+ if (candidate["type"] !== "textRange") return invalidBatch(`${rangePath}.type`, "expected \"textRange\"");
109
+ if (candidate["story"] !== "main") return invalidBatch(`${rangePath}.story`, "expected \"main\"");
110
+ const blockId = readString(candidate, "blockId", rangePath);
111
+ if (blockId.length === 0) return invalidBatch(`${rangePath}.blockId`, "expected a non-empty string");
112
+ const startOffset = readNonNegativeInteger(candidate, "startOffset", rangePath);
113
+ const endOffset = readNonNegativeInteger(candidate, "endOffset", rangePath);
114
+ if (endOffset <= startOffset) return invalidBatch(`${rangePath}.endOffset`, "expected a value greater than startOffset");
115
+ const selectedTextHash = readString(candidate, "selectedTextHash", rangePath);
116
+ if (!/^h[0-9a-z]+$/.test(selectedTextHash)) return invalidBatch(`${rangePath}.selectedTextHash`, "expected a normalized text hash");
117
+ return {
118
+ type: "textRange",
119
+ story: "main",
120
+ blockId,
121
+ startOffset,
122
+ endOffset,
123
+ selectedTextHash
124
+ };
125
+ };
126
+ const readInlineFormatting = (value, path) => {
127
+ const candidate = value["formatting"];
128
+ const formattingPath = `${path}.formatting`;
129
+ if (!isPlainObject(candidate)) return invalidBatch(formattingPath, "expected an object");
130
+ assertAllowedKeys(candidate, formattingPath, [
131
+ "bold",
132
+ "italic",
133
+ "underline"
134
+ ]);
135
+ const bold = readOptionalBoolean(candidate, "bold", formattingPath);
136
+ const italic = readOptionalBoolean(candidate, "italic", formattingPath);
137
+ const underline = readOptionalBoolean(candidate, "underline", formattingPath);
138
+ if (bold === void 0 && italic === void 0 && underline === void 0) return invalidBatch(formattingPath, "expected at least one formatting property");
139
+ return {
140
+ ...bold !== void 0 && { bold },
141
+ ...italic !== void 0 && { italic },
142
+ ...underline !== void 0 && { underline }
143
+ };
144
+ };
145
+ const readOptionalComment = (value, path) => {
146
+ const candidate = value["comment"];
147
+ if (candidate === void 0) return;
148
+ if (isPlainObject(candidate)) {
149
+ assertAllowedKeys(candidate, `${path}.comment`, ["text"]);
150
+ return { text: readString(candidate, "text", `${path}.comment`) };
151
+ }
152
+ return invalidBatch(`${path}.comment`, "expected an object when provided");
153
+ };
154
+ const readOptionalPrecondition = (value, path) => {
155
+ const candidate = value["precondition"];
156
+ if (candidate === void 0) return;
157
+ if (!isPlainObject(candidate)) return invalidBatch(`${path}.precondition`, "expected an object when provided");
158
+ const preconditionPath = `${path}.precondition`;
159
+ assertAllowedKeys(candidate, preconditionPath, ["blockTextHash"]);
160
+ const blockTextHash = readString(candidate, "blockTextHash", preconditionPath);
161
+ if (!/^h[0-9a-z]+$/.test(blockTextHash)) return invalidBatch(`${preconditionPath}.blockTextHash`, "expected a normalized block text hash");
162
+ return { blockTextHash };
163
+ };
164
+ const isReviewSeverity = (value) => value === "low" || value === "medium" || value === "high";
165
+ const readReviewMeta = (value, path) => {
166
+ const severity = value["severity"];
167
+ if (severity !== void 0 && !isReviewSeverity(severity)) return invalidBatch(`${path}.severity`, "expected \"low\", \"medium\", or \"high\" when provided");
168
+ const area = readOptionalString(value, "area", path);
169
+ return {
170
+ ...severity !== void 0 && { severity },
171
+ ...area !== void 0 && { area }
172
+ };
173
+ };
174
+ const COMMON_OPERATION_KEYS = [
175
+ "id",
176
+ "type",
177
+ "blockId",
178
+ "severity",
179
+ "area",
180
+ "precondition"
181
+ ];
182
+ const parseSignatureParties = (value, path) => {
183
+ const parties = value["parties"];
184
+ if (!Array.isArray(parties)) return invalidBatch(`${path}.parties`, "expected an array");
185
+ return parties.map((party, index) => {
186
+ const partyPath = `${path}.parties[${index}]`;
187
+ if (!isPlainObject(party)) return invalidBatch(partyPath, "expected an object");
188
+ assertAllowedKeys(party, partyPath, [
189
+ "name",
190
+ "signatory",
191
+ "title"
192
+ ]);
193
+ const signatory = readOptionalString(party, "signatory", partyPath);
194
+ const title = readOptionalString(party, "title", partyPath);
195
+ const parsedParty = { name: readString(party, "name", partyPath) };
196
+ if (signatory !== void 0) parsedParty.signatory = signatory;
197
+ if (title !== void 0) parsedParty.title = title;
198
+ return parsedParty;
199
+ });
200
+ };
201
+ const parseDocumentOperation = (value, index) => {
202
+ const path = `$.operations[${index}]`;
203
+ if (!isPlainObject(value)) return invalidBatch(path, "expected an object");
204
+ const id = readString(value, "id", path);
205
+ const type = readString(value, "type", path);
206
+ const reviewMeta = readReviewMeta(value, path);
207
+ const precondition = readOptionalPrecondition(value, path);
208
+ const comment = readOptionalComment(value, path);
209
+ const operationMeta = {
210
+ ...reviewMeta,
211
+ ...precondition !== void 0 && { precondition }
212
+ };
213
+ if (type === "replaceRange") {
214
+ assertAllowedKeys(value, path, [
215
+ "id",
216
+ "type",
217
+ "range",
218
+ "replace",
219
+ "comment",
220
+ "severity",
221
+ "area",
222
+ "precondition"
223
+ ]);
224
+ return {
225
+ ...operationMeta,
226
+ id,
227
+ type,
228
+ range: readTextRange(value, path),
229
+ replace: readString(value, "replace", path),
230
+ ...comment !== void 0 && { comment }
231
+ };
232
+ }
233
+ if (type === "commentOnRange") {
234
+ assertAllowedKeys(value, path, [
235
+ "id",
236
+ "type",
237
+ "range",
238
+ "comment",
239
+ "severity",
240
+ "area",
241
+ "precondition"
242
+ ]);
243
+ if (comment === void 0) return invalidBatch(`${path}.comment`, "expected an object");
244
+ return {
245
+ ...operationMeta,
246
+ id,
247
+ type,
248
+ range: readTextRange(value, path),
249
+ comment
250
+ };
251
+ }
252
+ if (type === "formatRange") {
253
+ assertAllowedKeys(value, path, [
254
+ "id",
255
+ "type",
256
+ "range",
257
+ "formatting",
258
+ "severity",
259
+ "area",
260
+ "precondition"
261
+ ]);
262
+ return {
263
+ ...operationMeta,
264
+ id,
265
+ type,
266
+ range: readTextRange(value, path),
267
+ formatting: readInlineFormatting(value, path)
268
+ };
269
+ }
270
+ const blockId = readString(value, "blockId", path);
271
+ if (type === "replaceInBlock") {
272
+ assertAllowedKeys(value, path, [
273
+ ...COMMON_OPERATION_KEYS,
274
+ "find",
275
+ "replace",
276
+ "comment"
277
+ ]);
278
+ return {
279
+ ...operationMeta,
280
+ id,
281
+ type,
282
+ blockId,
283
+ find: readString(value, "find", path),
284
+ replace: readString(value, "replace", path),
285
+ ...comment !== void 0 && { comment }
286
+ };
287
+ }
288
+ if (type === "insertAfterBlock" || type === "insertBeforeBlock") {
289
+ assertAllowedKeys(value, path, [
290
+ ...COMMON_OPERATION_KEYS,
291
+ "text",
292
+ "inheritFormatting",
293
+ "pageBreakBefore",
294
+ "styleId",
295
+ "comment"
296
+ ]);
297
+ const inheritFormatting = readOptionalBoolean(value, "inheritFormatting", path);
298
+ const pageBreakBefore = readOptionalBoolean(value, "pageBreakBefore", path);
299
+ const styleId = readOptionalString(value, "styleId", path);
300
+ return {
301
+ ...operationMeta,
302
+ id,
303
+ type,
304
+ blockId,
305
+ text: readString(value, "text", path),
306
+ ...inheritFormatting !== void 0 && { inheritFormatting },
307
+ ...pageBreakBefore !== void 0 && { pageBreakBefore },
308
+ ...styleId !== void 0 && { styleId },
309
+ ...comment !== void 0 && { comment }
310
+ };
311
+ }
312
+ if (type === "replaceBlock") {
313
+ assertAllowedKeys(value, path, [
314
+ ...COMMON_OPERATION_KEYS,
315
+ "text",
316
+ "preserveFormatting",
317
+ "styleId",
318
+ "comment"
319
+ ]);
320
+ const preserveFormatting = readOptionalBoolean(value, "preserveFormatting", path);
321
+ const styleId = readOptionalString(value, "styleId", path);
322
+ return {
323
+ ...operationMeta,
324
+ id,
325
+ type,
326
+ blockId,
327
+ text: readString(value, "text", path),
328
+ ...preserveFormatting !== void 0 && { preserveFormatting },
329
+ ...styleId !== void 0 && { styleId },
330
+ ...comment !== void 0 && { comment }
331
+ };
332
+ }
333
+ if (type === "deleteBlock") {
334
+ assertAllowedKeys(value, path, [...COMMON_OPERATION_KEYS, "comment"]);
335
+ return {
336
+ ...operationMeta,
337
+ id,
338
+ type,
339
+ blockId,
340
+ ...comment !== void 0 && { comment }
341
+ };
342
+ }
343
+ if (type === "commentOnBlock") {
344
+ assertAllowedKeys(value, path, [
345
+ ...COMMON_OPERATION_KEYS,
346
+ "quote",
347
+ "comment"
348
+ ]);
349
+ if (comment === void 0) return invalidBatch(`${path}.comment`, "expected an object");
350
+ const quote = readOptionalString(value, "quote", path);
351
+ return {
352
+ ...operationMeta,
353
+ id,
354
+ type,
355
+ blockId,
356
+ ...quote !== void 0 && { quote },
357
+ comment
358
+ };
359
+ }
360
+ if (type === "insertSignatureTable") {
361
+ assertAllowedKeys(value, path, [
362
+ ...COMMON_OPERATION_KEYS,
363
+ "position",
364
+ "parties",
365
+ "comment"
366
+ ]);
367
+ const position = value["position"];
368
+ if (position !== void 0 && position !== "after" && position !== "before") return invalidBatch(`${path}.position`, "expected \"after\" or \"before\" when provided");
369
+ return {
370
+ ...operationMeta,
371
+ id,
372
+ type,
373
+ blockId,
374
+ ...position !== void 0 && { position },
375
+ parties: parseSignatureParties(value, path),
376
+ ...comment !== void 0 && { comment }
377
+ };
378
+ }
379
+ return invalidBatch(`${path}.type`, `unsupported operation type "${type}"`);
380
+ };
381
+ const parseFolioDocumentOperationBatch = (value) => {
382
+ if (!isPlainObject(value)) return invalidBatch("$", "expected an object");
383
+ assertAllowedKeys(value, "$", [
384
+ "version",
385
+ "operations",
386
+ "mode",
387
+ "atomic",
388
+ "dryRun"
389
+ ]);
390
+ const version = assertSupportedFolioDocumentOperationVersion(value["version"]);
391
+ const operations = value["operations"];
392
+ if (!Array.isArray(operations)) return invalidBatch("$.operations", "expected an array");
393
+ const mode = value["mode"];
394
+ if (mode !== void 0 && mode !== "direct" && mode !== "tracked-changes") return invalidBatch("$.mode", "expected \"direct\" or \"tracked-changes\" when provided");
395
+ const atomic = readOptionalBoolean(value, "atomic", "$");
396
+ const dryRun = readOptionalBoolean(value, "dryRun", "$");
397
+ const parsedOperations = operations.map(parseDocumentOperation);
398
+ const operationIds = /* @__PURE__ */ new Set();
399
+ for (const [index, operation] of parsedOperations.entries()) {
400
+ if (operationIds.has(operation.id)) return invalidBatch(`$.operations[${index}].id`, "expected a unique operation id");
401
+ operationIds.add(operation.id);
402
+ }
403
+ return {
404
+ version,
405
+ operations: parsedOperations,
406
+ ...mode !== void 0 && { mode },
407
+ ...atomic !== void 0 && { atomic },
408
+ ...dryRun !== void 0 && { dryRun }
409
+ };
410
+ };
411
+ const recoveryByReason = {
412
+ missingBlock: "refreshDocument",
413
+ changedBlock: "refreshDocument",
414
+ ambiguousFind: "narrowMatch",
415
+ missingFind: "refreshDocument",
416
+ unsupportedBlock: "changeTarget",
417
+ unsupportedMode: "changeMode",
418
+ atomicBatchRejected: "inspectBatch",
419
+ preconditionFailed: "refreshDocument",
420
+ staleRange: "refreshDocument",
421
+ emptyOperation: "removeOperation",
422
+ noopOperation: "removeOperation"
423
+ };
424
+ const getFolioDocumentOperationIssues = (operations, skipped) => {
425
+ const indexById = /* @__PURE__ */ new Map();
426
+ operations.forEach(({ id }, index) => {
427
+ indexById.set(id, index);
428
+ });
429
+ return skipped.map(({ id, reason }) => {
430
+ const operationIndex = indexById.get(id) ?? -1;
431
+ return {
432
+ operationId: id,
433
+ operationIndex,
434
+ path: `$.operations[${operationIndex}]`,
435
+ code: reason,
436
+ retryable: reason !== "emptyOperation" && reason !== "noopOperation",
437
+ recovery: recoveryByReason[reason]
438
+ };
439
+ });
440
+ };
441
+ const getPrimaryAffectedTarget = (operation) => {
442
+ switch (operation.type) {
443
+ case "replaceInBlock":
444
+ case "replaceBlock": return {
445
+ type: "block",
446
+ story: "main",
447
+ blockId: operation.blockId,
448
+ effect: "updated"
449
+ };
450
+ case "replaceRange": return {
451
+ type: "block",
452
+ story: "main",
453
+ blockId: operation.range.blockId,
454
+ effect: "updated"
455
+ };
456
+ case "commentOnRange": return {
457
+ type: "textRange",
458
+ range: operation.range,
459
+ effect: "commented"
460
+ };
461
+ case "formatRange": return {
462
+ type: "textRange",
463
+ range: operation.range,
464
+ effect: "formatted"
465
+ };
466
+ case "insertAfterBlock":
467
+ case "insertBeforeBlock": return {
468
+ type: "insertion",
469
+ story: "main",
470
+ anchorBlockId: operation.blockId,
471
+ position: operation.type === "insertBeforeBlock" ? "before" : "after",
472
+ content: "block"
473
+ };
474
+ case "deleteBlock": return {
475
+ type: "block",
476
+ story: "main",
477
+ blockId: operation.blockId,
478
+ effect: "deleted"
479
+ };
480
+ case "commentOnBlock": return {
481
+ type: "block",
482
+ story: "main",
483
+ blockId: operation.blockId,
484
+ effect: "commented"
485
+ };
486
+ case "insertSignatureTable": return {
487
+ type: "insertion",
488
+ story: "main",
489
+ anchorBlockId: operation.blockId,
490
+ position: operation.position ?? "after",
491
+ content: "signatureTable"
492
+ };
493
+ }
494
+ };
495
+ /** Build deterministic affected-target receipts from operations and their applied entries. */
496
+ const getFolioDocumentOperationReceipts = (operations, applied) => {
497
+ const appliedById = /* @__PURE__ */ new Map();
498
+ applied.forEach((operation) => {
499
+ appliedById.set(operation.id, operation);
500
+ });
501
+ const receipts = [];
502
+ operations.forEach((operation, operationIndex) => {
503
+ const appliedOperation = appliedById.get(operation.id);
504
+ if (!appliedOperation) return;
505
+ const affected = [getPrimaryAffectedTarget(operation)];
506
+ if (appliedOperation.commentId !== void 0) affected.push({
507
+ type: "comment",
508
+ commentId: appliedOperation.commentId
509
+ });
510
+ receipts.push({
511
+ operationId: operation.id,
512
+ operationIndex,
513
+ affected
514
+ });
515
+ });
516
+ return receipts;
517
+ };
518
+ const applyFolioDocumentOperations = ({ view, snapshot, batch, author, createCommentId, createUndoHandle }) => {
519
+ const parsedBatch = parseFolioDocumentOperationBatch(batch);
520
+ const apply = ({ targetView, targetCreateCommentId = createCommentId, preview = false }) => {
521
+ return (preview ? previewFolioAIEditOperations : applyFolioAIEditOperations)({
522
+ view: targetView,
523
+ snapshot,
524
+ operations: parsedBatch.operations,
525
+ mode: parsedBatch.mode ?? "tracked-changes",
526
+ ...author !== void 0 && { author },
527
+ ...targetCreateCommentId !== void 0 && { createCommentId: targetCreateCommentId }
528
+ });
529
+ };
530
+ const preview = () => apply({
531
+ targetView: view,
532
+ preview: true
533
+ });
534
+ const atomicResult = (previewResult, status) => {
535
+ const skippedById = new Map(previewResult.skipped.map((operation) => [operation.id, operation]));
536
+ const skipped = parsedBatch.operations.map(({ id }) => skippedById.get(id) ?? {
537
+ id,
538
+ reason: "atomicBatchRejected"
539
+ });
540
+ return {
541
+ version: 1,
542
+ status,
543
+ applied: [],
544
+ skipped,
545
+ issues: getFolioDocumentOperationIssues(parsedBatch.operations, skipped),
546
+ receipts: [],
547
+ undoHandle: null
548
+ };
549
+ };
550
+ if (parsedBatch.dryRun === true) {
551
+ const previewResult = preview();
552
+ if (parsedBatch.atomic === true && previewResult.skipped.length > 0) return atomicResult(previewResult, "previewed");
553
+ return {
554
+ version: 1,
555
+ status: "previewed",
556
+ applied: previewResult.applied.map(({ id }) => ({ id })),
557
+ skipped: previewResult.skipped,
558
+ issues: getFolioDocumentOperationIssues(parsedBatch.operations, previewResult.skipped),
559
+ receipts: getFolioDocumentOperationReceipts(parsedBatch.operations, previewResult.applied),
560
+ undoHandle: null
561
+ };
562
+ }
563
+ if (parsedBatch.atomic === true) {
564
+ const previewResult = preview();
565
+ if (previewResult.skipped.length > 0) return atomicResult(previewResult, "rejected");
566
+ }
567
+ const result = apply({ targetView: view });
568
+ return {
569
+ version: 1,
570
+ status: "committed",
571
+ ...result,
572
+ issues: getFolioDocumentOperationIssues(parsedBatch.operations, result.skipped),
573
+ receipts: getFolioDocumentOperationReceipts(parsedBatch.operations, result.applied),
574
+ undoHandle: result.applied.length > 0 && createUndoHandle !== void 0 ? createUndoHandle() : null
575
+ };
576
+ };
577
+ //#endregion
578
+ export { 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 };
@@ -57,19 +57,30 @@ const computeListMarker = (paragraph, numbering, listCounters, abstractCounters)
57
57
  if (!listRendering || !numbering) return;
58
58
  const { numId, level } = listRendering;
59
59
  if (numId === 0) return;
60
- if (!listCounters.has(numId)) listCounters.set(numId, Array.from({ length: 9 }).fill(0));
60
+ if (!listCounters.has(numId)) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
61
61
  const counters = listCounters.get(numId);
62
62
  if (!counters) return;
63
63
  const abstractNumId = numbering.getAbstractNumId(numId);
64
+ const styleNumbering = paragraph.formatting?.numPrFromStyle;
65
+ if (abstractNumId !== null && styleNumbering) {
66
+ const latestAbstractCounters = abstractCounters.get(abstractNumId);
67
+ if (latestAbstractCounters) for (let i = 0; i < counters.length; i += 1) counters[i] = latestAbstractCounters[i] ?? NaN;
68
+ }
64
69
  if (abstractNumId !== null && level > 0) {
65
70
  const latestAbstractCounters = abstractCounters.get(abstractNumId);
66
- const missingParentCounters = counters.slice(0, level).every((value) => value === 0);
67
- if (latestAbstractCounters && missingParentCounters) for (let i = 0; i < level; i += 1) counters[i] = latestAbstractCounters[i] ?? 0;
71
+ if (counters.slice(0, level).every(Number.isNaN)) for (let i = 0; i < level; i += 1) {
72
+ const latestCounter = latestAbstractCounters?.[i];
73
+ counters[i] = latestCounter !== void 0 && !Number.isNaN(latestCounter) ? latestCounter : numbering.getLevel(numId, i)?.start ?? 1;
74
+ }
68
75
  }
69
- counters[level] = (counters[level] || 0) + 1;
70
- for (let i = level + 1; i < counters.length; i += 1) counters[i] = 0;
76
+ if (Number.isNaN(counters[level])) counters[level] = (numbering.getLevel(numId, level)?.start ?? 1) - 1;
77
+ counters[level] = (counters[level] ?? 0) + 1;
78
+ for (let i = level + 1; i < counters.length; i += 1) counters[i] = NaN;
71
79
  const childAdvances = listRendering.implicitChildLevelAdvances ?? 0;
72
- if (childAdvances > 0 && level + 1 < counters.length) counters[level + 1] = (counters[level + 1] ?? 0) + childAdvances;
80
+ if (childAdvances > 0 && level + 1 < counters.length) {
81
+ const childCounter = counters[level + 1];
82
+ counters[level + 1] = (childCounter === void 0 || Number.isNaN(childCounter) ? 0 : childCounter) + childAdvances;
83
+ }
73
84
  if (abstractNumId !== null) abstractCounters.set(abstractNumId, [...counters]);
74
85
  const pattern = listRendering.marker;
75
86
  if (listRendering.isBullet) {
@@ -90,7 +101,6 @@ const computeListMarker = (paragraph, numbering, listCounters, abstractCounters)
90
101
  listRendering.marker = computedMarker;
91
102
  };
92
103
  const enrichParagraphTextBoxes = (paragraph, paraXml, styles, theme, numbering, rels, media) => {
93
- if (paragraph.content.length === 0) return;
94
104
  const xmlChildren = getChildElements(paraXml);
95
105
  let parsedIndex = 0;
96
106
  let lastConsumedRun;
@@ -124,7 +124,8 @@ const hashPassword = async (password, salt, iterations, hashName) => {
124
124
  };
125
125
  /** [MS-OFFCRYPTO] 2.3.4.9 — finalize a derived key with a block key suffix. */
126
126
  const finalizeDerivedKey = async (passwordHash, blockKey, hashName, keyBits) => {
127
- return (await digest(webHashName(hashName), joinBytes([passwordHash, blockKey]))).subarray(0, keyBits / 8);
127
+ const algorithm = webHashName(hashName);
128
+ return (await digest(algorithm, joinBytes([passwordHash, blockKey]))).subarray(0, keyBits / 8);
128
129
  };
129
130
  const segmentIv = async (packageSalt, segmentIndex, hashName, blockBytes) => {
130
131
  return (await digest(webHashName(hashName), joinBytes([packageSalt, writeUint32Le(segmentIndex)]))).subarray(0, blockBytes);
@@ -86,7 +86,8 @@ const loadFatSectorIds = (file, header) => {
86
86
  let chain = header.difatStart;
87
87
  for (let step = 0; step < header.difatSectorCount; step++) {
88
88
  if (chain === SECTOR_END || chain === SECTOR_FREE) break;
89
- const view = viewOf(loadSector(file, header, chain));
89
+ const sector = loadSector(file, header, chain);
90
+ const view = viewOf(sector);
90
91
  const slotsPerSector = header.sectorBytes / 4 - 1;
91
92
  for (let slot = 0; slot < slotsPerSector; slot++) {
92
93
  const id = u32(view, slot * 4);
@@ -100,7 +101,8 @@ const loadFatSectorIds = (file, header) => {
100
101
  const loadFat = (file, header) => {
101
102
  const table = [];
102
103
  for (const fatSectorId of loadFatSectorIds(file, header)) {
103
- const view = viewOf(loadSector(file, header, fatSectorId));
104
+ const sector = loadSector(file, header, fatSectorId);
105
+ const view = viewOf(sector);
104
106
  const entries = header.sectorBytes / 4;
105
107
  for (let i = 0; i < entries; i++) table.push(u32(view, i * 4));
106
108
  }
@@ -73,22 +73,6 @@ declare function isSeparatorFootnote(footnote: document_d_exports.Footnote): boo
73
73
  * Check if an endnote is a separator (not regular content)
74
74
  */
75
75
  declare function isSeparatorEndnote(endnote: document_d_exports.Endnote): boolean;
76
- /**
77
- * Get footnote number for display (excluding separators)
78
- * @param footnote - The footnote to get the number for
79
- * @param footnoteMap - The footnote map
80
- * @param startNumber - Starting number (default 1)
81
- * @returns The display number, or null for separator footnotes
82
- */
83
- declare function getFootnoteDisplayNumber(footnote: document_d_exports.Footnote, footnoteMap: FootnoteMap, startNumber?: number): number | null;
84
- /**
85
- * Get endnote number for display (excluding separators)
86
- * @param endnote - The endnote to get the number for
87
- * @param endnoteMap - The endnote map
88
- * @param startNumber - Starting number (default 1)
89
- * @returns The display number, or null for separator endnotes
90
- */
91
- declare function getEndnoteDisplayNumber(endnote: document_d_exports.Endnote, endnoteMap: EndnoteMap, startNumber?: number): number | null;
92
76
  /**
93
77
  * Create an empty footnote map
94
78
  */
@@ -106,4 +90,4 @@ declare function mergeFootnoteMaps(...maps: FootnoteMap[]): FootnoteMap;
106
90
  */
107
91
  declare function mergeEndnoteMaps(...maps: EndnoteMap[]): EndnoteMap;
108
92
  //#endregion
109
- export { EndnoteMap, FootnoteMap, createEmptyEndnoteMap, createEmptyFootnoteMap, getEndnoteDisplayNumber, getEndnoteText, getFootnoteDisplayNumber, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote, mergeEndnoteMaps, mergeFootnoteMaps, parseEndnoteProperties, parseEndnotes, parseFootnoteProperties, parseFootnotes };
93
+ export { EndnoteMap, FootnoteMap, createEmptyEndnoteMap, createEmptyFootnoteMap, getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote, mergeEndnoteMaps, mergeFootnoteMaps, parseEndnoteProperties, parseEndnotes, parseFootnoteProperties, parseFootnotes };