docxodus 12.6.4 → 13.0.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 (92) hide show
  1. package/README.md +8 -5
  2. package/dist/canonical.d.ts.map +1 -1
  3. package/dist/canonical.js +7 -3
  4. package/dist/canonical.js.map +1 -1
  5. package/dist/conversion-options.d.ts +16 -0
  6. package/dist/conversion-options.d.ts.map +1 -0
  7. package/dist/conversion-options.js +31 -0
  8. package/dist/conversion-options.js.map +1 -0
  9. package/dist/core.d.ts +17 -9
  10. package/dist/core.d.ts.map +1 -1
  11. package/dist/core.js +23 -24
  12. package/dist/core.js.map +1 -1
  13. package/dist/docxodus.worker.js +2 -1073
  14. package/dist/docxodus.worker.js.map +7 -1
  15. package/dist/editor.bundle.js +35 -14296
  16. package/dist/editor.bundle.js.map +7 -0
  17. package/dist/editor.d.ts +33 -2
  18. package/dist/editor.d.ts.map +1 -1
  19. package/dist/editor.js +6739 -4773
  20. package/dist/editor.js.map +7 -1
  21. package/dist/embed.bundle.js +159 -18703
  22. package/dist/embed.bundle.js.map +7 -0
  23. package/dist/embed.d.ts +1 -1
  24. package/dist/embed.iife.js +37 -18595
  25. package/dist/embed.iife.js.map +7 -0
  26. package/dist/embed.js +1 -1
  27. package/dist/export-assets.json +27 -27
  28. package/dist/export-browser.bundle.js +1388 -477
  29. package/dist/export-browser.d.ts +20 -0
  30. package/dist/export-browser.d.ts.map +1 -1
  31. package/dist/export-browser.js +132 -37
  32. package/dist/export-browser.js.map +1 -1
  33. package/dist/font-runtime.d.ts.map +1 -1
  34. package/dist/font-runtime.js +54 -0
  35. package/dist/font-runtime.js.map +1 -1
  36. package/dist/line-metrics.d.ts +60 -0
  37. package/dist/line-metrics.d.ts.map +1 -0
  38. package/dist/line-metrics.js +343 -0
  39. package/dist/line-metrics.js.map +1 -0
  40. package/dist/pagination.bundle.js +3 -3214
  41. package/dist/pagination.bundle.js.map +7 -0
  42. package/dist/pagination.d.ts +86 -9
  43. package/dist/pagination.d.ts.map +1 -1
  44. package/dist/pagination.js +358 -55
  45. package/dist/pagination.js.map +1 -1
  46. package/dist/react.d.ts.map +1 -1
  47. package/dist/react.js +63 -12
  48. package/dist/react.js.map +1 -1
  49. package/dist/render-report-v2.schema.json +29 -0
  50. package/dist/session.bundle.js +2 -2013
  51. package/dist/session.bundle.js.map +7 -0
  52. package/dist/session.d.ts +14 -5
  53. package/dist/session.d.ts.map +1 -1
  54. package/dist/session.js +62 -29
  55. package/dist/session.js.map +1 -1
  56. package/dist/types.d.ts +137 -21
  57. package/dist/types.d.ts.map +1 -1
  58. package/dist/types.js +14 -0
  59. package/dist/types.js.map +1 -1
  60. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  61. package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
  62. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  63. package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
  64. package/dist/wasm/_framework/System.Collections.wasm +0 -0
  65. package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
  66. package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
  67. package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
  68. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  69. package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
  70. package/dist/wasm/_framework/System.Memory.wasm +0 -0
  71. package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
  72. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  73. package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
  74. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
  75. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
  76. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  77. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
  78. package/dist/wasm/_framework/dotnet.boot.js +11 -11
  79. package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
  80. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  81. package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
  82. package/dist/word-text.d.ts +14 -0
  83. package/dist/word-text.d.ts.map +1 -0
  84. package/dist/word-text.js +377 -0
  85. package/dist/word-text.js.map +1 -0
  86. package/dist/worker-proxy.bundle.js +2 -536
  87. package/dist/worker-proxy.bundle.js.map +7 -0
  88. package/dist/worker-proxy.d.ts +14 -1
  89. package/dist/worker-proxy.d.ts.map +1 -1
  90. package/dist/worker-proxy.js +357 -339
  91. package/dist/worker-proxy.js.map +1 -1
  92. package/package.json +13 -12
@@ -1,2013 +1,2 @@
1
- "use strict";
2
- var DocxodusSession = (() => {
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
-
21
- // src/session.ts
22
- var session_exports = {};
23
- __export(session_exports, {
24
- ContextBoundary: () => ContextBoundary,
25
- DocxSession: () => DocxSession,
26
- PlaceholderKinds: () => PlaceholderKinds,
27
- openDocxSession: () => openDocxSession
28
- });
29
-
30
- // src/verification-request.ts
31
- function serializeVerificationRequest(request) {
32
- if (request === null || typeof request !== "object" || Array.isArray(request)) {
33
- throw new RangeError("a verification request must be an object");
34
- }
35
- const { companionArtifacts, ...rest } = request;
36
- const wire = { ...rest };
37
- if (companionArtifacts !== void 0) {
38
- wire.companionArtifacts = companionArtifacts.map(encodeCompanion);
39
- }
40
- return JSON.stringify(wire);
41
- }
42
- function encodeCompanion(artifact) {
43
- const { bytes, ...rest } = artifact;
44
- const wire = { ...rest };
45
- if (bytes !== void 0) wire.bytesB64 = bytesToBase64(bytes);
46
- return wire;
47
- }
48
- function bytesToBase64(bytes) {
49
- if (typeof btoa === "function") {
50
- let binary = "";
51
- const chunkSize = 32768;
52
- for (let i = 0; i < bytes.length; i += chunkSize) {
53
- const chunk = bytes.subarray(i, i + chunkSize);
54
- binary += String.fromCharCode.apply(null, chunk);
55
- }
56
- return btoa(binary);
57
- }
58
- return Buffer.from(bytes).toString("base64");
59
- }
60
-
61
- // src/types.ts
62
- var PlaceholderKinds = {
63
- BlankFill: 1,
64
- AlternativeClause: 2,
65
- Instruction: 4,
66
- All: 7
67
- };
68
- var DiffFormat = {
69
- Json: 0,
70
- Unified: 1,
71
- SideBySide: 2
72
- };
73
- var ContextBoundary = {
74
- Char: 0,
75
- Bracket: 1,
76
- Sentence: 2,
77
- Comma: 3
78
- };
79
-
80
- // src/session.ts
81
- function mutationBatchChangeSet(before, after, key) {
82
- const beforeGroups = /* @__PURE__ */ new Map();
83
- const afterGroups = /* @__PURE__ */ new Map();
84
- const group = (items, target) => {
85
- items.forEach((item, index) => {
86
- const identity = key(item);
87
- const indices = target.get(identity) ?? [];
88
- indices.push(index);
89
- target.set(identity, indices);
90
- });
91
- };
92
- group(before, beforeGroups);
93
- group(after, afterGroups);
94
- const beforeMatched = before.map(() => false);
95
- const afterMatched = after.map(() => false);
96
- const modified = after.map(() => false);
97
- for (const [identity, afterIndices] of afterGroups) {
98
- const beforeIndices = beforeGroups.get(identity) ?? [];
99
- for (const afterIndex of afterIndices) {
100
- const beforeIndex = beforeIndices.find((index) => !beforeMatched[index] && JSON.stringify(before[index]) === JSON.stringify(after[afterIndex]));
101
- if (beforeIndex === void 0) continue;
102
- beforeMatched[beforeIndex] = true;
103
- afterMatched[afterIndex] = true;
104
- }
105
- const remainingBefore = beforeIndices.filter((index) => !beforeMatched[index]);
106
- const remainingAfter = afterIndices.filter((index) => !afterMatched[index]);
107
- const modifiedCount = Math.min(remainingBefore.length, remainingAfter.length);
108
- for (let index = 0; index < modifiedCount; index++) {
109
- beforeMatched[remainingBefore[index]] = true;
110
- afterMatched[remainingAfter[index]] = true;
111
- modified[remainingAfter[index]] = true;
112
- }
113
- }
114
- return {
115
- added: after.filter((_, index) => !afterMatched[index]),
116
- removed: before.filter((_, index) => !beforeMatched[index]),
117
- modified: after.filter((_, index) => modified[index])
118
- };
119
- }
120
- var BATCH_REPLAY_CAVEATS = {
121
- revisionDates: "Tracked-revision date attributes may use the execution clock; compare revision ids, authors, types, text, and anchors across separate executions.",
122
- commentDates: "Comment date attributes may be generated from the execution clock; supply dates explicitly when byte-identical replay is required.",
123
- annotationMetadata: "Auto-generated annotation ids or creation timestamps are execution metadata; supply id and created explicitly when byte-identical replay is required.",
124
- createdAnchors: "Created anchors and related OOXML ids may be generated independently on replay; preview/apply equivalence is semantic and packageHash or anchor ids may differ."
125
- };
126
- var BATCH_REPLAY_CAVEAT_TEXTS = new Set(Object.values(BATCH_REPLAY_CAVEATS));
127
- var RETAINED_PREVIEW_RECEIPTS = 8;
128
- var DocxSession = class _DocxSession {
129
- /** @internal */
130
- constructor(handle, wasm) {
131
- /**
132
- * The receipts this client composed for previews it retained, keyed by previewId. The host
133
- * keeps the package and the binding; the step receipts live only here, so a commit can return
134
- * the previewed steps and change sets without the host re-parsing a client-composed result.
135
- */
136
- this.retainedPreviewReceipts = /* @__PURE__ */ new Map();
137
- // ─── Raw escape hatch ────────────────────────────────────────────────
138
- this.raw = {
139
- getXml: (anchorId) => this.wasm.RawGetXml(this.handle, anchorId),
140
- insertXml: (anchorId, position, xml) => JSON.parse(this.wasm.RawInsertXml(this.handle, anchorId, position, xml)),
141
- replaceXml: (anchorId, xml) => JSON.parse(this.wasm.RawReplaceXml(this.handle, anchorId, xml))
142
- };
143
- this.handle = handle;
144
- this.wasm = wasm;
145
- }
146
- // ─── View ────────────────────────────────────────────────────────────
147
- project() {
148
- return JSON.parse(this.wasm.Project(this.handle));
149
- }
150
- /** Monotonic document version (0 at open; +1 per committed mutation/undo/redo). */
151
- getVersion() {
152
- return JSON.parse(this.wasm.GetVersion(this.handle)).version;
153
- }
154
- /**
155
- * Verification manifest for the current logical checkpoint. Unsaved edits are included and
156
- * the read does not mutate package bytes, caches, history, or the document version.
157
- */
158
- getPackageManifest() {
159
- return JSON.parse(this.wasm.GetPackageManifest(this.handle));
160
- }
161
- /** Register a browser-materialized PageMap without changing the document version. */
162
- registerPageMap(pageMap, expectedRendererFingerprint) {
163
- return JSON.parse(this.wasm.RegisterPageMap(
164
- this.handle,
165
- JSON.stringify(pageMap),
166
- expectedRendererFingerprint ?? ""
167
- ));
168
- }
169
- getPageMapStatus(request) {
170
- return JSON.parse(this.wasm.GetPageMapStatus(
171
- this.handle,
172
- request ? JSON.stringify(request) : ""
173
- ));
174
- }
175
- getPageCitation(anchorId, request) {
176
- return JSON.parse(this.wasm.GetPageCitation(
177
- this.handle,
178
- anchorId,
179
- JSON.stringify(request)
180
- ));
181
- }
182
- /** Evaluate optimistic guards without mutating or advancing the version. */
183
- checkPreconditions(preconditions) {
184
- return JSON.parse(
185
- this.wasm.CheckPreconditions(this.handle, JSON.stringify(preconditions))
186
- );
187
- }
188
- /**
189
- * Guard any synchronous mutation. WASM calls are synchronous and single-threaded, so the
190
- * check and callback form one uninterrupted client-side operation. Prefer a method's native
191
- * `preconditions` option where it has one (notably replaceTextRange's match-count guard).
192
- */
193
- runWithPreconditions(preconditions, mutation) {
194
- const checked = this.checkPreconditions(preconditions);
195
- return checked.success ? mutation() : checked;
196
- }
197
- /**
198
- * Execute synchronous mutations atomically by default. Atomic success is one undo/version
199
- * unit; any failed or thrown step restores the exact package and history checkpoint.
200
- */
201
- executeBatch(steps, mode = "atomic", transaction) {
202
- if (mode !== "atomic" && mode !== "best_effort") {
203
- throw new RangeError(`unknown mutation batch mode: ${String(mode)}`);
204
- }
205
- if (transaction === void 0) return this.recorded(steps, mode, void 0, () => this.runBatch(steps, mode));
206
- return this.runTransactional(
207
- transaction,
208
- mode,
209
- (identity) => this.recorded(steps, mode, identity, () => this.runBatch(steps, mode))
210
- );
211
- }
212
- /**
213
- * What the session's host-owned delivery evidence recorder holds (issue #748). `enabled` is
214
- * false, with the reason, unless the session was opened with `captureDeliveryEvidence`.
215
- */
216
- getDeliveryEvidenceStatus() {
217
- if (!this.wasm.GetDeliveryEvidenceStatus) {
218
- throw new Error("This WASM bundle predates delivery evidence; rebuild docxodus.");
219
- }
220
- return JSON.parse(this.wasm.GetDeliveryEvidenceStatus(this.handle));
221
- }
222
- /**
223
- * Build the receipt-bearing delivery of this session through the shared bundle service: the
224
- * clean current package (`final-docx`), the source-to-delivered semantic delta, and the change
225
- * receipt (`change-receipt`) minted from the captured evidence — verifiable with
226
- * `verifyDeliveryReceipt` against the returned artifact bytes. A history that cannot be
227
- * attested yields an `incomplete` bundle whose receipt artifact carries the reason, and
228
- * `evidence.unavailableReason` says why.
229
- */
230
- buildDeliveryReceipt(options) {
231
- if (!this.wasm.BuildDeliveryReceipt) {
232
- throw new Error("This WASM bundle predates delivery evidence; rebuild docxodus.");
233
- }
234
- const wire = JSON.parse(this.wasm.BuildDeliveryReceipt(this.handle, JSON.stringify(options ?? {})));
235
- const decode = (base64) => Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
236
- return {
237
- status: wire.status,
238
- verified: wire.verified,
239
- manifest: wire.manifest,
240
- manifestBytes: decode(wire.manifestBytes),
241
- artifacts: wire.artifacts.map((artifact) => {
242
- const { bytes, ...rest } = artifact;
243
- return bytes === void 0 ? rest : { ...rest, bytes: decode(bytes) };
244
- }),
245
- ...wire.evidence ? { evidence: wire.evidence } : {}
246
- };
247
- }
248
- /**
249
- * Describe a batch to the session's delivery evidence recorder before running it, and hand
250
- * it the step results afterwards, so the version steps the batch produced are recorded under
251
- * this description rather than as unlabeled mutations. A bundle without the recorder, or a
252
- * session not capturing evidence, simply runs the batch.
253
- */
254
- recorded(steps, mode, identity, run) {
255
- if (!this.wasm.BeginDeliveryEvidence || !this.wasm.CompleteDeliveryEvidence || !this.wasm.AbandonDeliveryEvidence) {
256
- return run();
257
- }
258
- const described = this.wasm.BeginDeliveryEvidence(
259
- this.handle,
260
- JSON.stringify(steps.map((step) => step.args === void 0 ? { tool: step.tool, action: step.action } : { tool: step.tool, action: step.action, args: step.args })),
261
- mode,
262
- identity ? JSON.stringify({ transactionId: identity.transactionId, requestFingerprint: identity.requestFingerprint }) : ""
263
- );
264
- if (!described) return run();
265
- let result;
266
- try {
267
- result = run();
268
- return result;
269
- } finally {
270
- if (result) this.wasm.CompleteDeliveryEvidence(this.handle, JSON.stringify(result.steps));
271
- else this.wasm.AbandonDeliveryEvidence(this.handle);
272
- }
273
- }
274
- /**
275
- * Make a preview retained by {@link previewBatch} (`retain: true`) the live document exactly
276
- * as previewed: the previewed package is restored byte-for-byte as one undo step, so the
277
- * generated anchor ids, timestamps and `packageHash` are exactly those the preview reported.
278
- * The commit is guarded — it refuses with `preview_stale`, editing nothing, when the session's
279
- * version, package content, tracked-changes mode or revision author moved since the preview,
280
- * and with `preview_not_found` once the preview expired, was evicted, or was already
281
- * committed. A `transaction` makes a retry after a lost response safe, as for
282
- * {@link executeBatch}.
283
- */
284
- commitPreview(previewId, transaction) {
285
- if (!this.wasm.CommitPreview) {
286
- throw new Error("This WASM bundle predates retained previews; rebuild docxodus.");
287
- }
288
- const commit = () => {
289
- const envelope = JSON.parse(this.wasm.CommitPreview(this.handle, previewId));
290
- if (!envelope.success) {
291
- if (envelope.failure?.error.code === "preview_not_found") this.retainedPreviewReceipts.delete(previewId);
292
- return envelope;
293
- }
294
- const receipt = this.retainedPreviewReceipts.get(previewId);
295
- this.retainedPreviewReceipts.delete(previewId);
296
- if (!receipt) return envelope;
297
- return {
298
- ...receipt,
299
- preview: false,
300
- html: null,
301
- baseVersion: envelope.baseVersion,
302
- resultVersion: envelope.resultVersion,
303
- packageHash: envelope.packageHash,
304
- warnings: receipt.warnings.filter((warning) => !BATCH_REPLAY_CAVEAT_TEXTS.has(warning)),
305
- retention: envelope.retention
306
- };
307
- };
308
- if (transaction === void 0) return commit();
309
- return this.runTransactional(transaction, "atomic", () => commit());
310
- }
311
- runTransactional(transaction, mode, body) {
312
- if (!this.wasm.BeginMutationTransaction || !this.wasm.CompleteMutationTransaction || !this.wasm.AbandonMutationTransaction) {
313
- throw new Error("This WASM bundle predates mutation transactions; rebuild docxodus.");
314
- }
315
- if (typeof transaction.transactionId !== "string") {
316
- throw new RangeError("transactionId must be a string");
317
- }
318
- if (transaction.request === null || typeof transaction.request !== "object" || Array.isArray(transaction.request)) {
319
- throw new RangeError("a mutation transaction needs a request object describing the batch");
320
- }
321
- const decision = JSON.parse(this.wasm.BeginMutationTransaction(
322
- this.handle,
323
- transaction.transactionId,
324
- JSON.stringify({ ...transaction.request, mode })
325
- ));
326
- if (decision.response !== null) {
327
- return JSON.parse(decision.response);
328
- }
329
- let completed = false;
330
- try {
331
- const result = {
332
- ...body(decision.transaction),
333
- transaction: decision.transaction
334
- };
335
- this.wasm.CompleteMutationTransaction(
336
- this.handle,
337
- transaction.transactionId,
338
- JSON.stringify(result)
339
- );
340
- completed = true;
341
- return result;
342
- } finally {
343
- if (!completed) this.wasm.AbandonMutationTransaction(this.handle, transaction.transactionId);
344
- }
345
- }
346
- runBatch(steps, mode) {
347
- const baseVersion = this.getVersion();
348
- const observationWarnings = [];
349
- const inspect = (label, read, fallback) => {
350
- try {
351
- return read();
352
- } catch (error) {
353
- observationWarnings.push(`${label} unavailable: ${error instanceof Error ? error.message : String(error)}`);
354
- return fallback;
355
- }
356
- };
357
- const beforeRevisions = inspect("Revision delta inspection", () => this.listRevisions(), []);
358
- const beforeComments = inspect("Comment delta inspection", () => this.listComments(), []);
359
- const beforeAnnotations = inspect("Annotation delta inspection", () => this.listAnnotations(), []);
360
- const complete = (result) => {
361
- try {
362
- const revisionChanges = mutationBatchChangeSet(
363
- beforeRevisions,
364
- inspect("Revision delta inspection", () => this.listRevisions(), beforeRevisions),
365
- (revision) => revision.id
366
- );
367
- const commentChanges = mutationBatchChangeSet(
368
- beforeComments,
369
- inspect("Comment delta inspection", () => this.listComments(), beforeComments),
370
- (comment) => comment.anchorId
371
- );
372
- const annotationChanges = mutationBatchChangeSet(
373
- beforeAnnotations,
374
- inspect("Annotation delta inspection", () => this.listAnnotations(), beforeAnnotations),
375
- (annotation) => annotation.id ?? ""
376
- );
377
- const resultVersion = inspect(
378
- "Result version inspection",
379
- () => this.getVersion(),
380
- baseVersion
381
- );
382
- const warnings = [...observationWarnings];
383
- if ([...revisionChanges.added, ...revisionChanges.modified].some((revision) => revision.date !== void 0 && revision.date !== null)) {
384
- warnings.push(BATCH_REPLAY_CAVEATS.revisionDates);
385
- }
386
- if ([...commentChanges.added, ...commentChanges.modified].some((comment) => comment.date !== void 0 && comment.date !== null)) {
387
- warnings.push(BATCH_REPLAY_CAVEATS.commentDates);
388
- }
389
- if (annotationChanges.added.some((annotation) => annotation.created !== void 0 && annotation.created !== null)) {
390
- warnings.push(BATCH_REPLAY_CAVEATS.annotationMetadata);
391
- }
392
- if (result.steps.some((step) => step.results.some((edit) => edit.created.length > 0))) {
393
- warnings.push(BATCH_REPLAY_CAVEATS.createdAnchors);
394
- }
395
- if (mode === "best_effort" && !result.success) {
396
- warnings.push("Best-effort execution retains every successful step despite later failures.");
397
- }
398
- let packageHash = null;
399
- if (!this.wasm.GetPackageContentHash) {
400
- warnings.push("This WASM bundle predates package equivalence hashes; packageHash is unavailable.");
401
- } else {
402
- try {
403
- packageHash = this.wasm.GetPackageContentHash(this.handle);
404
- } catch (error) {
405
- warnings.push(`Package equivalence hash unavailable: ${error instanceof Error ? error.message : String(error)}`);
406
- }
407
- }
408
- return {
409
- ...result,
410
- preview: false,
411
- baseVersion,
412
- resultVersion,
413
- packageHash,
414
- revisionChanges,
415
- commentChanges,
416
- annotationChanges,
417
- warnings,
418
- html: null
419
- };
420
- } catch (error) {
421
- return {
422
- ...result,
423
- preview: false,
424
- baseVersion,
425
- resultVersion: inspect("Result version inspection", () => this.getVersion(), baseVersion),
426
- packageHash: null,
427
- revisionChanges: { added: [], removed: [], modified: [] },
428
- commentChanges: { added: [], removed: [], modified: [] },
429
- annotationChanges: { added: [], removed: [], modified: [] },
430
- warnings: [
431
- ...observationWarnings,
432
- `Batch receipt enrichment unavailable: ${error instanceof Error ? error.message : String(error)}`
433
- ],
434
- html: null
435
- };
436
- }
437
- };
438
- const internalFailure = (value) => ({
439
- success: false,
440
- error: { code: "internal_error", message: value instanceof Error ? value.message : String(value) },
441
- created: [],
442
- removed: [],
443
- modified: []
444
- });
445
- const run = (step) => {
446
- try {
447
- const value = step.mutation();
448
- const results = Array.isArray(value) ? value : [value];
449
- if (results.some((result) => result === null || typeof result !== "object" || typeof result.success !== "boolean")) {
450
- return [internalFailure("batch mutation returned invalid edit results")];
451
- }
452
- return results;
453
- } catch (error) {
454
- return [internalFailure(error)];
455
- }
456
- };
457
- const failureOf = (step, rolledBack) => ({
458
- index: step.index,
459
- tool: step.tool,
460
- action: step.action,
461
- error: step.results.find((result) => !result.success)?.error ?? { code: "internal_error", message: "batch step failed without an error" },
462
- rolledBack
463
- });
464
- const preflightOne = (step) => {
465
- try {
466
- return step.preflight?.();
467
- } catch (error) {
468
- return internalFailure(error).error;
469
- }
470
- };
471
- if (mode === "atomic") {
472
- const preflight = steps.map(preflightOne);
473
- const failedPreflight = preflight.findIndex((error) => error !== void 0);
474
- if (failedPreflight >= 0) {
475
- const source = steps[failedPreflight];
476
- const failed2 = {
477
- index: failedPreflight,
478
- tool: source.tool,
479
- action: source.action,
480
- success: false,
481
- rolledBack: true,
482
- results: [{ success: false, error: preflight[failedPreflight], created: [], removed: [], modified: [] }]
483
- };
484
- return complete({
485
- mode,
486
- status: "failed",
487
- success: false,
488
- rolledBack: true,
489
- steps: [failed2],
490
- failure: failureOf(failed2, true)
491
- });
492
- }
493
- const transaction = this.wasm.BeginTransaction(this.handle);
494
- const completed2 = [];
495
- try {
496
- for (let index = 0; index < steps.length; index++) {
497
- const source = steps[index];
498
- const results = run(source);
499
- const step = {
500
- index,
501
- tool: source.tool,
502
- action: source.action,
503
- success: results.every((result) => result.success),
504
- rolledBack: false,
505
- results
506
- };
507
- completed2.push(step);
508
- if (!step.success) {
509
- this.wasm.RollbackTransaction(transaction);
510
- const rolledBack = completed2.map((value) => ({ ...value, rolledBack: true }));
511
- const failed2 = rolledBack[rolledBack.length - 1];
512
- return complete({
513
- mode,
514
- status: "failed",
515
- success: false,
516
- rolledBack: true,
517
- steps: rolledBack,
518
- failure: failureOf(failed2, true)
519
- });
520
- }
521
- }
522
- this.wasm.CommitTransaction(transaction);
523
- return complete({ mode, status: "ok", success: true, rolledBack: false, steps: completed2 });
524
- } catch (error) {
525
- try {
526
- this.wasm.RollbackTransaction(transaction);
527
- } catch {
528
- }
529
- throw error;
530
- }
531
- }
532
- const completed = steps.map((source, index) => {
533
- const preflight = preflightOne(source);
534
- const results = preflight ? [{ success: false, error: preflight, created: [], removed: [], modified: [] }] : run(source);
535
- return {
536
- index,
537
- tool: source.tool,
538
- action: source.action,
539
- success: results.every((result) => result.success),
540
- rolledBack: false,
541
- results
542
- };
543
- });
544
- const failed = completed.find((step) => !step.success);
545
- return complete({
546
- mode,
547
- status: failed ? completed.some((step) => step.success) ? "partial" : "failed" : "ok",
548
- success: failed === void 0,
549
- rolledBack: false,
550
- steps: completed,
551
- failure: failed ? failureOf(failed, false) : void 0
552
- });
553
- }
554
- /**
555
- * Execute the same callback batch algorithm against a complete isolated package clone.
556
- * Callbacks receive the shadow session explicitly; mutate that argument. The live session's
557
- * package, caches, version, configuration, and undo/redo history are never execution targets.
558
- */
559
- previewBatch(steps, mode = "atomic", options) {
560
- if (mode !== "atomic" && mode !== "best_effort") {
561
- throw new RangeError(`unknown mutation batch mode: ${String(mode)}`);
562
- }
563
- const htmlMode = options?.html ?? "none";
564
- if (htmlMode !== "none" && htmlMode !== "scoped" && htmlMode !== "full") {
565
- throw new RangeError(`unknown preview HTML mode: ${String(htmlMode)}`);
566
- }
567
- if (!this.wasm.OpenPreviewSession) {
568
- throw new Error("This WASM bundle does not support isolated mutation previews.");
569
- }
570
- const shadow = new _DocxSession(this.wasm.OpenPreviewSession(this.handle), this.wasm);
571
- try {
572
- const result = shadow.executeBatch(
573
- steps.map((step) => ({
574
- tool: step.tool,
575
- action: step.action,
576
- mutation: () => step.mutation(shadow),
577
- preflight: step.preflight ? () => step.preflight(shadow) : void 0
578
- })),
579
- mode
580
- );
581
- const warnings = [...result.warnings];
582
- let html = null;
583
- const unwrapRendered = (rendered) => {
584
- if (rendered.trimStart().startsWith("{")) {
585
- const envelope = JSON.parse(rendered);
586
- if (envelope.error) {
587
- warnings.push(`Preview HTML could not be generated: ${envelope.error}`);
588
- return null;
589
- }
590
- }
591
- return rendered;
592
- };
593
- const legacyProfileWarning = "This WASM bundle predates the shared preview HTML profile; preview HTML omits comments, annotations and headers/footers and may differ from other surfaces.";
594
- try {
595
- if (htmlMode === "scoped") {
596
- if (!options?.htmlAnchorId) {
597
- warnings.push("Scoped HTML was requested without htmlAnchorId; no HTML was generated.");
598
- } else if (this.wasm.RenderPreviewBlockHtml) {
599
- html = unwrapRendered(
600
- this.wasm.RenderPreviewBlockHtml(shadow.handle, options.htmlAnchorId)
601
- );
602
- } else {
603
- warnings.push(legacyProfileWarning);
604
- html = shadow.renderBlock(options.htmlAnchorId);
605
- }
606
- } else if (htmlMode === "full") {
607
- if (this.wasm.RenderPreviewHtml) {
608
- html = unwrapRendered(this.wasm.RenderPreviewHtml(shadow.handle));
609
- } else {
610
- warnings.push(legacyProfileWarning);
611
- html = unwrapRendered(this.wasm.RenderHtmlForReview ? this.wasm.RenderHtmlForReview(shadow.handle, "docx-", false, false, 1, true) : this.wasm.RenderHtml(shadow.handle, "docx-", false, false, 1));
612
- }
613
- }
614
- } catch (error) {
615
- warnings.push(`Preview HTML could not be generated: ${error instanceof Error ? error.message : String(error)}`);
616
- }
617
- let retention;
618
- if (options?.retain) {
619
- if (!this.wasm.RetainPreview) {
620
- throw new Error("This WASM bundle predates retained previews; rebuild docxodus.");
621
- }
622
- if (!result.success) {
623
- warnings.push("The preview did not succeed, so it was not retained for commit.");
624
- } else {
625
- const retained = JSON.parse(this.wasm.RetainPreview(shadow.handle));
626
- warnings.push(...retained.warnings);
627
- retention = retained.retention ?? void 0;
628
- }
629
- }
630
- const preview = retention ? { ...result, preview: true, warnings, html, retention } : { ...result, preview: true, warnings, html };
631
- if (retention) {
632
- if (this.retainedPreviewReceipts.size >= RETAINED_PREVIEW_RECEIPTS) {
633
- this.retainedPreviewReceipts.delete(this.retainedPreviewReceipts.keys().next().value);
634
- }
635
- this.retainedPreviewReceipts.set(retention.previewId, { ...preview, html: null });
636
- }
637
- return preview;
638
- } finally {
639
- shadow.close();
640
- }
641
- }
642
- /**
643
- * Project a slice of the document keyed off an anchor — useful for showing
644
- * one section to an LLM at a time without paying the cost of projecting the
645
- * whole document.
646
- *
647
- * - `ProjectionDepth.SelfOnly` — just the addressed block (one paragraph,
648
- * row, etc.).
649
- * - `ProjectionDepth.Subtree` — the block + descendants (e.g. a table with
650
- * all its rows/cells, but no following content).
651
- * - `ProjectionDepth.SubtreeAndFollowingSiblings` (default) — for headings
652
- * this returns the whole section (heading + content up to the next same-
653
- * or-higher heading); for non-headings it behaves like `Subtree`.
654
- *
655
- * @see docs/architecture/docx_mutation_api.md
656
- */
657
- projectAnchor(anchorId, depth = 2 /* SubtreeAndFollowingSiblings */, citation) {
658
- return JSON.parse(
659
- citation ? this.wasm.ProjectAnchorWithCitations(this.handle, anchorId, depth, JSON.stringify(citation)) : this.wasm.ProjectAnchor(this.handle, anchorId, depth)
660
- );
661
- }
662
- /**
663
- * Render a single block to faithful HTML from the live session — the editor's
664
- * incremental per-block re-render after an edit. Resolves against the in-memory
665
- * document (no Save round-trip). `anchorId` is a block anchor (`kind:scope:unid`)
666
- * or the bare unid carried by a `data-anchor` attribute. Returns the block's HTML
667
- * element (no `<html>`/`<head>` wrapper).
668
- */
669
- renderBlock(anchorId, options) {
670
- const html = this.wasm.RenderBlockHtml(
671
- this.handle,
672
- anchorId,
673
- options?.cssPrefix ?? "docx-",
674
- options?.fabricateClasses ?? false
675
- );
676
- if (html.charCodeAt(0) === 123) {
677
- const err = JSON.parse(html);
678
- throw new Error(`renderBlock failed: ${err.error ?? "unknown error"}`);
679
- }
680
- return html;
681
- }
682
- // ─── Tier A: text CRUD ───────────────────────────────────────────────
683
- replaceText(anchorId, markdown, preconditions) {
684
- const apply = () => JSON.parse(
685
- this.wasm.ReplaceText(this.handle, anchorId, markdown)
686
- );
687
- return preconditions ? this.runWithPreconditions(
688
- { ...preconditions, anchorId: preconditions.anchorId ?? anchorId },
689
- apply
690
- ) : apply();
691
- }
692
- deleteBlock(anchorId, preconditions) {
693
- const apply = () => JSON.parse(this.wasm.DeleteBlock(this.handle, anchorId));
694
- return preconditions ? this.runWithPreconditions(
695
- { ...preconditions, anchorId: preconditions.anchorId ?? anchorId },
696
- apply
697
- ) : apply();
698
- }
699
- /** Reorder one top-level paragraph/heading/list/table block relative to another. */
700
- moveBlock(sourceAnchorId, targetAnchorId, position) {
701
- return JSON.parse(
702
- this.wasm.MoveBlock(this.handle, sourceAnchorId, targetAnchorId, position)
703
- );
704
- }
705
- /**
706
- * Delete every top-level block-level sibling between `fromAnchorId` (inclusive)
707
- * and `toAnchorIdExclusive` (exclusive). Both anchors must share a direct
708
- * parent and live in the same package part. Returns a single `EditResult`
709
- * whose `removed` lists every anchor that was deleted.
710
- *
711
- * Records ONE undo snapshot — `undo()` restores the entire range.
712
- *
713
- * @see docs/architecture/docx_mutation_api.md#deleterange
714
- */
715
- deleteRange(fromAnchorId, toAnchorIdExclusive) {
716
- return JSON.parse(this.wasm.DeleteRange(this.handle, fromAnchorId, toAnchorIdExclusive));
717
- }
718
- /**
719
- * Delete a heading and everything below it up to (but not including) the next
720
- * heading at the same or higher level. The heading anchor must have `kind === "h"`.
721
- *
722
- * If the target is the last heading in its parent, the section extends to the
723
- * end of the parent (heading + everything after).
724
- *
725
- * @see docs/architecture/docx_mutation_api.md#deletesection
726
- */
727
- deleteSection(headingAnchorId) {
728
- return JSON.parse(this.wasm.DeleteSection(this.handle, headingAnchorId));
729
- }
730
- // ─── Tier B: structural ──────────────────────────────────────────────
731
- insertParagraph(anchorId, position, markdown) {
732
- return JSON.parse(this.wasm.InsertParagraph(this.handle, anchorId, position, markdown));
733
- }
734
- splitParagraph(anchorId, characterOffset) {
735
- return JSON.parse(this.wasm.SplitParagraph(this.handle, anchorId, characterOffset));
736
- }
737
- mergeParagraphs(firstAnchorId, secondAnchorId) {
738
- return JSON.parse(this.wasm.MergeParagraphs(this.handle, firstAnchorId, secondAnchorId));
739
- }
740
- /**
741
- * Insert an empty paragraph carrying a bottom border — an S-1-style horizontal rule —
742
- * before/after the block. `rule` styles the line (default: a single ≈1.5pt black rule).
743
- */
744
- insertHorizontalRule(anchorId, position, rule) {
745
- const ruleJson = rule ? JSON.stringify(rule) : "";
746
- return JSON.parse(
747
- this.wasm.InsertHorizontalRule(this.handle, anchorId, position, ruleJson)
748
- );
749
- }
750
- /**
751
- * Insert a `rows`×`cols` table before/after the block. `options` controls borders, row-major
752
- * cell markdown, and cell alignment. The returned `EditResult.created` lists canonical `tc`
753
- * anchors (row-major), so each cell can then be addressed to fill/format.
754
- */
755
- insertTable(anchorId, position, rows, cols, options) {
756
- const optionsJson = options ? JSON.stringify(options) : "";
757
- return JSON.parse(
758
- this.wasm.InsertTable(this.handle, anchorId, position, rows, cols, optionsJson)
759
- );
760
- }
761
- /** Resolve a canonical `tbl` anchor to explicit table/row/column/cell identities. */
762
- getTableMetadata(tableAnchorId) {
763
- return JSON.parse(this.wasm.GetTableMetadata(this.handle, tableAnchorId));
764
- }
765
- /** Resolve a canonical `tc` anchor to its zero-based table-grid coordinate and spans. */
766
- resolveTableCellAnchor(cellAnchorId) {
767
- return JSON.parse(
768
- this.wasm.ResolveTableCellAnchor(this.handle, cellAnchorId)
769
- );
770
- }
771
- /** Resolve a zero-based table-grid coordinate to the physical `tc` covering it. */
772
- resolveTableCellCoordinate(tableAnchorId, rowIndex, columnIndex) {
773
- return JSON.parse(
774
- this.wasm.ResolveTableCellCoordinate(this.handle, tableAnchorId, rowIndex, columnIndex)
775
- );
776
- }
777
- /**
778
- * Table row/column editing, addressed by the canonical `tc` anchor returned from
779
- * {@link insertTable}'s `created` or table metadata. Insert clones the reference row/column's
780
- * widths and starts empty (`created` lists new `tc` anchors); delete of the last row/column removes
781
- * the whole table. All four are grid-aware: inserting across a merge extends it, deleting
782
- * through one narrows it, and deleting a vertical merge's lead row promotes the next row to
783
- * carry it — the grid is never left ragged.
784
- */
785
- insertTableRow(cellAnchorId, position) {
786
- return JSON.parse(this.wasm.InsertTableRow(this.handle, cellAnchorId, position));
787
- }
788
- insertTableColumn(cellAnchorId, position) {
789
- return JSON.parse(this.wasm.InsertTableColumn(this.handle, cellAnchorId, position));
790
- }
791
- deleteTableRow(cellAnchorId) {
792
- return JSON.parse(this.wasm.DeleteTableRow(this.handle, cellAnchorId));
793
- }
794
- deleteTableColumn(cellAnchorId) {
795
- return JSON.parse(this.wasm.DeleteTableColumn(this.handle, cellAnchorId));
796
- }
797
- /**
798
- * Merge the rectangle of cells anchored at `cellAnchorId` running `rowSpan` rows down ×
799
- * `colSpan` cells right (Word's *Merge Cells*): `w:gridSpan` for the horizontal extent,
800
- * `w:vMerge` restart/continue for the vertical one. The rectangle must tile the same whole grid
801
- * columns in every row it covers and must not clip a vertical merge entering from above or
802
- * continuing below — a partial overlap fails with `invalid_table_merge` instead of tearing the
803
- * grid. `content` decides what happens to the absorbed cells' content (default `"append"`).
804
- */
805
- mergeCells(cellAnchorId, rowSpan, colSpan, content = "append") {
806
- return JSON.parse(
807
- this.wasm.MergeCells(this.handle, cellAnchorId, rowSpan, colSpan, content)
808
- );
809
- }
810
- /**
811
- * Split the merged cell at `cellAnchorId` back into unit cells, dropping its `w:gridSpan` and
812
- * `w:vMerge` markup and restoring one cell per grid column (each taking its `w:tblGrid` width).
813
- * Addressing a vertical-merge continuation unmerges the whole run. A cell with no merge markup
814
- * fails with `invalid_table_merge`.
815
- */
816
- unmergeCells(cellAnchorId) {
817
- return JSON.parse(this.wasm.UnmergeCells(this.handle, cellAnchorId));
818
- }
819
- /**
820
- * Table styling, addressed by a canonical `tc` anchor — the post-insert counterpart of
821
- * {@link insertTable}'s options (issue #315 Stage A). `setColumnWidths` retunes `w:tblGrid` +
822
- * every row's cell width (one positive twip value per column) and pins the table to fixed
823
- * layout, exactly as inserting with explicit `columnWidths` would.
824
- */
825
- setColumnWidths(cellAnchorId, widthsTwips) {
826
- return JSON.parse(
827
- this.wasm.SetColumnWidths(this.handle, cellAnchorId, JSON.stringify(widthsTwips))
828
- );
829
- }
830
- /**
831
- * Set the table-level borders (`w:tblPr/w:tblBorders`) of the table containing the anchor.
832
- * Only the edges named by `spec.scope` are written; the rest are left untouched. Style
833
- * `"none"` removes the targeted edges. Omitting `spec` writes a thin single border all round.
834
- */
835
- setTableBorders(cellAnchorId, spec) {
836
- return JSON.parse(
837
- this.wasm.SetTableBorders(this.handle, cellAnchorId, spec ? JSON.stringify(spec) : "")
838
- );
839
- }
840
- /**
841
- * Shade the cell containing the anchor — or, with scope `"row"`, every cell of its row
842
- * (header-row banding). `fill` is a hex RRGGBB triplet (leading '#' tolerated) or `"auto"`;
843
- * `null` removes the shading.
844
- */
845
- setCellShading(cellAnchorId, fill, scope = "cell") {
846
- return JSON.parse(
847
- this.wasm.SetCellShading(this.handle, cellAnchorId, fill ?? "", scope)
848
- );
849
- }
850
- /**
851
- * Mark (or unmark) the row containing the anchor as a repeating header row
852
- * (`w:trPr/w:tblHeader`), so a multi-page table re-shows it on every page. Word only honors
853
- * the flag on a run of rows starting at the table's first row.
854
- */
855
- setRepeatHeaderRow(cellAnchorId, repeat) {
856
- return JSON.parse(
857
- this.wasm.SetRepeatHeaderRow(this.handle, cellAnchorId, repeat)
858
- );
859
- }
860
- /** Apply row layout options to the row containing the canonical cell anchor. */
861
- setTableRowOptions(cellAnchorId, options) {
862
- return JSON.parse(
863
- this.wasm.SetTableRowOptions(
864
- this.handle,
865
- cellAnchorId,
866
- options.repeatHeader ?? null,
867
- options.allowBreakAcrossPages ?? null,
868
- options.heightTwips ?? null,
869
- options.heightRule ?? "atLeast"
870
- )
871
- );
872
- }
873
- // ─── Headers / footers / page numbers ────────────────────────────────
874
- /**
875
- * Set the running header story for the section that owns `anchorId` (any body block in that
876
- * section) to `markdown`. Creates the header part + `w:headerReference` if the story of `kind`
877
- * doesn't exist yet, else replaces its content. The created header-paragraph anchors (scope
878
- * `hdr{N}`) come back in `EditResult.created` — insert a page number into one with
879
- * {@link insertPageNumberField}. `"first"` sets the section's title-page flag; `"even"` sets the
880
- * document's even/odd-headers flag.
881
- */
882
- setHeaderText(anchorId, kind, markdown) {
883
- return JSON.parse(this.wasm.SetHeaderText(this.handle, anchorId, kind, markdown));
884
- }
885
- /** Set the running footer story for the section that owns `anchorId` — see {@link setHeaderText};
886
- * the created footer-paragraph anchors (scope `ftr{N}`) come back in `EditResult.created`. */
887
- setFooterText(anchorId, kind, markdown) {
888
- return JSON.parse(this.wasm.SetFooterText(this.handle, anchorId, kind, markdown));
889
- }
890
- /**
891
- * Append a page-number field to the paragraph `anchorId` — typically a header/footer paragraph
892
- * returned by {@link setFooterText}/{@link setHeaderText}. `"currentPage"` emits a PAGE field,
893
- * `"totalPages"` a NUMPAGES field (native complex field with a cached result), and
894
- * `"pageOfTotal"` Word's "Page X of Y" gallery entry — the text `Page `, a PAGE field, the text
895
- * ` of ` and a NUMPAGES field, every run inheriting the paragraph's last run formatting (the
896
- * pieces cannot be composed from the single-field form: text after a field cannot be appended
897
- * without rewriting the field's result). Center it by setting the paragraph alignment
898
- * ({@link setParagraphFormat}). Returns the paragraph anchor in `modified`.
899
- *
900
- * `format` writes the field's own `\*` general-formatting switch (`PAGE \* roman` → `i, ii, iii`).
901
- * Omitting it — the default — emits a plain field, which is what Word inserts and what follows the
902
- * SECTION's format ({@link setPageNumbering}). Prefer the section setting for ordinary page
903
- * numbering: a switch here overrides it for this one field and keeps overriding it if the section
904
- * later changes.
905
- */
906
- insertPageNumberField(anchorId, field = "currentPage", format) {
907
- return JSON.parse(
908
- this.wasm.InsertPageNumberField(this.handle, anchorId, field, format ?? "")
909
- );
910
- }
911
- /**
912
- * Set the page-numbering properties (`w:pgNumType`) of the section that owns `anchorId` (any body
913
- * block in that section) — Word's *Format Page Numbers…* dialog: which number the section starts
914
- * at and which format its pages use. Omitted fields on `op` are left unchanged, so the start can
915
- * be set without disturbing the format and vice versa. Creates the element, and a trailing
916
- * `w:sectPr`, if absent.
917
- *
918
- * Applying values the section already has is a successful no-op that does NOT consume undo
919
- * history — safe to call from a dropdown's change handler.
920
- */
921
- setPageNumbering(anchorId, op) {
922
- return JSON.parse(
923
- this.wasm.SetPageNumbering(this.handle, anchorId, JSON.stringify(op))
924
- );
925
- }
926
- /**
927
- * Insert a **table of contents** before or after `anchorId` (issue #607). The field is written
928
- * dirty and the document asks for a field update on open, so Word paginates and fills the table
929
- * itself — nothing here ships a cached result that is stale the moment anything above it moves.
930
- *
931
- * The table is wrapped in the `w:sdt` content control Word puts around one, which is what gives
932
- * it the *Update Table* control in Word's UI. Word's `TOCHeading` and `TOC1` styles are
933
- * find-or-created; a document that already defines them keeps its own.
934
- *
935
- * Body anchors only, and refused under tracked-change recording: a generated table is regenerated
936
- * wholesale on every field update, so there is no reversible way to redline it.
937
- */
938
- insertTableOfContents(anchorId, pos = "before", options) {
939
- return JSON.parse(
940
- this.wasm.InsertTableOfContents(
941
- this.handle,
942
- anchorId,
943
- pos,
944
- options ? JSON.stringify(options) : ""
945
- )
946
- );
947
- }
948
- /**
949
- * Insert a **table of figures** — the captions carrying `options.captionLabel` and their page
950
- * numbers. Same field mechanics as {@link insertTableOfContents}; Word writes this one as a bare
951
- * paragraph rather than inside a content control, so this does too.
952
- */
953
- insertTableOfFigures(anchorId, pos = "before", options) {
954
- return JSON.parse(
955
- this.wasm.InsertTableOfFigures(
956
- this.handle,
957
- anchorId,
958
- pos,
959
- options ? JSON.stringify(options) : ""
960
- )
961
- );
962
- }
963
- /**
964
- * Insert a **table of authorities** — the cases, statutes or other authorities marked in the
965
- * document, grouped by `options.category`. The table lists entries the document has MARKED with
966
- * `TA` fields; a document with no marked citations produces a table that is correct and empty.
967
- */
968
- insertTableOfAuthorities(anchorId, pos = "before", options) {
969
- return JSON.parse(
970
- this.wasm.InsertTableOfAuthorities(
971
- this.handle,
972
- anchorId,
973
- pos,
974
- options ? JSON.stringify(options) : ""
975
- )
976
- );
977
- }
978
- /**
979
- * Remove the section's page-numbering start/format: it reverts to continuing the previous
980
- * section's numbering in Word's default `1, 2, 3`. Chapter-numbering attributes
981
- * (`w:chapStyle`/`w:chapSep`) are preserved. A section with nothing to clear is a no-op.
982
- */
983
- clearPageNumbering(anchorId) {
984
- return JSON.parse(this.wasm.ClearPageNumbering(this.handle, anchorId));
985
- }
986
- /**
987
- * Make the `kind` header/footer stories of the section that owns `anchorId` actually RENDER:
988
- * `"first"` sets `w:titlePg`, `"even"` sets the document-global `w:evenAndOddHeaders`;
989
- * `"default"` needs no flag and succeeds as a no-op. Idempotent.
990
- *
991
- * {@link setHeaderText}/{@link setFooterText} set these flags while writing content, which covers
992
- * authoring a story from scratch — but NOT a document that already carries a first/even reference
993
- * with the flag absent (Word leaves exactly that behind when "Different first page" is switched
994
- * off). Editing such a story through the text ops otherwise yields header content that is present
995
- * but invisible. Note the `"even"` caveat from {@link setHeaderText}: the flag is document-global
996
- * and governs footers too.
997
- */
998
- ensureHeaderFooterVisible(anchorId, kind) {
999
- return JSON.parse(this.wasm.EnsureHeaderFooterVisible(this.handle, anchorId, kind));
1000
- }
1001
- /**
1002
- * Word's "Different first page" / "Different odd & even pages" checkboxes as one verb: switch
1003
- * the `kind` story of the section that owns `anchorId` (any body block in it) on or off.
1004
- * `enabled: true` is exactly {@link ensureHeaderFooterVisible}; `enabled: false` removes the
1005
- * flag that selects the story — `w:titlePg` from the governing `w:sectPr` for `"first"`, the
1006
- * document-global `w:evenAndOddHeaders` for `"even"` — and leaves the story parts in place,
1007
- * which is what Word does when the checkbox is cleared (re-enabling brings the content straight
1008
- * back). Disabling `"default"` fails with `invalid_page_setup`: that story has no flag.
1009
- *
1010
- * A flag already in the requested state is a successful no-op that records NO undo step, so a
1011
- * checkbox handler can call this unconditionally. Read the current state from
1012
- * {@link SectionInfo.titlePage} / {@link SectionInfo.evenAndOddHeaders}.
1013
- */
1014
- setHeaderFooterKindEnabled(anchorId, kind, enabled) {
1015
- if (!this.wasm.SetHeaderFooterKindEnabled) {
1016
- throw new Error("SetHeaderFooterKindEnabled is not available in this WASM build");
1017
- }
1018
- return JSON.parse(
1019
- this.wasm.SetHeaderFooterKindEnabled(this.handle, anchorId, kind, enabled)
1020
- );
1021
- }
1022
- /**
1023
- * Set the page geometry (`w:pgSz` / `w:pgMar`) of the section that owns `anchorId` — Word's
1024
- * *Page Setup* dialog: page size, orientation, margins and header/footer distance. Omitted
1025
- * fields on `op` are left unchanged; the elements are created in schema order when absent, as
1026
- * is a trailing `w:sectPr` for a body that has none. Read the result back with
1027
- * {@link getSectionInfo}.
1028
- *
1029
- * `landscape: true` without an explicit size swaps a portrait-shaped page's width and height
1030
- * (and `false` swaps back), matching Word's orientation toggle. Invalid geometry — a
1031
- * non-positive size, a negative margin, or opposing margins that leave no room — fails with
1032
- * `invalid_page_setup` and touches nothing. Values the section already has are a successful
1033
- * no-op that consumes no undo history.
1034
- */
1035
- setPageSetup(anchorId, op) {
1036
- if (!this.wasm.SetPageSetup) {
1037
- throw new Error("SetPageSetup is not available in this WASM build");
1038
- }
1039
- return JSON.parse(
1040
- this.wasm.SetPageSetup(this.handle, anchorId, JSON.stringify(op))
1041
- );
1042
- }
1043
- // ─── Footnotes / endnotes ────────────────────────────────────────────
1044
- /**
1045
- * Create a footnote whose body is `markdown` and cite it from the body paragraph `anchorId`, at
1046
- * `characterOffset` characters into that paragraph's text (0 = before all text, text length =
1047
- * after all of it). On a document with no footnotes yet this also creates the footnotes part,
1048
- * Word's two reserved separator notes, the `FootnoteText`/`FootnoteReference` styles and the
1049
- * `w:footnotePr` settings declaration; otherwise the existing part is reused. The note id is
1050
- * allocated above every id already used in the package, so non-contiguous ids can't collide.
1051
- *
1052
- * Returns the created note anchors in `EditResult.created` — the definition (kind `fn`) and its
1053
- * paragraphs (kind `p`, scope `fn`) — so the note can immediately be edited with
1054
- * {@link replaceText} or removed with {@link deleteBlock} (which also drops the body reference).
1055
- *
1056
- * Body paragraphs only: Word does not allow a note reference inside a header/footer story or
1057
- * inside another note, so a non-body anchor fails with `anchorWrongKind`.
1058
- */
1059
- insertFootnote(anchorId, characterOffset, markdown) {
1060
- return JSON.parse(
1061
- this.wasm.InsertFootnote(this.handle, anchorId, characterOffset, markdown)
1062
- );
1063
- }
1064
- /**
1065
- * Insert a Word-faithful internal cross-reference — a `REF` field targeting an existing
1066
- * bookmark — at a character offset (issue #545). The field carries a cached result run
1067
- * (the bookmarked text, or the target's auto-number under `referenceNumber`), so
1068
- * renderers that do not recompute fields show a faithful snapshot and Word updates it
1069
- * like a hand-authored cross-reference. A missing or incoherent bookmark fails with
1070
- * `missing_bookmark_target`.
1071
- */
1072
- insertCrossReference(anchorId, characterOffset, bookmarkName, options) {
1073
- return JSON.parse(
1074
- this.wasm.InsertCrossReference(
1075
- this.handle,
1076
- anchorId,
1077
- characterOffset,
1078
- bookmarkName,
1079
- options ? JSON.stringify(options) : ""
1080
- )
1081
- );
1082
- }
1083
- /** Create an endnote — see {@link insertFootnote}; writes the endnotes part and a
1084
- * `w:endnoteReference`, and the created definition anchor has kind `en`. */
1085
- insertEndnote(anchorId, characterOffset, markdown) {
1086
- return JSON.parse(
1087
- this.wasm.InsertEndnote(this.handle, anchorId, characterOffset, markdown)
1088
- );
1089
- }
1090
- // ─── Comments (issue #300) ───────────────────────────────────────────
1091
- /**
1092
- * Add a **native Word comment** (real `w:comment` markup, visible in Word/Google
1093
- * Docs/LibreOffice's Reviewing pane — not the {@link addAnnotation} overlay) on the body
1094
- * paragraph `anchorId`. `span` selects the commented character range; `null` comments the
1095
- * whole block. On a document with no comments yet this also creates the comments part and
1096
- * the `CommentText`/`CommentReference` styles. `opts.date` (ISO-8601) is written only when
1097
- * provided, keeping output deterministic by default.
1098
- *
1099
- * Returns the created definition anchor (kind `cmt`) and its paragraph anchors (kind `p`,
1100
- * scope `cmt`) in `EditResult.created`, so the comment can immediately be edited with
1101
- * {@link updateComment} or removed with {@link removeComment}.
1102
- *
1103
- * Body paragraphs only (Word has no comments-on-comments); a non-body anchor fails with
1104
- * `anchor_wrong_kind`, a zero-length span with `empty_comment_span`.
1105
- */
1106
- addComment(anchorId, span, author, markdown, opts) {
1107
- const spanJson = span ? JSON.stringify(span) : "";
1108
- return JSON.parse(
1109
- this.wasm.AddComment(
1110
- this.handle,
1111
- anchorId,
1112
- spanJson,
1113
- author,
1114
- opts?.initials ?? "",
1115
- opts?.date ?? "",
1116
- markdown
1117
- )
1118
- );
1119
- }
1120
- /** Add a native Word comment around the exact live extent of a tracked revision returned by
1121
- * {@link listRevisions}. Accepting/rejecting the revision keeps the comment and either leaves
1122
- * its range on surviving text or collapses it to a point. */
1123
- addCommentToRevision(revisionId, author, markdown, opts) {
1124
- return JSON.parse(
1125
- this.wasm.AddCommentToRevision(
1126
- this.handle,
1127
- revisionId,
1128
- author,
1129
- opts?.initials ?? "",
1130
- opts?.date ?? "",
1131
- markdown
1132
- )
1133
- );
1134
- }
1135
- /**
1136
- * Add a native Word reply to `parentCommentAnchorId`. It adds an adjacent reference and
1137
- * inherits the thread root's range through `w15:paraIdParent`; the required
1138
- * `commentsExtended.xml` and `commentsIds.xml` parts are created when the parent was flat.
1139
- */
1140
- addCommentReply(parentCommentAnchorId, author, markdown, opts) {
1141
- return JSON.parse(
1142
- this.wasm.AddCommentReply(
1143
- this.handle,
1144
- parentCommentAnchorId,
1145
- author,
1146
- opts?.initials ?? "",
1147
- opts?.date ?? "",
1148
- markdown
1149
- )
1150
- );
1151
- }
1152
- /** Replace a comment's body text, addressed by its definition anchor (kind `cmt`); the
1153
- * comment's author/initials/date are preserved, as is the last paragraph's `w14:paraId`
1154
- * (Word's reply-threading key). */
1155
- updateComment(commentAnchorId, markdown) {
1156
- return JSON.parse(this.wasm.UpdateComment(this.handle, commentAnchorId, markdown));
1157
- }
1158
- /** Resolve or reopen one comment (`false` reopens it). Flat comments are upgraded with the
1159
- * paraId-keyed metadata Word uses, and the operation is undoable. */
1160
- setCommentResolved(commentAnchorId, resolved) {
1161
- return JSON.parse(
1162
- this.wasm.SetCommentResolved(this.handle, commentAnchorId, resolved)
1163
- );
1164
- }
1165
- /** Remove a comment: the definition, its body marker triple everywhere in the package, and
1166
- * any `commentsExtended`/`commentsIds` threading entries keyed by it. */
1167
- removeComment(commentAnchorId) {
1168
- return JSON.parse(this.wasm.RemoveComment(this.handle, commentAnchorId));
1169
- }
1170
- /** The document's native Word comments in comments-part order. */
1171
- listComments() {
1172
- return JSON.parse(this.wasm.ListComments(this.handle));
1173
- }
1174
- listHyperlinks(scopes = 63 /* All */) {
1175
- return JSON.parse(this.wasm.ListHyperlinks(this.handle, scopes));
1176
- }
1177
- addHyperlink(anchorId, span, kind, target) {
1178
- return JSON.parse(this.wasm.AddHyperlink(
1179
- this.handle,
1180
- anchorId,
1181
- span.start,
1182
- span.length,
1183
- kind,
1184
- target
1185
- ));
1186
- }
1187
- updateHyperlink(hyperlinkId, kind, target) {
1188
- return JSON.parse(this.wasm.UpdateHyperlink(this.handle, hyperlinkId, kind, target));
1189
- }
1190
- removeHyperlink(hyperlinkId) {
1191
- return JSON.parse(this.wasm.RemoveHyperlink(this.handle, hyperlinkId));
1192
- }
1193
- /** Versioned operational facts for native image inspection/mutation in this runtime. */
1194
- getImageCapabilities() {
1195
- return JSON.parse(this.wasm.GetImageCapabilities());
1196
- }
1197
- listImages(scopes = 63 /* All */) {
1198
- return JSON.parse(this.wasm.ListImages(this.handle, scopes));
1199
- }
1200
- insertImage(anchorId, characterOffset, bytes, options = {}) {
1201
- return JSON.parse(this.wasm.InsertImage(
1202
- this.handle,
1203
- anchorId,
1204
- characterOffset,
1205
- imageBytesToBase64(bytes),
1206
- JSON.stringify(options)
1207
- ));
1208
- }
1209
- replaceImage(imageId, bytes) {
1210
- return JSON.parse(this.wasm.ReplaceImage(
1211
- this.handle,
1212
- imageId,
1213
- imageBytesToBase64(bytes)
1214
- ));
1215
- }
1216
- /**
1217
- * Convert an external linked picture into an embedded one using bytes the caller fetched;
1218
- * the engine never reaches the network itself. Refused for pictures that already embed
1219
- * their media — see `ImageOccurrence.operations`.
1220
- */
1221
- embedLinkedImage(imageId, bytes) {
1222
- return JSON.parse(this.wasm.EmbedLinkedImage(
1223
- this.handle,
1224
- imageId,
1225
- imageBytesToBase64(bytes)
1226
- ));
1227
- }
1228
- setImageDimensions(imageId, dimensions) {
1229
- return JSON.parse(this.wasm.SetImageDimensions(
1230
- this.handle,
1231
- imageId,
1232
- JSON.stringify(dimensions)
1233
- ));
1234
- }
1235
- setImageMetadata(imageId, altText, title) {
1236
- return JSON.parse(this.wasm.SetImageMetadata(
1237
- this.handle,
1238
- imageId,
1239
- altText,
1240
- title
1241
- ));
1242
- }
1243
- setImageFloatingLayout(imageId, layout) {
1244
- return JSON.parse(this.wasm.SetImageFloatingLayout(
1245
- this.handle,
1246
- imageId,
1247
- JSON.stringify(layout)
1248
- ));
1249
- }
1250
- removeImage(imageId) {
1251
- return JSON.parse(this.wasm.RemoveImage(this.handle, imageId));
1252
- }
1253
- /** Native Word structured-document tags, in outer-before-inner story order. */
1254
- listContentControls(scopes = 63 /* All */) {
1255
- return JSON.parse(this.wasm.ListContentControls(this.handle, scopes));
1256
- }
1257
- fillContentControlText(anchorId, text, options = {}) {
1258
- return JSON.parse(this.wasm.FillContentControlText(
1259
- this.handle,
1260
- anchorId,
1261
- text,
1262
- JSON.stringify(options)
1263
- ));
1264
- }
1265
- fillContentControlRichText(anchorId, markdown, options = {}) {
1266
- return JSON.parse(this.wasm.FillContentControlRichText(
1267
- this.handle,
1268
- anchorId,
1269
- markdown,
1270
- JSON.stringify(options)
1271
- ));
1272
- }
1273
- setContentControlChecked(anchorId, isChecked, options = {}) {
1274
- return JSON.parse(this.wasm.SetContentControlChecked(
1275
- this.handle,
1276
- anchorId,
1277
- isChecked,
1278
- JSON.stringify(options)
1279
- ));
1280
- }
1281
- setContentControlDate(anchorId, value, displayText, options = {}) {
1282
- const timestamp = value instanceof Date ? value.toISOString() : value;
1283
- return JSON.parse(this.wasm.SetContentControlDate(
1284
- this.handle,
1285
- anchorId,
1286
- timestamp,
1287
- displayText ?? null,
1288
- JSON.stringify(options)
1289
- ));
1290
- }
1291
- selectContentControlItem(anchorId, value, options = {}) {
1292
- return JSON.parse(this.wasm.SelectContentControlItem(
1293
- this.handle,
1294
- anchorId,
1295
- value,
1296
- JSON.stringify(options)
1297
- ));
1298
- }
1299
- fillContentControlPicture(anchorId, bytes, options = {}) {
1300
- return JSON.parse(this.wasm.FillContentControlPicture(
1301
- this.handle,
1302
- anchorId,
1303
- imageBytesToBase64(bytes),
1304
- JSON.stringify(options)
1305
- ));
1306
- }
1307
- addRepeatingSectionItem(sectionAnchorId, afterItemAnchorId, options = {}) {
1308
- return JSON.parse(this.wasm.AddRepeatingSectionItem(
1309
- this.handle,
1310
- sectionAnchorId,
1311
- afterItemAnchorId ?? "",
1312
- JSON.stringify(options)
1313
- ));
1314
- }
1315
- removeRepeatingSectionItem(itemAnchorId) {
1316
- return JSON.parse(this.wasm.RemoveRepeatingSectionItem(
1317
- this.handle,
1318
- itemAnchorId
1319
- ));
1320
- }
1321
- listBookmarks(scopes = 63 /* All */) {
1322
- return JSON.parse(this.wasm.ListBookmarks(this.handle, scopes));
1323
- }
1324
- addBookmark(name, range) {
1325
- return JSON.parse(this.wasm.AddBookmark(
1326
- this.handle,
1327
- name,
1328
- range.startAnchorId,
1329
- range.startOffset,
1330
- range.endAnchorId,
1331
- range.endOffset
1332
- ));
1333
- }
1334
- renameBookmark(name, newName) {
1335
- return JSON.parse(this.wasm.RenameBookmark(this.handle, name, newName));
1336
- }
1337
- moveBookmark(name, range) {
1338
- return JSON.parse(this.wasm.MoveBookmark(
1339
- this.handle,
1340
- name,
1341
- range.startAnchorId,
1342
- range.startOffset,
1343
- range.endAnchorId,
1344
- range.endOffset
1345
- ));
1346
- }
1347
- removeBookmark(name) {
1348
- return JSON.parse(this.wasm.RemoveBookmark(this.handle, name));
1349
- }
1350
- // ─── Tracked revisions (issue #318) ──────────────────────────────────
1351
- /** Markup-native tracked-revision listing, in document order across body, headers,
1352
- * footers, footnotes, and endnotes. Ids are stable while the underlying markup
1353
- * exists and address {@link acceptRevision}/{@link rejectRevision}; authors/dates
1354
- * are the markup's own (no accept/reject re-diff). */
1355
- listRevisions() {
1356
- return JSON.parse(this.wasm.ListRevisions(this.handle));
1357
- }
1358
- /**
1359
- * The explicit repairs the registry offers for entries it refuses to resolve (issues
1360
- * #754–#758): which carriers are defective, whether a unique repair exists, and what it
1361
- * would do. Read-only; listing, accept and reject never repair.
1362
- */
1363
- listRevisionRepairs() {
1364
- if (!this.wasm.ListRevisionRepairs) {
1365
- throw new Error("This WASM bundle predates revision repairs; rebuild docxodus.");
1366
- }
1367
- return JSON.parse(this.wasm.ListRevisionRepairs(this.handle));
1368
- }
1369
- /**
1370
- * Perform requested repairs atomically as one undo step. A kind the registry did not offer,
1371
- * a non-repairable proposal, or a wrap-as-deletion without author and date refuses the whole
1372
- * call (`revision_repair_rejected`) with nothing mutated. Repaired entries get new ids.
1373
- */
1374
- repairRevisions(repairs) {
1375
- if (!this.wasm.RepairRevisions) {
1376
- throw new Error("This WASM bundle predates revision repairs; rebuild docxodus.");
1377
- }
1378
- return JSON.parse(this.wasm.RepairRevisions(this.handle, JSON.stringify(repairs)));
1379
- }
1380
- /** Accept ONE revision by the id {@link listRevisions} reported — insertions keep
1381
- * their content, deletions are carried out, a move materializes at its destination,
1382
- * a format change keeps the new properties. Undoable. */
1383
- acceptRevision(revisionId) {
1384
- return JSON.parse(this.wasm.AcceptRevision(this.handle, revisionId));
1385
- }
1386
- /** Reject ONE revision by id — the inverse of {@link acceptRevision}: insertions are
1387
- * removed, deleted content is restored, a move stays at its source, a format change
1388
- * restores the stored old properties. Undoable. */
1389
- rejectRevision(revisionId) {
1390
- return JSON.parse(this.wasm.RejectRevision(this.handle, revisionId));
1391
- }
1392
- /**
1393
- * Accept every live revision as one undoable session mutation.
1394
- *
1395
- * Fails closed: an unsupported, malformed, or ambiguous registry entry aborts the whole
1396
- * operation (`revisionUnsupported`/`revisionMalformed`/`revisionAmbiguous`) and nothing is
1397
- * mutated. There is no force mode — call {@link listRevisions} and read each entry's
1398
- * `diagnostic` to see what blocks it.
1399
- */
1400
- acceptAllRevisions() {
1401
- return JSON.parse(this.wasm.AcceptAllRevisions(this.handle));
1402
- }
1403
- /** Reject every live revision as one undoable session mutation. Fails closed exactly like
1404
- * {@link acceptAllRevisions}. */
1405
- rejectAllRevisions() {
1406
- return JSON.parse(this.wasm.RejectAllRevisions(this.handle));
1407
- }
1408
- // ─── Tier C: formatting ──────────────────────────────────────────────
1409
- applyFormat(anchorId, span, op) {
1410
- const spanJson = span ? JSON.stringify(span) : "";
1411
- return JSON.parse(this.wasm.ApplyFormat(this.handle, anchorId, spanJson, JSON.stringify(op)));
1412
- }
1413
- /**
1414
- * Convenience: find `substring` in the anchor's flat text and apply `op` to the
1415
- * first occurrence. Eliminates the offset-arithmetic trap from #138 — caller passes
1416
- * the visible text they want formatted, the WASM-side resolves it to a CharSpan.
1417
- */
1418
- applyFormatBySubstring(anchorId, substring, op) {
1419
- return JSON.parse(
1420
- this.wasm.ApplyFormatBySubstring(this.handle, anchorId, substring, JSON.stringify(op))
1421
- );
1422
- }
1423
- /**
1424
- * Convenience: apply `op` to the exact span of a {@link TextMatch} (typically from
1425
- * {@link grep}). The match's `enclosingAnchor.id` + `span` address one specific
1426
- * occurrence even when several identical needles share the same block.
1427
- */
1428
- applyFormatToMatch(match, op) {
1429
- const span = { start: match.span.start, length: match.span.length };
1430
- return this.applyFormat(match.enclosingAnchor.id, span, op);
1431
- }
1432
- setParagraphStyle(anchorId, styleId) {
1433
- return JSON.parse(this.wasm.SetParagraphStyle(this.handle, anchorId, styleId));
1434
- }
1435
- /** Set paragraph alignment / indent / page-break-before (omitted fields are left unchanged). */
1436
- setParagraphFormat(anchorId, op) {
1437
- return JSON.parse(this.wasm.SetParagraphFormat(this.handle, anchorId, JSON.stringify(op)));
1438
- }
1439
- setListLevel(anchorId, levelDelta) {
1440
- return JSON.parse(this.wasm.SetListLevel(this.handle, anchorId, levelDelta));
1441
- }
1442
- removeListMembership(anchorId) {
1443
- return JSON.parse(this.wasm.RemoveListMembership(this.handle, anchorId));
1444
- }
1445
- /** Make the paragraph a bullet/numbered list item, or remove list membership ("none"). */
1446
- applyListFormat(anchorId, kind) {
1447
- return JSON.parse(this.wasm.ApplyListFormat(this.handle, anchorId, kind));
1448
- }
1449
- /** Apply one list format across the contiguous sibling run from `firstAnchorId` to
1450
- * `lastAnchorId` inclusive (either document order). Every member shares one `w:num`
1451
- * instance so the numbering sequence stays intact; the whole range is a single undo step. */
1452
- applyListFormatRange(firstAnchorId, lastAnchorId, kind) {
1453
- return JSON.parse(
1454
- this.wasm.ApplyListFormatRange(this.handle, firstAnchorId, lastAnchorId, kind)
1455
- );
1456
- }
1457
- /** Restart the anchored list item's numbering at `value` — Word's *Set Numbering Value…*.
1458
- * Writes a `w:startOverride` on a dedicated `w:num` instance and repoints the anchored item
1459
- * plus every following member of its sequence, so a mid-list restart splits the sequence
1460
- * exactly like Word (earlier items keep their numbers, the tail continues from `value`). */
1461
- setListStartOverride(anchorId, value) {
1462
- return JSON.parse(this.wasm.SetListStartOverride(this.handle, anchorId, value));
1463
- }
1464
- /** Remove the numbering restart from the anchored item's whole sequence (the inverse of
1465
- * {@link setListStartOverride}); the sequence reverts to the definition's own start. A
1466
- * sequence with no override at the item's level is a successful no-op. */
1467
- clearListStartOverride(anchorId) {
1468
- return JSON.parse(this.wasm.ClearListStartOverride(this.handle, anchorId));
1469
- }
1470
- // ─── Tier D: cell content ────────────────────────────────────────────
1471
- replaceCellContent(cellAnchorId, markdown) {
1472
- return JSON.parse(this.wasm.ReplaceCellContent(this.handle, cellAnchorId, markdown));
1473
- }
1474
- // ─── Search ──────────────────────────────────────────────────────────
1475
- /**
1476
- * Searches the flat text of every paragraph/heading/list-item in scope for
1477
- * matches of `pattern`, returning them in document order with the run
1478
- * fragments each match spans. Lets callers rewrite a match in place while
1479
- * preserving each fragment's formatting (bold/italic/hyperlink/etc.).
1480
- *
1481
- * `pattern` is a regular expression — use plain string equivalents wrapped
1482
- * in `^` / `$` or pass literal text escaped via a helper.
1483
- *
1484
- * @see docs/architecture/docx_mutation_api.md#grep
1485
- */
1486
- grep(pattern, options) {
1487
- return JSON.parse(this.wasm.Grep(this.handle, pattern, options ? JSON.stringify(options) : ""));
1488
- }
1489
- /**
1490
- * Like {@link grep}, but lets a single match span adjacent block-level
1491
- * siblings (paragraphs/headings/list items) under the same parent. Block
1492
- * boundaries appear in the matched text as `\n`, so `^`/`$` with the
1493
- * Multiline flag anchor at boundaries and `.` won't cross unless Singleline
1494
- * is set.
1495
- *
1496
- * Matches never cross OOXML package parts, container boundaries (body →
1497
- * table cell), or non-paragraph siblings (a table between two paragraphs
1498
- * breaks the run). Returned superset of {@link grep}: single-block matches
1499
- * still appear with one slice. Filter `slices.length > 1` for cross-block only.
1500
- *
1501
- * @see docs/architecture/docx_mutation_api.md#grepcrossblock
1502
- */
1503
- grepCrossBlock(pattern, options) {
1504
- return JSON.parse(
1505
- this.wasm.GrepCrossBlock(this.handle, pattern, options ? JSON.stringify(options) : "")
1506
- );
1507
- }
1508
- /**
1509
- * Finds every literal occurrence of `find` in the anchor's flat text and
1510
- * replaces it with `replace`, preserving the surrounding run formatting that
1511
- * the match didn't touch. Returns one `EditResult` per attempted match.
1512
- *
1513
- * A `find` that matches nothing fails with a single `text_not_found` result
1514
- * naming the anchor and the needle — never an empty array. Pass
1515
- * `expectedMatchCount: 0` to assert absence as a successful no-op instead.
1516
- *
1517
- * Run-formatting contract: the replacement text inherits the formatting of
1518
- * the FIRST run the match spanned. Middle/trailing runs keep their `w:rPr`
1519
- * but lose the slice of text the match consumed.
1520
- *
1521
- * @see docs/architecture/docx_mutation_api.md#replacetextrange
1522
- */
1523
- replaceTextRange(anchorId, find, replace, options) {
1524
- return JSON.parse(
1525
- this.wasm.ReplaceTextRange(this.handle, anchorId, find, replace, options ? JSON.stringify(options) : "")
1526
- );
1527
- }
1528
- /**
1529
- * Replaces a specific Grep match in place — addresses the exact span by
1530
- * `enclosingAnchor.id` + `span.{start,length}`, so identical needles in the
1531
- * same paragraph (the template-fill case where five `[___]` placeholders
1532
- * each get a different value) don't collide.
1533
- *
1534
- * Pass `format` to replace text and format exactly the replacement atomically, with one
1535
- * undo/version unit. This interactive path returns an ordinary EditResult and avoids the
1536
- * package checkpoint/hash required by executeBatch's receipt. Empty replacements only delete.
1537
- */
1538
- replaceMatch(match, replace, format) {
1539
- if (format !== void 0) {
1540
- if (!this.wasm.ReplaceTextAtSpanWithFormat) {
1541
- throw new Error("This WASM bundle predates atomic text-plus-format edits; rebuild docxodus.");
1542
- }
1543
- return JSON.parse(this.wasm.ReplaceTextAtSpanWithFormat(
1544
- this.handle,
1545
- match.enclosingAnchor.id,
1546
- match.span.start,
1547
- match.span.length,
1548
- replace,
1549
- JSON.stringify(format)
1550
- ));
1551
- }
1552
- return JSON.parse(
1553
- this.wasm.ReplaceTextAtSpan(this.handle, match.enclosingAnchor.id, match.span.start, match.span.length, replace)
1554
- );
1555
- }
1556
- /**
1557
- * Helper for {@link fillPlaceholders} `coalesceWhitespaceAroundEmptyFill` path —
1558
- * mirrors the .NET `ReplaceMatchCoalescingNeighbors` rules. Inspects the chars
1559
- * immediately surrounding the match via `match.contextBefore` / `contextAfter`
1560
- * (so the option requires `contextChars >= 1`, the default) and expands the
1561
- * deletion span to absorb whitespace / leading-space-before-punctuation /
1562
- * matched-brackets where the patterns match. Falls back to literal-delete
1563
- * when no neighbor pattern applies.
1564
- *
1565
- * Note: with `boundary: ContextBoundary.Bracket`, neighbor brackets are not
1566
- * captured in context, so the bracket-coalesce rule won't fire on the JS side.
1567
- * The .NET implementation reads flat text directly and handles that case;
1568
- * callers who care should leave `boundary` at the default `Char`.
1569
- */
1570
- replaceMatchCoalescingNeighbors(match) {
1571
- const fold = (c) => {
1572
- if (c === "\xA0" || c === "\u202F" || c === "\u2009") return " ";
1573
- return c;
1574
- };
1575
- const l = fold(match.contextBefore.length > 0 ? match.contextBefore[match.contextBefore.length - 1] : void 0);
1576
- const r = fold(match.contextAfter.length > 0 ? match.contextAfter[0] : void 0);
1577
- const isSpace = (c) => c === " " || c === " ";
1578
- const isClauseTerm = (c) => c === "." || c === "," || c === ";" || c === ":" || c === "!" || c === "?";
1579
- const isOpen = (c) => c === "(" || c === "[" || c === "{";
1580
- const isClose = (c) => c === ")" || c === "]" || c === "}";
1581
- let extendLeft = 0;
1582
- let extendRight = 0;
1583
- if (isSpace(l) && isSpace(r)) {
1584
- extendRight = 1;
1585
- } else if (isSpace(l) && isClauseTerm(r)) {
1586
- extendLeft = 1;
1587
- } else if (isOpen(l) && isClose(r)) {
1588
- extendLeft = 1;
1589
- extendRight = 1;
1590
- }
1591
- if (extendLeft === 0 && extendRight === 0) {
1592
- return this.replaceMatch(match, "");
1593
- }
1594
- return JSON.parse(
1595
- this.wasm.ReplaceTextAtSpan(
1596
- this.handle,
1597
- match.enclosingAnchor.id,
1598
- match.span.start - extendLeft,
1599
- match.span.length + extendLeft + extendRight,
1600
- ""
1601
- )
1602
- );
1603
- }
1604
- /**
1605
- * Replace the bracketed portion of a `TextMatch` with `newInner`, preserving any
1606
- * prefix or suffix outside the brackets. Designed for `findPlaceholders` matches
1607
- * like `$[___]` where the regex `\$?\[…\]` captures a leading `$`:
1608
- * `replaceInner(match, "0.20")` yields `$0.20`, not `0.20`.
1609
- *
1610
- * Returns `MalformedMarkdown` if the match text does not contain balanced brackets.
1611
- */
1612
- replaceInner(match, newInner) {
1613
- return JSON.parse(this.wasm.ReplaceInner(
1614
- this.handle,
1615
- match.text,
1616
- match.enclosingAnchor.id,
1617
- match.span.start,
1618
- match.span.length,
1619
- newInner
1620
- ));
1621
- }
1622
- /**
1623
- * Picker-driven template fill. For every placeholder matching `options.kinds`,
1624
- * calls `picker`; if the picker returns a non-null string, the placeholder is
1625
- * replaced (with optional `$`-prefix preservation). Iterates until no more
1626
- * placeholders match (or `maxPasses` is reached, or a pass makes zero changes)
1627
- * — handles nested brackets that surface only after the inner ones are stripped.
1628
- *
1629
- * The TypeScript implementation mirrors the .NET `DocxSession.FillPlaceholders`
1630
- * exactly.
1631
- *
1632
- * The picker is invoked synchronously by this loop on the JS side (it does
1633
- * NOT run inside the WASM module). Async pickers are not supported: returning
1634
- * a `Promise` will cause a `TypeError` at runtime inside the `$`-prefix
1635
- * preservation branch (`Promise.startsWith is not a function`). For async
1636
- * data, pre-build a lookup map before calling and have the picker read from
1637
- * it synchronously.
1638
- */
1639
- fillPlaceholders(picker, options) {
1640
- const opts = options ?? {};
1641
- const kinds = opts.kinds ?? PlaceholderKinds.All;
1642
- const scope = opts.scope ?? 1;
1643
- const maxPasses = opts.maxPasses ?? 8;
1644
- const preserveDollarPrefix = opts.preserveDollarPrefix ?? true;
1645
- const contextChars = opts.contextChars ?? 80;
1646
- const boundary = opts.boundary ?? ContextBoundary.Char;
1647
- const coalesceEmpty = opts.coalesceWhitespaceAroundEmptyFill ?? false;
1648
- if (maxPasses <= 0) {
1649
- throw new RangeError("FillOptions.maxPasses must be > 0");
1650
- }
1651
- let filled = 0;
1652
- let workPasses = 0;
1653
- const errors = [];
1654
- const unfilled = [];
1655
- const seenSkipKeys = /* @__PURE__ */ new Set();
1656
- for (let pass = 1; pass <= maxPasses; pass++) {
1657
- const placeholders = this.findPlaceholders(kinds, scope, contextChars, boundary).sort((a, b) => {
1658
- const cmp = b.match.enclosingAnchor.id.localeCompare(a.match.enclosingAnchor.id);
1659
- if (cmp !== 0) return cmp;
1660
- return b.match.span.start - a.match.span.start;
1661
- });
1662
- if (placeholders.length === 0) break;
1663
- let passChanges = 0;
1664
- for (const p of placeholders) {
1665
- const pick = picker(p);
1666
- if (pick == null) {
1667
- const key = `${p.match.enclosingAnchor.id}:${p.match.span.start}:${p.match.span.length}`;
1668
- if (!seenSkipKeys.has(key)) {
1669
- seenSkipKeys.add(key);
1670
- unfilled.push(p);
1671
- }
1672
- continue;
1673
- }
1674
- let replacement = pick;
1675
- if (preserveDollarPrefix && p.match.text.startsWith("$") && !replacement.startsWith("$")) {
1676
- replacement = "$" + replacement;
1677
- }
1678
- const r = coalesceEmpty && replacement.length === 0 ? this.replaceMatchCoalescingNeighbors(p.match) : this.replaceMatch(p.match, replacement);
1679
- if (r.success) {
1680
- filled++;
1681
- passChanges++;
1682
- } else if (r.error) {
1683
- errors.push(r.error);
1684
- }
1685
- }
1686
- if (passChanges > 0) workPasses = pass;
1687
- if (passChanges === 0) break;
1688
- }
1689
- const stillPresent = this.findPlaceholders(kinds, scope).length;
1690
- return {
1691
- filled,
1692
- skipped: unfilled.length,
1693
- stillPresent,
1694
- passes: workPasses,
1695
- unfilled,
1696
- errors
1697
- };
1698
- }
1699
- /**
1700
- * Enumerate template placeholders in the document. Thin classifier over
1701
- * {@link grep}: distinguishes `[___]` value blanks (`blank_fill`),
1702
- * `[bracketed alternative clauses]` (`alternative_clause`), and
1703
- * `[insert X]` / `[*italic hint*]` instructions (`instruction`).
1704
- *
1705
- * Combine kinds with bitwise OR: `PlaceholderKinds.BlankFill | PlaceholderKinds.Instruction`.
1706
- * Default is `PlaceholderKinds.All`; default scope is body only (1).
1707
- *
1708
- * @see docs/architecture/docx_mutation_api.md#findplaceholders
1709
- */
1710
- findPlaceholders(kinds = PlaceholderKinds.All, scope = 1, contextChars = 80, boundary = ContextBoundary.Char, citation) {
1711
- const json = citation ? this.wasm.FindPlaceholdersWithCitations(
1712
- this.handle,
1713
- kinds,
1714
- scope,
1715
- contextChars,
1716
- boundary,
1717
- JSON.stringify(citation)
1718
- ) : this.wasm.FindPlaceholders(this.handle, kinds, scope, contextChars, boundary);
1719
- return JSON.parse(json);
1720
- }
1721
- /**
1722
- * Returns a snapshot of edit-state introspection signals — placeholder counts,
1723
- * underscore-run leftovers, footnote/comment counts. Useful for "am I done?"
1724
- * verification at the end of an edit pipeline.
1725
- */
1726
- getEditSummary() {
1727
- return JSON.parse(this.wasm.GetEditSummary(this.handle));
1728
- }
1729
- /**
1730
- * Discoverability alias for {@link findPlaceholders}. Same return shape.
1731
- */
1732
- remainingPlaceholders(kinds = PlaceholderKinds.All) {
1733
- return JSON.parse(this.wasm.RemainingPlaceholders(this.handle, kinds));
1734
- }
1735
- getDiff(format = DiffFormat.Json) {
1736
- const raw = this.wasm.GetDiff(this.handle, format);
1737
- if (format === DiffFormat.Json) {
1738
- return JSON.parse(raw);
1739
- }
1740
- return raw;
1741
- }
1742
- /**
1743
- * Return stable semantic changes from the package opened for this session to
1744
- * its current state. Requires `captureInitialProjection` (enabled by default).
1745
- */
1746
- getSemanticChanges() {
1747
- return JSON.parse(this.wasm.GetSemanticChanges(this.handle));
1748
- }
1749
- /**
1750
- * Run the default deliverable gate on this session's normal clean-save checkpoint.
1751
- * With initial projection capture enabled (the default), exact opening bytes are the
1752
- * baseline used for dispositions and semantic/package deltas.
1753
- */
1754
- verifyDeliverable(request) {
1755
- if (request !== void 0) {
1756
- if (!this.wasm.VerifyDeliverableWithRequest) {
1757
- throw new Error("This WASM bundle predates full verification requests; rebuild docxodus.");
1758
- }
1759
- return JSON.parse(this.wasm.VerifyDeliverableWithRequest(
1760
- this.handle,
1761
- serializeVerificationRequest(request)
1762
- ));
1763
- }
1764
- return JSON.parse(
1765
- this.wasm.VerifyDeliverable(this.handle)
1766
- );
1767
- }
1768
- // ─── Annotation-based anchor discovery (#132) ────────────────────────
1769
- /**
1770
- * Resolves an annotation's range to the block-level markdown anchors covering
1771
- * it, in document order. The bridge between Docxodus' read-side annotation API
1772
- * and the write-side session: an agent that wants to edit "the indemnification
1773
- * clause" looks the annotation up by id and gets the anchors it can hand to
1774
- * {@link replaceText} / {@link deleteBlock} / {@link raw}. Returns an empty
1775
- * list when the id is unknown or its bookmark is missing.
1776
- *
1777
- * v1 returns the enclosing block anchors — every paragraph/heading/list-item/
1778
- * cell/row/table whose subtree overlaps the bookmark range. Filter by
1779
- * `kind === "p" | "h" | "li"` when you want only text-bearing blocks.
1780
- *
1781
- * @see docs/architecture/docx_mutation_api.md#findbyannotation
1782
- */
1783
- findByAnnotation(annotationId, citation) {
1784
- const json = citation ? this.wasm.FindByAnnotationWithCitations(this.handle, annotationId, JSON.stringify(citation)) : this.wasm.FindByAnnotation(this.handle, annotationId);
1785
- return JSON.parse(json);
1786
- }
1787
- /**
1788
- * Finds every annotation whose `labelId` matches and resolves each of their
1789
- * ranges. The result is keyed by annotation id so callers can disambiguate
1790
- * when the same label is applied to multiple regions (three "WARRANTY"
1791
- * annotations on different paragraphs become three entries). Annotations
1792
- * whose bookmark resolves to no anchors are omitted from the result.
1793
- */
1794
- findByLabel(labelId, citation) {
1795
- const json = citation ? this.wasm.FindByLabelWithCitations(this.handle, labelId, JSON.stringify(citation)) : this.wasm.FindByLabel(this.handle, labelId);
1796
- return JSON.parse(json);
1797
- }
1798
- /**
1799
- * Resolves any bookmark in the main document part (Docxodus-managed or
1800
- * user-authored) to the block-level anchors covering its range, in document
1801
- * order. Empty when the bookmark name is unknown. Use this for raw bookmark
1802
- * names that didn't come from the annotation system.
1803
- */
1804
- findByBookmark(bookmarkName, citation) {
1805
- const json = citation ? this.wasm.FindByBookmarkWithCitations(this.handle, bookmarkName, JSON.stringify(citation)) : this.wasm.FindByBookmark(this.handle, bookmarkName);
1806
- return JSON.parse(json);
1807
- }
1808
- // ─── Text/kind-based anchor discovery (#171) ─────────────────────────
1809
- /**
1810
- * True when `anchorId` resolves to a live element in the current session.
1811
- * Cheap existence probe — use it to guard an anchor obtained from an earlier
1812
- * projection before handing it to a mutation (anchors can be invalidated by
1813
- * intervening edits; see the anchor lifecycle table in the mutation docs).
1814
- */
1815
- exists(anchorId) {
1816
- return this.wasm.Exists(this.handle, anchorId);
1817
- }
1818
- /**
1819
- * Find the first block-level anchor (in document order) whose flat text
1820
- * contains `needle`, or `null` when nothing matches. `options` tune case /
1821
- * whitespace handling and narrow the search by kind or scope. For all
1822
- * matches use {@link findAllByText}.
1823
- */
1824
- findByText(needle, options) {
1825
- return JSON.parse(
1826
- this.wasm.FindByText(this.handle, needle, options ? JSON.stringify(options) : "")
1827
- );
1828
- }
1829
- /**
1830
- * Like {@link findByText} but returns every matching anchor in document
1831
- * order (empty when nothing matches).
1832
- */
1833
- findAllByText(needle, options) {
1834
- return JSON.parse(
1835
- this.wasm.FindAllByText(this.handle, needle, options ? JSON.stringify(options) : "")
1836
- );
1837
- }
1838
- /**
1839
- * Find every block-level anchor whose flat text matches the regular
1840
- * expression `pattern`, in document order. `regexOptions` uses the numeric
1841
- * layout of .NET `RegexOptions` (e.g. `1` = IgnoreCase); `options` is the
1842
- * same shape as {@link findByText} (its `ignoreCase` composes with the regex
1843
- * flag). Defaults to `regexOptions = 0` (none).
1844
- */
1845
- findByRegex(pattern, regexOptions = 0, options) {
1846
- return JSON.parse(
1847
- this.wasm.FindByRegex(this.handle, pattern, regexOptions, options ? JSON.stringify(options) : "")
1848
- );
1849
- }
1850
- /**
1851
- * Return every anchor of the given `kind` — one of `"p"`, `"h"`, `"li"`,
1852
- * `"tbl"`, `"tr"`, `"tc"`, `"col"`, `"sdt"`, `"sec"`, `"fn"`, `"en"`,
1853
- * `"cmt"`, `"unk"` — in document order. Matching is an exact string comparison;
1854
- * in particular the row and cell tokens are `"tr"` and `"tc"`, never
1855
- * `"row"`/`"cell"`. Unknown strings are not parsed or rejected: they simply
1856
- * return an empty array. `"img"` and `"drw"` likewise return empty because the
1857
- * projection never assigns those reserved kinds; address images through the
1858
- * image surface instead. Reads the projection's anchor
1859
- * index directly — no text scan. Pass `scope` (e.g. `"body"`) to restrict to
1860
- * a single part; omit it to span all scopes.
1861
- */
1862
- findByKind(kind, scope, citation) {
1863
- const json = citation ? this.wasm.FindByKindWithCitations(
1864
- this.handle,
1865
- kind,
1866
- scope ?? "",
1867
- JSON.stringify(citation)
1868
- ) : this.wasm.FindByKind(this.handle, kind, scope ?? "");
1869
- return JSON.parse(json);
1870
- }
1871
- /**
1872
- * Look up a single anchor's preview info — `{ id, kind, scope, textPreview }`.
1873
- * Returns null when the anchor id is unknown.
1874
- *
1875
- * For iterating many anchors at once, prefer reading `textPreview` directly
1876
- * off the {@link MarkdownProjection.anchorIndex} entries (cheaper — no extra
1877
- * WASM round trip), or use {@link getAnchorInfos} for batched lookups.
1878
- */
1879
- getAnchorInfo(anchorId) {
1880
- const raw = this.wasm.GetAnchorInfo(this.handle, anchorId);
1881
- return JSON.parse(raw);
1882
- }
1883
- /**
1884
- * Bulk variant of {@link getAnchorInfo}: takes an array of anchor ids,
1885
- * returns a record where each unknown id maps to `null`.
1886
- */
1887
- getAnchorInfos(anchorIds) {
1888
- const raw = this.wasm.GetAnchorInfos(this.handle, JSON.stringify(anchorIds));
1889
- return JSON.parse(raw);
1890
- }
1891
- /**
1892
- * Resolve block-level metadata (style id+name, outline level, list membership,
1893
- * formatting probe) for an anchor. Returns null when the anchor doesn't exist.
1894
- */
1895
- getBlockMetadata(anchorId) {
1896
- const raw = this.wasm.GetBlockMetadata(this.handle, anchorId);
1897
- return JSON.parse(raw);
1898
- }
1899
- /**
1900
- * Bulk variant of {@link getBlockMetadata}. Unknown ids map to null;
1901
- * duplicates are deduped.
1902
- */
1903
- getBlockMetadatas(anchorIds) {
1904
- const raw = this.wasm.GetBlockMetadatas(this.handle, JSON.stringify(anchorIds));
1905
- return JSON.parse(raw);
1906
- }
1907
- /**
1908
- * Resolve the numbering facts for a list-item paragraph; returns null when
1909
- * the anchor has no w:numPr.
1910
- */
1911
- getListMembership(anchorId) {
1912
- const raw = this.wasm.GetListMembership(this.handle, anchorId);
1913
- return JSON.parse(raw);
1914
- }
1915
- /**
1916
- * Resolve page-layout info for the w:sectPr that governs an anchor.
1917
- * Returns null for anchors outside the body part.
1918
- */
1919
- getSectionInfo(anchorId) {
1920
- const raw = this.wasm.GetSectionInfo(this.handle, anchorId);
1921
- return JSON.parse(raw);
1922
- }
1923
- /** Enumerate the document's explicit style catalog with resolved high-signal properties. */
1924
- listStyles() {
1925
- return JSON.parse(this.wasm.ListStyles(this.handle));
1926
- }
1927
- /** Inspect direct and effective paragraph/run formatting for one paragraph anchor. */
1928
- getFormatting(anchorId) {
1929
- return JSON.parse(this.wasm.GetFormatting(this.handle, anchorId));
1930
- }
1931
- /** Enumerate text-bearing runs as mutation-compatible anchor/span pairs. */
1932
- listInlineSpans(anchorId) {
1933
- return JSON.parse(this.wasm.ListInlineSpans(this.handle, anchorId));
1934
- }
1935
- /**
1936
- * Enumerates every annotation persisted in the document. Lets an agent prime
1937
- * itself with "here are the labeled regions you can target" before committing
1938
- * to a specific id.
1939
- */
1940
- listAnnotations() {
1941
- return JSON.parse(this.wasm.ListAnnotations(this.handle));
1942
- }
1943
- // ─── Annotation write surface ────────────────────────────────────────
1944
- /**
1945
- * Annotate a range inside `anchorId`. When `span` is `null`/`undefined`
1946
- * the annotation wraps every inline run of the block. When
1947
- * `annotation.id` is `undefined`, a 16-char hex id is auto-generated and
1948
- * returned in `EditResult.annotationId`.
1949
- */
1950
- addAnnotation(anchorId, span, annotation) {
1951
- const spanJson = span ? JSON.stringify(span) : "";
1952
- return JSON.parse(
1953
- this.wasm.AddAnnotation(this.handle, anchorId, spanJson, JSON.stringify(annotation))
1954
- );
1955
- }
1956
- removeAnnotation(annotationId) {
1957
- return JSON.parse(this.wasm.SessionRemoveAnnotation(this.handle, annotationId));
1958
- }
1959
- updateAnnotation(annotationId, update) {
1960
- return JSON.parse(
1961
- this.wasm.UpdateAnnotation(this.handle, annotationId, JSON.stringify(update))
1962
- );
1963
- }
1964
- moveAnnotation(annotationId, newAnchorId, newSpan) {
1965
- const spanJson = newSpan ? JSON.stringify(newSpan) : "";
1966
- return JSON.parse(
1967
- this.wasm.MoveAnnotation(this.handle, annotationId, newAnchorId, spanJson)
1968
- );
1969
- }
1970
- // ─── Lifecycle ───────────────────────────────────────────────────────
1971
- /**
1972
- * Switch how subsequent mutations are recorded (issue #304). Session configuration,
1973
- * not a document mutation: not undoable, and already-applied markup is never touched.
1974
- */
1975
- setTrackedChanges(mode) {
1976
- this.wasm.SetTrackedChanges(this.handle, mode);
1977
- }
1978
- /** Author stamped on subsequent tracked-change markup; `null` restores the "docxodus" default. */
1979
- setRevisionAuthor(author) {
1980
- this.wasm.SetRevisionAuthor(this.handle, author ?? "");
1981
- }
1982
- undo() {
1983
- return this.wasm.Undo(this.handle);
1984
- }
1985
- redo() {
1986
- return this.wasm.Redo(this.handle);
1987
- }
1988
- save() {
1989
- return this.wasm.Save(this.handle);
1990
- }
1991
- close() {
1992
- this.retainedPreviewReceipts.clear();
1993
- this.wasm.CloseSession(this.handle);
1994
- }
1995
- // TypeScript 5.2+ disposable protocol
1996
- [Symbol.dispose]() {
1997
- this.close();
1998
- }
1999
- };
2000
- function imageBytesToBase64(bytes) {
2001
- let binary = "";
2002
- for (let offset = 0; offset < bytes.length; offset += 32768) {
2003
- binary += String.fromCharCode(...bytes.subarray(offset, offset + 32768));
2004
- }
2005
- return globalThis.btoa(binary);
2006
- }
2007
- function openDocxSession(bytes, wasmExports, settings) {
2008
- const bridge = wasmExports.DocxSessionBridge;
2009
- const handle = bridge.OpenSession(bytes, settings ? JSON.stringify(settings) : "");
2010
- return new DocxSession(handle, bridge);
2011
- }
2012
- return __toCommonJS(session_exports);
2013
- })();
1
+ "use strict";var DocxodusSession=(()=>{var B=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var _=(d,e)=>{for(var t in e)B(d,t,{get:e[t],enumerable:!0})},L=(d,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of N(e))!W.call(d,r)&&r!==t&&B(d,r,{get:()=>e[r],enumerable:!(n=F(e,r))||n.enumerable});return d};var U=d=>L(B({},"__esModule",{value:!0}),d);var K={};_(K,{ContextBoundary:()=>P,DocxSession:()=>E,PlaceholderKinds:()=>w,openDocxSession:()=>j});function O(d){if(d===null||typeof d!="object"||Array.isArray(d))throw new RangeError("a verification request must be an object");let{companionArtifacts:e,...t}=d,n={...t};return e!==void 0&&(n.companionArtifacts=e.map(V)),JSON.stringify(n)}function V(d){let{bytes:e,...t}=d,n={...t};return e!==void 0&&(n.bytesB64=H(e)),n}function H(d){if(typeof btoa=="function"){let e="";for(let n=0;n<d.length;n+=32768){let r=d.subarray(n,n+32768);e+=String.fromCharCode.apply(null,r)}return btoa(e)}return Buffer.from(d).toString("base64")}var w={BlankFill:1,AlternativeClause:2,Instruction:4,All:7},D={Json:0,Unified:1,SideBySide:2},P={Char:0,Bracket:1,Sentence:2,Comma:3};function T(d,e,t){let n=new Map,r=new Map,i=(h,p)=>{h.forEach((s,S)=>{let g=t(s),v=p.get(g)??[];v.push(S),p.set(g,v)})};i(d,n),i(e,r);let a=d.map(()=>!1),c=e.map(()=>!1),x=e.map(()=>!1);for(let[h,p]of r){let s=n.get(h)??[];for(let R of p){let o=s.find(l=>!a[l]&&JSON.stringify(d[l])===JSON.stringify(e[R]));o!==void 0&&(a[o]=!0,c[R]=!0)}let S=s.filter(R=>!a[R]),g=p.filter(R=>!c[R]),v=Math.min(S.length,g.length);for(let R=0;R<v;R++)a[S[R]]=!0,c[g[R]]=!0,x[g[R]]=!0}return{added:e.filter((h,p)=>!c[p]),removed:d.filter((h,p)=>!a[p]),modified:e.filter((h,p)=>x[p])}}var A={revisionDates:"Tracked-revision date attributes may use the execution clock; compare revision ids, authors, types, text, and anchors across separate executions.",commentDates:"Comment date attributes may be generated from the execution clock; supply dates explicitly when byte-identical replay is required.",annotationMetadata:"Auto-generated annotation ids or creation timestamps are execution metadata; supply id and created explicitly when byte-identical replay is required.",createdAnchors:"Created anchors and related OOXML ids may be generated independently on replay; preview/apply equivalence is semantic and packageHash or anchor ids may differ."},q=new Set(Object.values(A)),G=8,E=class d{constructor(e,t){this.retainedPreviewReceipts=new Map;this.raw={getXml:e=>this.wasm.RawGetXml(this.handle,e),insertXml:(e,t,n)=>JSON.parse(this.wasm.RawInsertXml(this.handle,e,t,n)),replaceXml:(e,t)=>JSON.parse(this.wasm.RawReplaceXml(this.handle,e,t))};this.handle=e,this.wasm=t}project(){return JSON.parse(this.wasm.Project(this.handle))}getVersion(){return JSON.parse(this.wasm.GetVersion(this.handle)).version}getPackageManifest(){return JSON.parse(this.wasm.GetPackageManifest(this.handle))}registerPageMap(e,t){return JSON.parse(this.wasm.RegisterPageMap(this.handle,JSON.stringify(e),t??""))}getPageMapStatus(e){return JSON.parse(this.wasm.GetPageMapStatus(this.handle,e?JSON.stringify(e):""))}getPageCitation(e,t){return JSON.parse(this.wasm.GetPageCitation(this.handle,e,JSON.stringify(t)))}checkPreconditions(e){return JSON.parse(this.wasm.CheckPreconditions(this.handle,JSON.stringify(e)))}runWithPreconditions(e,t){let n=this.checkPreconditions(e);return n.success?t():n}executeBatch(e,t="atomic",n){if(t!=="atomic"&&t!=="best_effort")throw new RangeError(`unknown mutation batch mode: ${String(t)}`);return n===void 0?this.recorded(e,t,void 0,()=>this.runBatch(e,t)):this.runTransactional(n,t,r=>this.recorded(e,t,r,()=>this.runBatch(e,t)))}getDeliveryEvidenceStatus(){if(!this.wasm.GetDeliveryEvidenceStatus)throw new Error("This WASM bundle predates delivery evidence; rebuild docxodus.");return JSON.parse(this.wasm.GetDeliveryEvidenceStatus(this.handle))}buildDeliveryReceipt(e){if(!this.wasm.BuildDeliveryReceipt)throw new Error("This WASM bundle predates delivery evidence; rebuild docxodus.");let t=JSON.parse(this.wasm.BuildDeliveryReceipt(this.handle,JSON.stringify(e??{}))),n=r=>Uint8Array.from(atob(r),i=>i.charCodeAt(0));return{status:t.status,verified:t.verified,manifest:t.manifest,manifestBytes:n(t.manifestBytes),artifacts:t.artifacts.map(r=>{let{bytes:i,...a}=r;return i===void 0?a:{...a,bytes:n(i)}}),...t.evidence?{evidence:t.evidence}:{}}}recorded(e,t,n,r){if(!this.wasm.BeginDeliveryEvidence||!this.wasm.CompleteDeliveryEvidence||!this.wasm.AbandonDeliveryEvidence||!this.wasm.BeginDeliveryEvidence(this.handle,JSON.stringify(e.map(c=>c.args===void 0?{tool:c.tool,action:c.action}:{tool:c.tool,action:c.action,args:c.args})),t,n?JSON.stringify({transactionId:n.transactionId,requestFingerprint:n.requestFingerprint}):""))return r();let a;try{return a=r(),a}finally{a?this.wasm.CompleteDeliveryEvidence(this.handle,JSON.stringify(a.steps)):this.wasm.AbandonDeliveryEvidence(this.handle)}}commitPreview(e,t){if(!this.wasm.CommitPreview)throw new Error("This WASM bundle predates retained previews; rebuild docxodus.");let n=()=>{let r=JSON.parse(this.wasm.CommitPreview(this.handle,e));if(!r.success)return r.failure?.error.code==="preview_not_found"&&this.retainedPreviewReceipts.delete(e),r;let i=this.retainedPreviewReceipts.get(e);return this.retainedPreviewReceipts.delete(e),i?{...i,preview:!1,html:null,baseVersion:r.baseVersion,resultVersion:r.resultVersion,packageHash:r.packageHash,warnings:i.warnings.filter(a=>!q.has(a)),retention:r.retention}:r};return t===void 0?n():this.runTransactional(t,"atomic",()=>n())}runTransactional(e,t,n){if(!this.wasm.BeginMutationTransaction||!this.wasm.CompleteMutationTransaction||!this.wasm.AbandonMutationTransaction)throw new Error("This WASM bundle predates mutation transactions; rebuild docxodus.");if(typeof e.transactionId!="string")throw new RangeError("transactionId must be a string");if(e.request===null||typeof e.request!="object"||Array.isArray(e.request))throw new RangeError("a mutation transaction needs a request object describing the batch");let r=JSON.parse(this.wasm.BeginMutationTransaction(this.handle,e.transactionId,JSON.stringify({...e.request,mode:t})));if(r.response!==null)return JSON.parse(r.response);let i=!1;try{let a={...n(r.transaction),transaction:r.transaction};return this.wasm.CompleteMutationTransaction(this.handle,e.transactionId,JSON.stringify(a)),i=!0,a}finally{i||this.wasm.AbandonMutationTransaction(this.handle,e.transactionId)}}runBatch(e,t){let n=this.getVersion(),r=[],i=(o,l,y)=>{try{return l()}catch(b){return r.push(`${o} unavailable: ${b instanceof Error?b.message:String(b)}`),y}},a=i("Revision delta inspection",()=>this.listRevisions(),[]),c=i("Comment delta inspection",()=>this.listComments(),[]),x=i("Annotation delta inspection",()=>this.listAnnotations(),[]),h=o=>{try{let l=T(a,i("Revision delta inspection",()=>this.listRevisions(),a),m=>m.id),y=T(c,i("Comment delta inspection",()=>this.listComments(),c),m=>m.anchorId),b=T(x,i("Annotation delta inspection",()=>this.listAnnotations(),x),m=>m.id??""),u=i("Result version inspection",()=>this.getVersion(),n),f=[...r];[...l.added,...l.modified].some(m=>m.date!==void 0&&m.date!==null)&&f.push(A.revisionDates),[...y.added,...y.modified].some(m=>m.date!==void 0&&m.date!==null)&&f.push(A.commentDates),b.added.some(m=>m.created!==void 0&&m.created!==null)&&f.push(A.annotationMetadata),o.steps.some(m=>m.results.some(k=>k.created.length>0))&&f.push(A.createdAnchors),t==="best_effort"&&!o.success&&f.push("Best-effort execution retains every successful step despite later failures.");let C=null;if(!this.wasm.GetPackageContentHash)f.push("This WASM bundle predates package equivalence hashes; packageHash is unavailable.");else try{C=this.wasm.GetPackageContentHash(this.handle)}catch(m){f.push(`Package equivalence hash unavailable: ${m instanceof Error?m.message:String(m)}`)}return{...o,preview:!1,baseVersion:n,resultVersion:u,packageHash:C,revisionChanges:l,commentChanges:y,annotationChanges:b,warnings:f,html:null}}catch(l){return{...o,preview:!1,baseVersion:n,resultVersion:i("Result version inspection",()=>this.getVersion(),n),packageHash:null,revisionChanges:{added:[],removed:[],modified:[]},commentChanges:{added:[],removed:[],modified:[]},annotationChanges:{added:[],removed:[],modified:[]},warnings:[...r,`Batch receipt enrichment unavailable: ${l instanceof Error?l.message:String(l)}`],html:null}}},p=o=>({success:!1,error:{code:"internal_error",message:o instanceof Error?o.message:String(o)},created:[],removed:[],modified:[]}),s=o=>{try{let l=o.mutation(),y=Array.isArray(l)?l:[l];return y.some(b=>b===null||typeof b!="object"||typeof b.success!="boolean")?[p("batch mutation returned invalid edit results")]:y}catch(l){return[p(l)]}},S=(o,l)=>({index:o.index,tool:o.tool,action:o.action,error:o.results.find(y=>!y.success)?.error??{code:"internal_error",message:"batch step failed without an error"},rolledBack:l}),g=o=>{try{return o.preflight?.()}catch(l){return p(l).error}};if(t==="atomic"){let o=e.map(g),l=o.findIndex(u=>u!==void 0);if(l>=0){let u=e[l],f={index:l,tool:u.tool,action:u.action,success:!1,rolledBack:!0,results:[{success:!1,error:o[l],created:[],removed:[],modified:[]}]};return h({mode:t,status:"failed",success:!1,rolledBack:!0,steps:[f],failure:S(f,!0)})}let y=this.wasm.BeginTransaction(this.handle),b=[];try{for(let u=0;u<e.length;u++){let f=e[u],C=s(f),m={index:u,tool:f.tool,action:f.action,success:C.every(k=>k.success),rolledBack:!1,results:C};if(b.push(m),!m.success){this.wasm.RollbackTransaction(y);let k=b.map(J=>({...J,rolledBack:!0})),M=k[k.length-1];return h({mode:t,status:"failed",success:!1,rolledBack:!0,steps:k,failure:S(M,!0)})}}return this.wasm.CommitTransaction(y),h({mode:t,status:"ok",success:!0,rolledBack:!1,steps:b})}catch(u){try{this.wasm.RollbackTransaction(y)}catch{}throw u}}let v=e.map((o,l)=>{let y=g(o),b=y?[{success:!1,error:y,created:[],removed:[],modified:[]}]:s(o);return{index:l,tool:o.tool,action:o.action,success:b.every(u=>u.success),rolledBack:!1,results:b}}),R=v.find(o=>!o.success);return h({mode:t,status:R?v.some(o=>o.success)?"partial":"failed":"ok",success:R===void 0,rolledBack:!1,steps:v,failure:R?S(R,!1):void 0})}previewBatch(e,t="atomic",n){if(t!=="atomic"&&t!=="best_effort")throw new RangeError(`unknown mutation batch mode: ${String(t)}`);let r=n?.html??"none";if(r!=="none"&&r!=="scoped"&&r!=="full")throw new RangeError(`unknown preview HTML mode: ${String(r)}`);if(!this.wasm.OpenPreviewSession)throw new Error("This WASM bundle does not support isolated mutation previews.");let i=new d(this.wasm.OpenPreviewSession(this.handle),this.wasm);try{let a=i.executeBatch(e.map(g=>({tool:g.tool,action:g.action,mutation:()=>g.mutation(i),preflight:g.preflight?()=>g.preflight(i):void 0})),t),c=[...a.warnings],x=null,h=g=>{if(g.trimStart().startsWith("{")){let v=JSON.parse(g);if(v.error)return c.push(`Preview HTML could not be generated: ${v.error}`),null}return g},p="This WASM bundle predates the shared preview HTML profile; preview HTML omits comments, annotations and headers/footers and may differ from other surfaces.";try{r==="scoped"?n?.htmlAnchorId?this.wasm.RenderPreviewBlockHtml?x=h(this.wasm.RenderPreviewBlockHtml(i.handle,n.htmlAnchorId)):(c.push(p),x=i.renderBlock(n.htmlAnchorId)):c.push("Scoped HTML was requested without htmlAnchorId; no HTML was generated."):r==="full"&&(this.wasm.RenderPreviewHtml?x=h(this.wasm.RenderPreviewHtml(i.handle)):(c.push(p),x=h(this.wasm.RenderHtmlForReview?this.wasm.RenderHtmlForReview(i.handle,"docx-",!1,!1,1,!0):this.wasm.RenderHtml(i.handle,"docx-",!1,!1,1))))}catch(g){c.push(`Preview HTML could not be generated: ${g instanceof Error?g.message:String(g)}`)}let s;if(n?.retain){if(!this.wasm.RetainPreview)throw new Error("This WASM bundle predates retained previews; rebuild docxodus.");if(!a.success)c.push("The preview did not succeed, so it was not retained for commit.");else{let g=JSON.parse(this.wasm.RetainPreview(i.handle));c.push(...g.warnings),s=g.retention??void 0}}let S=s?{...a,preview:!0,warnings:c,html:x,retention:s}:{...a,preview:!0,warnings:c,html:x};return s&&(this.retainedPreviewReceipts.size>=G&&this.retainedPreviewReceipts.delete(this.retainedPreviewReceipts.keys().next().value),this.retainedPreviewReceipts.set(s.previewId,{...S,html:null})),S}finally{i.close()}}projectAnchor(e,t,n){return JSON.parse(n?this.wasm.ProjectAnchorWithCitations(this.handle,e,t??null,JSON.stringify(n)):this.wasm.ProjectAnchor(this.handle,e,t??null))}renderBlock(e,t){let n=this.wasm.RenderBlockHtml(this.handle,e,t?.cssPrefix??null,t?.fabricateClasses??null);if(n.charCodeAt(0)===123){let r=JSON.parse(n);throw new Error(`renderBlock failed: ${r.error??"unknown error"}`)}return n}replaceText(e,t,n){let r=()=>JSON.parse(this.wasm.ReplaceText(this.handle,e,t));return n?this.runWithPreconditions({...n,anchorId:n.anchorId??e},r):r()}deleteBlock(e,t){let n=()=>JSON.parse(this.wasm.DeleteBlock(this.handle,e));return t?this.runWithPreconditions({...t,anchorId:t.anchorId??e},n):n()}moveBlock(e,t,n){return JSON.parse(this.wasm.MoveBlock(this.handle,e,t,n))}deleteRange(e,t){return JSON.parse(this.wasm.DeleteRange(this.handle,e,t))}deleteSection(e){return JSON.parse(this.wasm.DeleteSection(this.handle,e))}insertParagraph(e,t,n){return JSON.parse(this.wasm.InsertParagraph(this.handle,e,t,n))}splitParagraph(e,t){return JSON.parse(this.wasm.SplitParagraph(this.handle,e,t))}mergeParagraphs(e,t){return JSON.parse(this.wasm.MergeParagraphs(this.handle,e,t))}insertHorizontalRule(e,t,n){let r=n?JSON.stringify(n):"";return JSON.parse(this.wasm.InsertHorizontalRule(this.handle,e,t,r))}insertTable(e,t,n,r,i){let a=i?JSON.stringify(i):"";return JSON.parse(this.wasm.InsertTable(this.handle,e,t,n,r,a))}getTableMetadata(e){return JSON.parse(this.wasm.GetTableMetadata(this.handle,e))}resolveTableCellAnchor(e){return JSON.parse(this.wasm.ResolveTableCellAnchor(this.handle,e))}resolveTableCellCoordinate(e,t,n){return JSON.parse(this.wasm.ResolveTableCellCoordinate(this.handle,e,t,n))}insertTableRow(e,t){return JSON.parse(this.wasm.InsertTableRow(this.handle,e,t))}insertTableColumn(e,t){return JSON.parse(this.wasm.InsertTableColumn(this.handle,e,t))}deleteTableRow(e){return JSON.parse(this.wasm.DeleteTableRow(this.handle,e))}deleteTableColumn(e){return JSON.parse(this.wasm.DeleteTableColumn(this.handle,e))}mergeCells(e,t,n,r="append"){return JSON.parse(this.wasm.MergeCells(this.handle,e,t,n,r))}unmergeCells(e){return JSON.parse(this.wasm.UnmergeCells(this.handle,e))}setColumnWidths(e,t){return JSON.parse(this.wasm.SetColumnWidths(this.handle,e,JSON.stringify(t)))}setTableBorders(e,t){return JSON.parse(this.wasm.SetTableBorders(this.handle,e,t?JSON.stringify(t):""))}setCellShading(e,t,n){return JSON.parse(this.wasm.SetCellShading(this.handle,e,t??"",n??""))}setRepeatHeaderRow(e,t){return JSON.parse(this.wasm.SetRepeatHeaderRow(this.handle,e,t??null))}setTableRowOptions(e,t){return JSON.parse(this.wasm.SetTableRowOptions(this.handle,e,t.repeatHeader??null,t.allowBreakAcrossPages??null,t.heightTwips??null,t.heightRule??"atLeast"))}setHeaderText(e,t,n){return JSON.parse(this.wasm.SetHeaderText(this.handle,e,t,n))}setFooterText(e,t,n){return JSON.parse(this.wasm.SetFooterText(this.handle,e,t,n))}insertPageNumberField(e,t,n){return JSON.parse(this.wasm.InsertPageNumberField(this.handle,e,t??"",n??""))}setPageNumbering(e,t){return JSON.parse(this.wasm.SetPageNumbering(this.handle,e,JSON.stringify(t)))}insertTableOfContents(e,t,n){return JSON.parse(this.wasm.InsertTableOfContents(this.handle,e,t??"",n?JSON.stringify(n):""))}insertTableOfFigures(e,t,n){return JSON.parse(this.wasm.InsertTableOfFigures(this.handle,e,t??"",n?JSON.stringify(n):""))}insertTableOfAuthorities(e,t,n){return JSON.parse(this.wasm.InsertTableOfAuthorities(this.handle,e,t??"",n?JSON.stringify(n):""))}clearPageNumbering(e){return JSON.parse(this.wasm.ClearPageNumbering(this.handle,e))}ensureHeaderFooterVisible(e,t){return JSON.parse(this.wasm.EnsureHeaderFooterVisible(this.handle,e,t))}setHeaderFooterKindEnabled(e,t,n){if(!this.wasm.SetHeaderFooterKindEnabled)throw new Error("SetHeaderFooterKindEnabled is not available in this WASM build");return JSON.parse(this.wasm.SetHeaderFooterKindEnabled(this.handle,e,t,n))}setPageSetup(e,t){if(!this.wasm.SetPageSetup)throw new Error("SetPageSetup is not available in this WASM build");return JSON.parse(this.wasm.SetPageSetup(this.handle,e,JSON.stringify(t)))}insertFootnote(e,t,n){return JSON.parse(this.wasm.InsertFootnote(this.handle,e,t,n))}insertCrossReference(e,t,n,r){return JSON.parse(this.wasm.InsertCrossReference(this.handle,e,t,n,r?JSON.stringify(r):""))}listNotes(e){if(!this.wasm.ListNotes)throw new Error("ListNotes is not available in this WASM build");return JSON.parse(this.wasm.ListNotes(this.handle,e))}insertEndnote(e,t,n){return JSON.parse(this.wasm.InsertEndnote(this.handle,e,t,n))}addComment(e,t,n,r,i){let a=t?JSON.stringify(t):"";return JSON.parse(this.wasm.AddComment(this.handle,e,a,n,i?.initials??"",i?.date??"",r))}addCommentToRevision(e,t,n,r){return JSON.parse(this.wasm.AddCommentToRevision(this.handle,e,t,r?.initials??"",r?.date??"",n))}addCommentReply(e,t,n,r){return JSON.parse(this.wasm.AddCommentReply(this.handle,e,t,r?.initials??"",r?.date??"",n))}updateComment(e,t){return JSON.parse(this.wasm.UpdateComment(this.handle,e,t))}setCommentResolved(e,t){return JSON.parse(this.wasm.SetCommentResolved(this.handle,e,t))}removeComment(e){return JSON.parse(this.wasm.RemoveComment(this.handle,e))}listComments(){return JSON.parse(this.wasm.ListComments(this.handle))}listHyperlinks(e=63){return JSON.parse(this.wasm.ListHyperlinks(this.handle,e))}addHyperlink(e,t,n,r){return JSON.parse(this.wasm.AddHyperlink(this.handle,e,t.start,t.length,n,r))}updateHyperlink(e,t,n){return JSON.parse(this.wasm.UpdateHyperlink(this.handle,e,t,n))}removeHyperlink(e){return JSON.parse(this.wasm.RemoveHyperlink(this.handle,e))}getImageCapabilities(){return JSON.parse(this.wasm.GetImageCapabilities())}listImages(e=63){return JSON.parse(this.wasm.ListImages(this.handle,e))}insertImage(e,t,n,r={}){return JSON.parse(this.wasm.InsertImage(this.handle,e,t,I(n),JSON.stringify(r)))}replaceImage(e,t){return JSON.parse(this.wasm.ReplaceImage(this.handle,e,I(t)))}embedLinkedImage(e,t){return JSON.parse(this.wasm.EmbedLinkedImage(this.handle,e,I(t)))}setImageDimensions(e,t){return JSON.parse(this.wasm.SetImageDimensions(this.handle,e,JSON.stringify(t)))}setImageMetadata(e,t,n){return JSON.parse(this.wasm.SetImageMetadata(this.handle,e,t,n))}setImageFloatingLayout(e,t){return JSON.parse(this.wasm.SetImageFloatingLayout(this.handle,e,JSON.stringify(t)))}removeImage(e){return JSON.parse(this.wasm.RemoveImage(this.handle,e))}listContentControls(e=63){return JSON.parse(this.wasm.ListContentControls(this.handle,e))}fillContentControlText(e,t,n={}){return JSON.parse(this.wasm.FillContentControlText(this.handle,e,t,JSON.stringify(n)))}fillContentControlRichText(e,t,n={}){return JSON.parse(this.wasm.FillContentControlRichText(this.handle,e,t,JSON.stringify(n)))}setContentControlChecked(e,t,n={}){return JSON.parse(this.wasm.SetContentControlChecked(this.handle,e,t,JSON.stringify(n)))}setContentControlDate(e,t,n,r={}){let i=t instanceof Date?t.toISOString():t;return JSON.parse(this.wasm.SetContentControlDate(this.handle,e,i,n??null,JSON.stringify(r)))}selectContentControlItem(e,t,n={}){return JSON.parse(this.wasm.SelectContentControlItem(this.handle,e,t,JSON.stringify(n)))}fillContentControlPicture(e,t,n={}){return JSON.parse(this.wasm.FillContentControlPicture(this.handle,e,I(t),JSON.stringify(n)))}addRepeatingSectionItem(e,t,n={}){return JSON.parse(this.wasm.AddRepeatingSectionItem(this.handle,e,t??"",JSON.stringify(n)))}removeRepeatingSectionItem(e){return JSON.parse(this.wasm.RemoveRepeatingSectionItem(this.handle,e))}listBookmarks(e=63){return JSON.parse(this.wasm.ListBookmarks(this.handle,e))}addBookmark(e,t){return JSON.parse(this.wasm.AddBookmark(this.handle,e,t.startAnchorId,t.startOffset,t.endAnchorId,t.endOffset))}renameBookmark(e,t){return JSON.parse(this.wasm.RenameBookmark(this.handle,e,t))}moveBookmark(e,t){return JSON.parse(this.wasm.MoveBookmark(this.handle,e,t.startAnchorId,t.startOffset,t.endAnchorId,t.endOffset))}removeBookmark(e){return JSON.parse(this.wasm.RemoveBookmark(this.handle,e))}listRevisions(e){if(e&&Object.values(e).some(t=>t!=null)){if(!this.wasm.ListRevisionsFiltered)throw new Error("This WASM bundle predates filtered revision listing; rebuild docxodus.");return JSON.parse(this.wasm.ListRevisionsFiltered(this.handle,JSON.stringify(e)))}return JSON.parse(this.wasm.ListRevisions(this.handle))}listRevisionRepairs(){if(!this.wasm.ListRevisionRepairs)throw new Error("This WASM bundle predates revision repairs; rebuild docxodus.");return JSON.parse(this.wasm.ListRevisionRepairs(this.handle))}repairRevisions(e){if(!this.wasm.RepairRevisions)throw new Error("This WASM bundle predates revision repairs; rebuild docxodus.");return JSON.parse(this.wasm.RepairRevisions(this.handle,JSON.stringify(e)))}acceptRevision(e){return JSON.parse(this.wasm.AcceptRevision(this.handle,e))}rejectRevision(e){return JSON.parse(this.wasm.RejectRevision(this.handle,e))}acceptAllRevisions(){return JSON.parse(this.wasm.AcceptAllRevisions(this.handle))}rejectAllRevisions(){return JSON.parse(this.wasm.RejectAllRevisions(this.handle))}applyFormat(e,t,n){let r=t?JSON.stringify(t):"";return JSON.parse(this.wasm.ApplyFormat(this.handle,e,r,JSON.stringify(n)))}applyFormatBySubstring(e,t,n){return JSON.parse(this.wasm.ApplyFormatBySubstring(this.handle,e,t,JSON.stringify(n)))}applyFormatToMatch(e,t){let n={start:e.span.start,length:e.span.length};return this.applyFormat(e.enclosingAnchor.id,n,t)}setParagraphStyle(e,t){return JSON.parse(this.wasm.SetParagraphStyle(this.handle,e,t))}setParagraphFormat(e,t){return JSON.parse(this.wasm.SetParagraphFormat(this.handle,e,JSON.stringify(t)))}setListLevel(e,t){return JSON.parse(this.wasm.SetListLevel(this.handle,e,t))}removeListMembership(e){return JSON.parse(this.wasm.RemoveListMembership(this.handle,e))}applyListFormat(e,t){return JSON.parse(this.wasm.ApplyListFormat(this.handle,e,t))}applyListFormatRange(e,t,n){return JSON.parse(this.wasm.ApplyListFormatRange(this.handle,e,t,n))}setListStartOverride(e,t){return JSON.parse(this.wasm.SetListStartOverride(this.handle,e,t))}clearListStartOverride(e){return JSON.parse(this.wasm.ClearListStartOverride(this.handle,e))}replaceCellContent(e,t){return JSON.parse(this.wasm.ReplaceCellContent(this.handle,e,t))}grep(e,t){return JSON.parse(this.wasm.Grep(this.handle,e,t?JSON.stringify(t):""))}grepCrossBlock(e,t){return JSON.parse(this.wasm.GrepCrossBlock(this.handle,e,t?JSON.stringify(t):""))}replaceTextRange(e,t,n,r){return JSON.parse(this.wasm.ReplaceTextRange(this.handle,e,t,n,r?JSON.stringify(r):""))}replaceMatch(e,t,n){if(n!==void 0){if(!this.wasm.ReplaceTextAtSpanWithFormat)throw new Error("This WASM bundle predates atomic text-plus-format edits; rebuild docxodus.");return JSON.parse(this.wasm.ReplaceTextAtSpanWithFormat(this.handle,e.enclosingAnchor.id,e.span.start,e.span.length,t,JSON.stringify(n)))}return JSON.parse(this.wasm.ReplaceTextAtSpan(this.handle,e.enclosingAnchor.id,e.span.start,e.span.length,t))}replaceMatchCoalescingNeighbors(e){let t=s=>s==="\xA0"||s==="\u202F"||s==="\u2009"?" ":s,n=t(e.contextBefore.length>0?e.contextBefore[e.contextBefore.length-1]:void 0),r=t(e.contextAfter.length>0?e.contextAfter[0]:void 0),i=s=>s===" "||s===" ",a=s=>s==="."||s===","||s===";"||s===":"||s==="!"||s==="?",c=s=>s==="("||s==="["||s==="{",x=s=>s===")"||s==="]"||s==="}",h=0,p=0;return i(n)&&i(r)?p=1:i(n)&&a(r)?h=1:c(n)&&x(r)&&(h=1,p=1),h===0&&p===0?this.replaceMatch(e,""):JSON.parse(this.wasm.ReplaceTextAtSpan(this.handle,e.enclosingAnchor.id,e.span.start-h,e.span.length+h+p,""))}replaceInner(e,t){return JSON.parse(this.wasm.ReplaceInner(this.handle,e.text,e.enclosingAnchor.id,e.span.start,e.span.length,t))}fillPlaceholders(e,t){let n=t??{},r=n.kinds??w.All,i=n.scope??1,a=n.maxPasses??8,c=n.preserveDollarPrefix??!0,x=n.contextChars,h=n.boundary??P.Char,p=n.coalesceWhitespaceAroundEmptyFill??!1;if(a<=0)throw new RangeError("FillOptions.maxPasses must be > 0");let s=0,S=0,g=[],v=[],R=new Set;for(let l=1;l<=a;l++){let y=this.findPlaceholders(r,i,x,h).sort((u,f)=>{let C=f.match.enclosingAnchor.id.localeCompare(u.match.enclosingAnchor.id);return C!==0?C:f.match.span.start-u.match.span.start});if(y.length===0)break;let b=0;for(let u of y){let f=e(u);if(f==null){let k=`${u.match.enclosingAnchor.id}:${u.match.span.start}:${u.match.span.length}`;R.has(k)||(R.add(k),v.push(u));continue}let C=f;c&&u.match.text.startsWith("$")&&!C.startsWith("$")&&(C="$"+C);let m=p&&C.length===0?this.replaceMatchCoalescingNeighbors(u.match):this.replaceMatch(u.match,C);m.success?(s++,b++):m.error&&g.push(m.error)}if(b>0&&(S=l),b===0)break}let o=this.findPlaceholders(r,i).length;return{filled:s,skipped:v.length,stillPresent:o,passes:S,unfilled:v,errors:g}}findPlaceholders(e,t,n,r,i){let a=i?this.wasm.FindPlaceholdersWithCitations(this.handle,e??null,t??null,n??null,r??null,JSON.stringify(i)):this.wasm.FindPlaceholders(this.handle,e??null,t??null,n??null,r??null);return JSON.parse(a)}getEditSummary(){return JSON.parse(this.wasm.GetEditSummary(this.handle))}remainingPlaceholders(e){return JSON.parse(this.wasm.RemainingPlaceholders(this.handle,e??null))}getDiff(e){let t=this.wasm.GetDiff(this.handle,e??null);return e==null||e===D.Json?JSON.parse(t):t}getSemanticChanges(){return JSON.parse(this.wasm.GetSemanticChanges(this.handle))}verifyDeliverable(e){if(e!==void 0){if(!this.wasm.VerifyDeliverableWithRequest)throw new Error("This WASM bundle predates full verification requests; rebuild docxodus.");return JSON.parse(this.wasm.VerifyDeliverableWithRequest(this.handle,O(e)))}return JSON.parse(this.wasm.VerifyDeliverable(this.handle))}findByAnnotation(e,t){let n=t?this.wasm.FindByAnnotationWithCitations(this.handle,e,JSON.stringify(t)):this.wasm.FindByAnnotation(this.handle,e);return JSON.parse(n)}findByLabel(e,t){let n=t?this.wasm.FindByLabelWithCitations(this.handle,e,JSON.stringify(t)):this.wasm.FindByLabel(this.handle,e);return JSON.parse(n)}findByBookmark(e,t){let n=t?this.wasm.FindByBookmarkWithCitations(this.handle,e,JSON.stringify(t)):this.wasm.FindByBookmark(this.handle,e);return JSON.parse(n)}exists(e){return this.wasm.Exists(this.handle,e)}findByText(e,t){return JSON.parse(this.wasm.FindByText(this.handle,e,t?JSON.stringify(t):""))}findAllByText(e,t){return JSON.parse(this.wasm.FindAllByText(this.handle,e,t?JSON.stringify(t):""))}findByRegex(e,t,n){return JSON.parse(this.wasm.FindByRegex(this.handle,e,t??null,n?JSON.stringify(n):""))}findByKind(e,t,n){let r=n?this.wasm.FindByKindWithCitations(this.handle,e,t??"",JSON.stringify(n)):this.wasm.FindByKind(this.handle,e,t??"");return JSON.parse(r)}getAnchorInfo(e){let t=this.wasm.GetAnchorInfo(this.handle,e);return JSON.parse(t)}getAnchorInfos(e){let t=this.wasm.GetAnchorInfos(this.handle,JSON.stringify(e));return JSON.parse(t)}getBlockMetadata(e){let t=this.wasm.GetBlockMetadata(this.handle,e);return JSON.parse(t)}getBlockMetadatas(e){let t=this.wasm.GetBlockMetadatas(this.handle,JSON.stringify(e));return JSON.parse(t)}getListMembership(e){let t=this.wasm.GetListMembership(this.handle,e);return JSON.parse(t)}getSectionInfo(e){let t=this.wasm.GetSectionInfo(this.handle,e);return JSON.parse(t)}listStyles(){return JSON.parse(this.wasm.ListStyles(this.handle))}getFormatting(e){return JSON.parse(this.wasm.GetFormatting(this.handle,e))}listInlineSpans(e){return JSON.parse(this.wasm.ListInlineSpans(this.handle,e))}listAnnotations(){return JSON.parse(this.wasm.ListAnnotations(this.handle))}addAnnotation(e,t,n){let r=t?JSON.stringify(t):"";return JSON.parse(this.wasm.AddAnnotation(this.handle,e,r,JSON.stringify(n)))}removeAnnotation(e){return JSON.parse(this.wasm.SessionRemoveAnnotation(this.handle,e))}updateAnnotation(e,t){return JSON.parse(this.wasm.UpdateAnnotation(this.handle,e,JSON.stringify(t)))}moveAnnotation(e,t,n){let r=n?JSON.stringify(n):"";return JSON.parse(this.wasm.MoveAnnotation(this.handle,e,t,r))}setTrackedChanges(e){this.wasm.SetTrackedChanges(this.handle,e)}setRevisionAuthor(e){this.wasm.SetRevisionAuthor(this.handle,e??"")}undo(){return this.wasm.Undo(this.handle)}redo(){return this.wasm.Redo(this.handle)}save(){return this.wasm.Save(this.handle)}close(){this.retainedPreviewReceipts.clear(),this.wasm.CloseSession(this.handle)}[Symbol.dispose](){this.close()}};function I(d){let e="";for(let t=0;t<d.length;t+=32768)e+=String.fromCharCode(...d.subarray(t,t+32768));return globalThis.btoa(e)}function j(d,e,t){let n=e.DocxSessionBridge,r=n.OpenSession(d,t?JSON.stringify(t):"");return new E(r,n)}return U(K);})();
2
+ //# sourceMappingURL=session.bundle.js.map