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.
- package/README.md +8 -5
- package/dist/canonical.d.ts.map +1 -1
- package/dist/canonical.js +7 -3
- package/dist/canonical.js.map +1 -1
- package/dist/conversion-options.d.ts +16 -0
- package/dist/conversion-options.d.ts.map +1 -0
- package/dist/conversion-options.js +31 -0
- package/dist/conversion-options.js.map +1 -0
- package/dist/core.d.ts +17 -9
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +23 -24
- package/dist/core.js.map +1 -1
- package/dist/docxodus.worker.js +2 -1073
- package/dist/docxodus.worker.js.map +7 -1
- package/dist/editor.bundle.js +35 -14296
- package/dist/editor.bundle.js.map +7 -0
- package/dist/editor.d.ts +33 -2
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +6739 -4773
- package/dist/editor.js.map +7 -1
- package/dist/embed.bundle.js +159 -18703
- package/dist/embed.bundle.js.map +7 -0
- package/dist/embed.d.ts +1 -1
- package/dist/embed.iife.js +37 -18595
- package/dist/embed.iife.js.map +7 -0
- package/dist/embed.js +1 -1
- package/dist/export-assets.json +27 -27
- package/dist/export-browser.bundle.js +1388 -477
- package/dist/export-browser.d.ts +20 -0
- package/dist/export-browser.d.ts.map +1 -1
- package/dist/export-browser.js +132 -37
- package/dist/export-browser.js.map +1 -1
- package/dist/font-runtime.d.ts.map +1 -1
- package/dist/font-runtime.js +54 -0
- package/dist/font-runtime.js.map +1 -1
- package/dist/line-metrics.d.ts +60 -0
- package/dist/line-metrics.d.ts.map +1 -0
- package/dist/line-metrics.js +343 -0
- package/dist/line-metrics.js.map +1 -0
- package/dist/pagination.bundle.js +3 -3214
- package/dist/pagination.bundle.js.map +7 -0
- package/dist/pagination.d.ts +86 -9
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +358 -55
- package/dist/pagination.js.map +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +63 -12
- package/dist/react.js.map +1 -1
- package/dist/render-report-v2.schema.json +29 -0
- package/dist/session.bundle.js +2 -2013
- package/dist/session.bundle.js.map +7 -0
- package/dist/session.d.ts +14 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +62 -29
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +137 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -1
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Memory.wasm +0 -0
- package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +11 -11
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
- package/dist/word-text.d.ts +14 -0
- package/dist/word-text.d.ts.map +1 -0
- package/dist/word-text.js +377 -0
- package/dist/word-text.js.map +1 -0
- package/dist/worker-proxy.bundle.js +2 -536
- package/dist/worker-proxy.bundle.js.map +7 -0
- package/dist/worker-proxy.d.ts +14 -1
- package/dist/worker-proxy.d.ts.map +1 -1
- package/dist/worker-proxy.js +357 -339
- package/dist/worker-proxy.js.map +1 -1
- package/package.json +13 -12
package/dist/session.bundle.js
CHANGED
|
@@ -1,2013 +1,2 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
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
|