@cloudpdf/engine 3.0.0-next.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/dist/index.cjs ADDED
@@ -0,0 +1,2513 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
3
+ let _embedpdf_engine_core_wire = require("@embedpdf/engine-core/wire");
4
+ let _embedpdf_engine_services = require("@embedpdf/engine-services");
5
+ //#region src/document/planes.ts
6
+ /**
7
+ * Plane-scope resolution — THE one rule every cloud service uses to pick a
8
+ * path family: a read resolves at the DOC-LEVEL (shared base) path iff EVERY
9
+ * plane it depends on is inherited (`'base'`) by this layer. One owned plane
10
+ * → the layer-scoped path.
11
+ *
12
+ * Absent `scopes` (base manifests never carry it; pre-plane servers omit
13
+ * it) means layer paths — never wrong, only unshared.
14
+ */
15
+ function planesInherited(manifest, planes) {
16
+ const scopes = manifest.scopes;
17
+ if (!scopes) return false;
18
+ return planes.every((plane) => scopes[plane] === "base");
19
+ }
20
+ //#endregion
21
+ //#region src/document/CloudDocumentActionsService.ts
22
+ var CloudDocumentActionsService = class {
23
+ constructor(http, docId, layerName, isClosed, manifest) {
24
+ this.http = http;
25
+ this.docId = docId;
26
+ this.layerName = layerName;
27
+ this.isClosed = isClosed;
28
+ this.manifest = manifest;
29
+ }
30
+ read() {
31
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
32
+ return _embedpdf_engine_core_runtime.AbortablePromise.run((signal) => this.http.getJsonWithRefresh(async (currentSignal) => {
33
+ const manifest = await this.manifest.get(currentSignal);
34
+ return planesInherited(manifest, ["actions"]) ? _embedpdf_engine_core_wire.wirePaths.docActions(this.docId, manifest.actionsVersion) : _embedpdf_engine_core_wire.wirePaths.layerActions(this.docId, this.layerName, manifest.actionsVersion);
35
+ }, (raw) => _embedpdf_engine_core_wire.DocumentActionsSnapshotSchema.parse(raw), async (currentSignal) => {
36
+ await this.manifest.refresh(currentSignal);
37
+ }, signal));
38
+ }
39
+ };
40
+ //#endregion
41
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/typeof.js
42
+ function _typeof(o) {
43
+ "@babel/helpers - typeof";
44
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
45
+ return typeof o;
46
+ } : function(o) {
47
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
48
+ }, _typeof(o);
49
+ }
50
+ //#endregion
51
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPrimitive.js
52
+ function toPrimitive(t, r) {
53
+ if ("object" != _typeof(t) || !t) return t;
54
+ var e = t[Symbol.toPrimitive];
55
+ if (void 0 !== e) {
56
+ var i = e.call(t, r || "default");
57
+ if ("object" != _typeof(i)) return i;
58
+ throw new TypeError("@@toPrimitive must return a primitive value.");
59
+ }
60
+ return ("string" === r ? String : Number)(t);
61
+ }
62
+ //#endregion
63
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPropertyKey.js
64
+ function toPropertyKey(t) {
65
+ var i = toPrimitive(t, "string");
66
+ return "symbol" == _typeof(i) ? i : i + "";
67
+ }
68
+ //#endregion
69
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/defineProperty.js
70
+ function _defineProperty(e, r, t) {
71
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
72
+ value: t,
73
+ enumerable: !0,
74
+ configurable: !0,
75
+ writable: !0
76
+ }) : e[r] = t, e;
77
+ }
78
+ //#endregion
79
+ //#region src/document/CloudDocumentAnnotationsService.ts
80
+ var CloudDocumentAnnotationsService = class {
81
+ constructor(http, docId, layerName, isClosed, manifest) {
82
+ this.http = http;
83
+ this.docId = docId;
84
+ this.layerName = layerName;
85
+ this.isClosed = isClosed;
86
+ this.manifest = manifest;
87
+ }
88
+ listRawAll() {
89
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
90
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
91
+ const manifest = await this.manifest.get(signal);
92
+ return { pages: await Promise.all(manifest.pages.map((page) => this.readCurrentPage(page.state.pageObjectNumber, signal))) };
93
+ });
94
+ }
95
+ listRaw(pageObjectNumber) {
96
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
97
+ return _embedpdf_engine_core_runtime.AbortablePromise.run((signal) => this.readCurrentPage(pageObjectNumber, signal));
98
+ }
99
+ beginWeakEdit(pageObjectNumbers) {
100
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
101
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
102
+ const response = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerWeakAnnotationSession(this.docId, this.layerName), { pageObjectNumbers }, (raw) => _embedpdf_engine_core_wire.WeakAnnotationSessionResponseSchema.parse(raw), signal);
103
+ return new CloudWeakAnnotationEditSession(this.http, this.docId, this.layerName, () => this.isClosed(), response);
104
+ });
105
+ }
106
+ async readCurrentPage(pageObjectNumber, signal) {
107
+ return this.http.getJson(_embedpdf_engine_core_wire.wirePaths.layerPageAnnotationsCurrent(this.docId, this.layerName, pageObjectNumber), (raw) => _embedpdf_engine_core_wire.AnnotationListPageSnapshotSchema.parse(raw), signal);
108
+ }
109
+ };
110
+ var CloudWeakAnnotationEditSession = class {
111
+ constructor(http, docId, layerName, isClosed, response) {
112
+ this.http = http;
113
+ this.docId = docId;
114
+ this.layerName = layerName;
115
+ this.isClosed = isClosed;
116
+ _defineProperty(this, "response", void 0);
117
+ _defineProperty(this, "released", false);
118
+ this.response = response;
119
+ }
120
+ get id() {
121
+ return this.response.sessionId;
122
+ }
123
+ get expiresAt() {
124
+ return this.response.expiresAt;
125
+ }
126
+ get heartbeatIntervalMs() {
127
+ return this.response.heartbeatIntervalMs;
128
+ }
129
+ get pageObjectNumbers() {
130
+ return this.response.pageObjectNumbers;
131
+ }
132
+ covers(pageObjectNumber) {
133
+ return this.response.pageObjectNumbers.includes(pageObjectNumber);
134
+ }
135
+ updatePages(pageObjectNumbers) {
136
+ if (this.isClosed() || this.released) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `weak annotation session ${this.id} is closed`));
137
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
138
+ this.response = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerWeakAnnotationSessionPages(this.docId, this.layerName, this.id), { pageObjectNumbers }, (raw) => _embedpdf_engine_core_wire.WeakAnnotationSessionResponseSchema.parse(raw), signal);
139
+ });
140
+ }
141
+ heartbeat() {
142
+ if (this.isClosed() || this.released) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `weak annotation session ${this.id} is closed`));
143
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
144
+ this.response = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerWeakAnnotationSessionHeartbeat(this.docId, this.layerName, this.id), {}, (raw) => _embedpdf_engine_core_wire.WeakAnnotationSessionResponseSchema.parse(raw), signal);
145
+ });
146
+ }
147
+ release() {
148
+ if (this.released) return _embedpdf_engine_core_runtime.AbortablePromise.resolveValue(void 0);
149
+ this.released = true;
150
+ return _embedpdf_engine_core_runtime.AbortablePromise.run((signal) => this.http.deleteEmpty(_embedpdf_engine_core_wire.wirePaths.layerWeakAnnotationSessionRelease(this.docId, this.layerName, this.id), signal));
151
+ }
152
+ };
153
+ //#endregion
154
+ //#region src/document/buildMutationForm.ts
155
+ /**
156
+ * Multipart envelope for mutations that carry binaries: part `body` holds
157
+ * the exact JSON the plain request would have been, plus one
158
+ * `resource:{key}` file part per binary payload. Mirrors the appearance
159
+ * response protocol (`manifest` part + named image parts) in reverse.
160
+ * Shared by the annotation mutations (stamp images, attachment files) and
161
+ * the document-level `attachments.create` — one envelope, one parser on
162
+ * the server side.
163
+ */
164
+ function buildMutationForm(body, resources) {
165
+ const form = new FormData();
166
+ form.append("body", JSON.stringify(body));
167
+ for (const [key, resource] of Object.entries(resources)) form.append(`resource:${key}`, new Blob([resource.bytes], { type: resource.mimeType ?? "application/octet-stream" }), resource.name ?? key);
168
+ return form;
169
+ }
170
+ function hasResources(resources) {
171
+ return Object.keys(resources).length > 0;
172
+ }
173
+ //#endregion
174
+ //#region src/document/parseAttachmentContent.ts
175
+ /** Header carrying the file's name, token-text encoded (names are
176
+ * arbitrary UTF-8; HTTP header values are not). */
177
+ const FILE_NAME_HEADER = "X-EmbedPDF-File-Name";
178
+ /**
179
+ * Project an attachment-file response into `AttachmentContent`. The body
180
+ * IS the decoded bytes; the metadata rides as headers — `Content-Type`
181
+ * for the declared mime type, `X-EmbedPDF-File-Name` for the file name.
182
+ * Shared by the document-level `attachments.download()` and the
183
+ * annotation-level `annotations.downloadFile()` reads — one wire shape
184
+ * for both homes a file can live in.
185
+ */
186
+ function parseAttachmentContent(file) {
187
+ const encodedName = file.headers.get(FILE_NAME_HEADER);
188
+ if (encodedName === null) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.WireFormat, `attachment response missing ${FILE_NAME_HEADER} header`);
189
+ let name;
190
+ try {
191
+ name = (0, _embedpdf_engine_core_wire.decodeTokenText)(encodedName);
192
+ } catch (err) {
193
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.WireFormat, `malformed ${FILE_NAME_HEADER} header: ${err?.message ?? err}`, { cause: err });
194
+ }
195
+ const mimeType = file.headers.get("Content-Type");
196
+ return {
197
+ bytes: file.bytes,
198
+ name,
199
+ ...mimeType !== null ? { mimeType } : {}
200
+ };
201
+ }
202
+ //#endregion
203
+ //#region src/document/CloudDocumentAttachmentsService.ts
204
+ /** The `/attachments@…` listing leaf is a bare array of name-tree entries. */
205
+ const EmbeddedFileListSchema = _embedpdf_engine_core_wire.EmbeddedFileItemSchema.array();
206
+ /**
207
+ * Cloud-side document-level attachments (the catalog's `/EmbeddedFiles`
208
+ * name tree). Reads use immutable leaves pinned by the manifest's
209
+ * `attachmentsVersion`: `/attachments@…` for the metadata listing and
210
+ * `/attachment-files/…@…` for one file's decoded bytes — a separate
211
+ * capability tier, so a CDN credential for the listing can never egress
212
+ * content. Mutations use unversioned collection/member URLs and are
213
+ * never cached.
214
+ */
215
+ var CloudDocumentAttachmentsService = class {
216
+ constructor(http, docId, layerName, isClosed, manifest, publisher) {
217
+ this.http = http;
218
+ this.docId = docId;
219
+ this.layerName = layerName;
220
+ this.isClosed = isClosed;
221
+ this.manifest = manifest;
222
+ this.publisher = publisher;
223
+ }
224
+ /**
225
+ * Snapshot of the name tree, in tree (key-sorted) order. Pulls
226
+ * `attachmentsVersion` from the cached manifest and, on a 404 (stale
227
+ * pointer), transparently refreshes the manifest and retries once.
228
+ * The pin bumps only on attachment writes, so this leaf stays cached
229
+ * across page and annotation edits.
230
+ */
231
+ list() {
232
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
233
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
234
+ const buildPath = async (s) => {
235
+ const manifest = await this.manifest.get(s);
236
+ return planesInherited(manifest, ["attachments"]) ? _embedpdf_engine_core_wire.wirePaths.docAttachments(this.docId, manifest.attachmentsVersion) : _embedpdf_engine_core_wire.wirePaths.layerAttachments(this.docId, this.layerName, manifest.attachmentsVersion);
237
+ };
238
+ return this.http.getJsonWithRefresh(buildPath, (raw) => EmbeddedFileListSchema.parse(raw), async (s) => {
239
+ await this.manifest.refresh(s);
240
+ }, signal);
241
+ });
242
+ }
243
+ /**
244
+ * Decode and return one embedded file's bytes. The body is the decoded
245
+ * stream; the name and mime type ride as response headers (see
246
+ * `parseAttachmentContent`). Same versioned-leaf + refresh-retry rails
247
+ * as {@link list}.
248
+ */
249
+ download(ref) {
250
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
251
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
252
+ const buildPath = async (s) => {
253
+ const manifest = await this.manifest.get(s);
254
+ return planesInherited(manifest, ["attachments"]) ? _embedpdf_engine_core_wire.wirePaths.docAttachmentFile(this.docId, ref.key, manifest.attachmentsVersion) : _embedpdf_engine_core_wire.wirePaths.layerAttachmentFile(this.docId, this.layerName, ref.key, manifest.attachmentsVersion);
255
+ };
256
+ return parseAttachmentContent(await this.http.getFileWithRefresh(buildPath, async (s) => {
257
+ await this.manifest.refresh(s);
258
+ }, signal));
259
+ });
260
+ }
261
+ /**
262
+ * Create an embedded file in the name tree. Same splitter the
263
+ * file-attachment annotation draft uses — metadata into the `body`
264
+ * JSON part, bytes into the `resource:r0` file part — POSTed as the
265
+ * standard multipart mutation envelope.
266
+ */
267
+ create(file) {
268
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
269
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
270
+ const { wireFile, resource } = await (0, _embedpdf_engine_core_runtime.normalizeAttachmentFileSource)(file, "r0");
271
+ const result = await this.http.postMultipartJson(_embedpdf_engine_core_wire.wirePaths.layerAttachmentsCollection(this.docId, this.layerName), buildMutationForm(wireFile, { r0: resource }), (raw) => _embedpdf_engine_core_wire.AttachmentCreateResultSchema.parse(raw), signal);
272
+ if (result.cache) this.manifest.applyAttachments(result.cache);
273
+ this.publisher.publishLocal({
274
+ type: "attachment.created",
275
+ ...result
276
+ });
277
+ return result;
278
+ });
279
+ }
280
+ /** Delete an embedded file from the name tree by its durable key. */
281
+ delete(ref) {
282
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
283
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
284
+ const result = await this.http.deleteJson(_embedpdf_engine_core_wire.wirePaths.layerAttachmentItem(this.docId, this.layerName, ref.key), (raw) => _embedpdf_engine_core_wire.AttachmentDeleteResultSchema.parse(raw), signal);
285
+ if (result.cache) this.manifest.applyAttachments(result.cache);
286
+ this.publisher.publishLocal({
287
+ type: "attachment.deleted",
288
+ ...result
289
+ });
290
+ return result;
291
+ });
292
+ }
293
+ };
294
+ //#endregion
295
+ //#region src/document/CloudDocumentFormsService.ts
296
+ /** Content types the import POST body may carry; the server sniffs the
297
+ * actual format from the bytes, so this is advisory only. */
298
+ const IMPORT_CONTENT_TYPE = {
299
+ fdf: "application/vnd.fdf",
300
+ xfdf: "application/vnd.adobe.xfdf"
301
+ };
302
+ /**
303
+ * Cloud-side document forms service. Mirrors the local wiring: each call
304
+ * produces an `AbortablePromise` that propagates `signal.abort()` down to
305
+ * `fetch` and validates the JSON response with the wire-stable Zod schema.
306
+ *
307
+ * Forms are document-scoped, so reads use the unversioned `/form` URLs
308
+ * (always `no-store` — there is no content-addressed variant). Mutation
309
+ * results carry the per-page `cacheDelta` for pages whose widget
310
+ * appearances changed; `absorbMutation` folds it into the cached manifest
311
+ * so annotation/render reads stay coherent.
312
+ */
313
+ var CloudDocumentFormsService = class {
314
+ constructor(http, docId, layerName, isClosed, manifest, publisher) {
315
+ this.http = http;
316
+ this.docId = docId;
317
+ this.layerName = layerName;
318
+ this.isClosed = isClosed;
319
+ this.manifest = manifest;
320
+ this.publisher = publisher;
321
+ }
322
+ list() {
323
+ const rejected = this.rejectIfClosed();
324
+ if (rejected) return rejected;
325
+ return _embedpdf_engine_core_runtime.AbortablePromise.run((signal) => this.http.getJson(_embedpdf_engine_core_wire.wirePaths.layerForm(this.docId, this.layerName), (raw) => _embedpdf_engine_core_wire.FormSnapshotSchema.parse(raw), signal));
326
+ }
327
+ get(ref) {
328
+ const rejected = this.rejectIfClosed();
329
+ if (rejected) return rejected;
330
+ return _embedpdf_engine_core_runtime.AbortablePromise.run((signal) => this.http.getJson(_embedpdf_engine_core_wire.wirePaths.layerFormFieldByKey(this.docId, this.layerName, (0, _embedpdf_engine_core_runtime.encodeFieldRefKey)(ref)), (raw) => _embedpdf_engine_core_wire.FormFieldDTOSchema.parse(raw), signal));
331
+ }
332
+ setValue(ref, value) {
333
+ const rejected = this.rejectIfClosed();
334
+ if (rejected) return rejected;
335
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
336
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerFormFieldValue(this.docId, this.layerName, (0, _embedpdf_engine_core_runtime.encodeFieldRefKey)(ref)), { value }, (raw) => _embedpdf_engine_core_wire.FormSetValueResultSchema.parse(raw), signal);
337
+ return this.absorbMutation(result, "form.valueChanged");
338
+ });
339
+ }
340
+ reset(ref) {
341
+ const rejected = this.rejectIfClosed();
342
+ if (rejected) return rejected;
343
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
344
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerFormFieldReset(this.docId, this.layerName, (0, _embedpdf_engine_core_runtime.encodeFieldRefKey)(ref)), {}, (raw) => _embedpdf_engine_core_wire.FormSetValueResultSchema.parse(raw), signal);
345
+ return this.absorbMutation(result, "form.valueChanged");
346
+ });
347
+ }
348
+ applyEffects(effects) {
349
+ const rejected = this.rejectIfClosed();
350
+ if (rejected) return rejected;
351
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
352
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerFormEffects(this.docId, this.layerName), { effects }, (raw) => _embedpdf_engine_core_wire.FormEffectsResultSchema.parse(raw), signal);
353
+ if (result.meta === null) return result;
354
+ this.manifest.apply(result.meta, ["annotations"]);
355
+ this.publisher.publishLocal({
356
+ type: "form.effectsApplied",
357
+ ...result
358
+ });
359
+ return result;
360
+ });
361
+ }
362
+ exportData(format = "xfdf") {
363
+ const rejected = this.rejectIfClosed();
364
+ if (rejected) return rejected;
365
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
366
+ return {
367
+ format,
368
+ bytes: await this.http.getBytes(_embedpdf_engine_core_wire.wirePaths.layerFormData(this.docId, this.layerName, format), signal)
369
+ };
370
+ });
371
+ }
372
+ importData(data, format) {
373
+ const rejected = this.rejectIfClosed();
374
+ if (rejected) return rejected;
375
+ const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
376
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
377
+ const result = await this.http.postBytesJson(_embedpdf_engine_core_wire.wirePaths.layerFormData(this.docId, this.layerName, format), bytes, format ? IMPORT_CONTENT_TYPE[format] : "application/octet-stream", (raw) => _embedpdf_engine_core_wire.FormImportResultSchema.parse(raw), signal);
378
+ return this.absorbMutation(result, "form.imported");
379
+ });
380
+ }
381
+ createField(draft) {
382
+ const rejected = this.rejectIfClosed();
383
+ if (rejected) return rejected;
384
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
385
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerFormFields(this.docId, this.layerName), draft, (raw) => _embedpdf_engine_core_wire.FormFieldCreateResultSchema.parse(raw), signal);
386
+ return this.absorbMutation(result, "form.fieldCreated");
387
+ });
388
+ }
389
+ updateField(ref, patch) {
390
+ const rejected = this.rejectIfClosed();
391
+ if (rejected) return rejected;
392
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
393
+ const result = await this.http.patchJson(_embedpdf_engine_core_wire.wirePaths.layerFormFieldByKey(this.docId, this.layerName, (0, _embedpdf_engine_core_runtime.encodeFieldRefKey)(ref)), patch, (raw) => _embedpdf_engine_core_wire.FormFieldUpdateResultSchema.parse(raw), signal);
394
+ return this.absorbMutation(result, "form.fieldUpdated");
395
+ });
396
+ }
397
+ deleteField(ref) {
398
+ const rejected = this.rejectIfClosed();
399
+ if (rejected) return rejected;
400
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
401
+ const result = await this.http.deleteJson(_embedpdf_engine_core_wire.wirePaths.layerFormFieldByKey(this.docId, this.layerName, (0, _embedpdf_engine_core_runtime.encodeFieldRefKey)(ref)), (raw) => _embedpdf_engine_core_wire.FormFieldDeleteResultSchema.parse(raw), signal);
402
+ return this.absorbMutation(result, "form.fieldDeleted");
403
+ });
404
+ }
405
+ attachWidget(ref, widget, options) {
406
+ const rejected = this.rejectIfClosed();
407
+ if (rejected) return rejected;
408
+ const onState = options?.onState;
409
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
410
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerFormFieldWidgets(this.docId, this.layerName, (0, _embedpdf_engine_core_runtime.encodeFieldRefKey)(ref)), {
411
+ widget,
412
+ ...onState ? { onState } : {}
413
+ }, (raw) => _embedpdf_engine_core_wire.FormWidgetLinkResultSchema.parse(raw), signal);
414
+ return this.absorbMutation(result, "form.widgetAttached");
415
+ });
416
+ }
417
+ detachWidget(ref, widget) {
418
+ const rejected = this.rejectIfClosed();
419
+ if (rejected) return rejected;
420
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
421
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerFormFieldWidgetsDetach(this.docId, this.layerName, (0, _embedpdf_engine_core_runtime.encodeFieldRefKey)(ref)), { widget }, (raw) => _embedpdf_engine_core_wire.FormWidgetLinkResultSchema.parse(raw), signal);
422
+ return this.absorbMutation(result, "form.widgetDetached");
423
+ });
424
+ }
425
+ repair(options) {
426
+ const rejected = this.rejectIfClosed();
427
+ if (rejected) return rejected;
428
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
429
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerFormRepair(this.docId, this.layerName), { bakeAppearances: options?.bakeAppearances ?? false }, (raw) => _embedpdf_engine_core_wire.FormRepairResultSchema.parse(raw), signal);
430
+ return this.absorbMutation(result, "form.repaired");
431
+ });
432
+ }
433
+ rejectIfClosed() {
434
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
435
+ return null;
436
+ }
437
+ /**
438
+ * Patch the cached manifest, then publish the mutation to the document's
439
+ * event stream (in that order — listeners reading the manifest in their
440
+ * callback must see post-mutation state). Same rails as annotations.
441
+ */
442
+ absorbMutation(result, type) {
443
+ this.manifest.apply(result.meta, ["annotations"]);
444
+ this.publisher.publishLocal({
445
+ type,
446
+ ...result
447
+ });
448
+ return result;
449
+ }
450
+ };
451
+ //#endregion
452
+ //#region src/document/CloudDocumentPagesService.ts
453
+ /**
454
+ * Cloud-side document pages service. Mirrors `LocalDocumentPagesService`
455
+ * over HTTP: GET /pages for `list`, POST /pages/move for the reorder.
456
+ *
457
+ * Page identity rule (locked with the user, do not change):
458
+ * - Pages are addressed exclusively by their indirect
459
+ * `pageObjectNumber`. The wire never sends a page index for a
460
+ * mutation. This keeps multi-call client logic from having to
461
+ * account for index drift between requests.
462
+ * - Successful `move()` returns the new `layout` (order + geometry) plus
463
+ * cloud coherence pins. The server does NOT bump per-page revisions on a
464
+ * page move (page reorder is intentionally outside the weak-ref staleness
465
+ * model), only `docVersion` + `layoutVersion`.
466
+ */
467
+ var CloudDocumentPagesService = class {
468
+ constructor(http, docId, layerName, isClosed, manifest, publisher) {
469
+ this.http = http;
470
+ this.docId = docId;
471
+ this.layerName = layerName;
472
+ this.isClosed = isClosed;
473
+ this.manifest = manifest;
474
+ this.publisher = publisher;
475
+ }
476
+ /**
477
+ * Page-geometry list. The geometry bytes live at the content-addressed
478
+ * `/layout@layoutVersion=N` leaf (not in the manifest); the manifest only
479
+ * publishes the `layoutVersion` pointer. So `list()` reads `layoutVersion`
480
+ * from the cached manifest, fetches the layout leaf, and on a 404 (stale
481
+ * pointer) transparently refreshes the manifest and retries once — the
482
+ * same ladder the per-page text/geometry reads use. `layoutVersion` bumps
483
+ * only on structural page ops, so this leaf stays cached across content
484
+ * and annotation edits.
485
+ */
486
+ list() {
487
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
488
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
489
+ const buildPath = async (s) => {
490
+ const manifest = await this.manifest.get(s);
491
+ return planesInherited(manifest, ["layout"]) ? _embedpdf_engine_core_wire.wirePaths.docLayout(this.docId, manifest.layoutVersion) : _embedpdf_engine_core_wire.wirePaths.layerLayout(this.docId, this.layerName, manifest.layoutVersion);
492
+ };
493
+ return this.http.getJsonWithRefresh(buildPath, (raw) => _embedpdf_engine_core_wire.PageListSnapshotSchema.parse(raw), async (s) => {
494
+ await this.manifest.refresh(s);
495
+ }, signal);
496
+ });
497
+ }
498
+ move(pageObjectNumbers, destIndex) {
499
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
500
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
501
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerPagesMove(this.docId, this.layerName), {
502
+ pageObjectNumbers,
503
+ destIndex
504
+ }, (raw) => _embedpdf_engine_core_wire.PageMoveResultSchema.parse(raw), signal);
505
+ if (result.cache) this.manifest.applyPageStructure(result.cache);
506
+ this.publisher.publishLocal({
507
+ type: "pages.moved",
508
+ pageObjectNumbers,
509
+ destIndex,
510
+ ...result
511
+ });
512
+ return result;
513
+ });
514
+ }
515
+ rotate(pageObjectNumbers, rotation) {
516
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
517
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
518
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerPagesRotate(this.docId, this.layerName), {
519
+ pageObjectNumbers,
520
+ rotation
521
+ }, (raw) => _embedpdf_engine_core_wire.PageRotateResultSchema.parse(raw), signal);
522
+ if (result.cache) this.manifest.applyPageStructure(result.cache);
523
+ this.publisher.publishLocal({
524
+ type: "pages.rotated",
525
+ pageObjectNumbers,
526
+ rotation,
527
+ ...result
528
+ });
529
+ return result;
530
+ });
531
+ }
532
+ delete(pageObjectNumbers) {
533
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
534
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
535
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerPagesDelete(this.docId, this.layerName), { pageObjectNumbers }, (raw) => _embedpdf_engine_core_wire.PageDeleteResultSchema.parse(raw), signal);
536
+ if (result.cache) this.manifest.applyPageDelete(result.cache, pageObjectNumbers);
537
+ this.publisher.publishLocal({
538
+ type: "pages.deleted",
539
+ pageObjectNumbers,
540
+ ...result
541
+ });
542
+ return result;
543
+ });
544
+ }
545
+ flatten(pageObjectNumbers, usage = "display") {
546
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
547
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
548
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerPagesFlatten(this.docId, this.layerName), {
549
+ pageObjectNumbers,
550
+ usage
551
+ }, (raw) => _embedpdf_engine_core_wire.PageFlattenResultSchema.parse(raw), signal);
552
+ if (result.meta === null) return result;
553
+ this.manifest.apply(result.meta, ["content", "annotations"]);
554
+ this.publisher.publishLocal({
555
+ type: "pages.flattened",
556
+ ...result
557
+ });
558
+ return result;
559
+ });
560
+ }
561
+ };
562
+ //#endregion
563
+ //#region src/document/CloudDocumentRedactionService.ts
564
+ /**
565
+ * Cloud-side redaction apply. Mirrors `LocalDocumentRedactionService` over
566
+ * HTTP (POST /redactions/apply); the server enforces the capability gate
567
+ * (`doc.pages.modify` + `doc.annotate.modify` + `doc.redact`) and persists
568
+ * the rewritten layer artifact. See `DocumentRedactionService` for the
569
+ * two-stage model and the layer trust boundary — an apply rewrites THIS
570
+ * LAYER's bytes; the immutable base keeps the original.
571
+ */
572
+ var CloudDocumentRedactionService = class {
573
+ constructor(http, docId, layerName, isClosed, manifest, publisher) {
574
+ this.http = http;
575
+ this.docId = docId;
576
+ this.layerName = layerName;
577
+ this.isClosed = isClosed;
578
+ this.manifest = manifest;
579
+ this.publisher = publisher;
580
+ }
581
+ apply(scope) {
582
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
583
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
584
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerRedactionsApply(this.docId, this.layerName), { scope }, (raw) => _embedpdf_engine_core_wire.RedactionApplyResultSchema.parse(raw), signal);
585
+ if (result.meta === null) return result;
586
+ this.manifest.apply(result.meta, ["content", "annotations"]);
587
+ this.publisher.publishLocal({
588
+ type: "redaction.applied",
589
+ ...result
590
+ });
591
+ return result;
592
+ });
593
+ }
594
+ };
595
+ //#endregion
596
+ //#region src/document/CloudDocumentSearchService.ts
597
+ /**
598
+ * Cloud search transport: versioned GETs, one per budgeted slice.
599
+ *
600
+ * Every slice URL is a canonical cache key — the search token pins the
601
+ * layer's content epoch (computed from the cached manifest), the
602
+ * CANONICAL query (default-fold literals collapse case/diacritic
603
+ * variants into one entry), and the resume position. Identical searches
604
+ * from any reader of the document hit the same CDN entry; matches are
605
+ * all that ever crosses the trust boundary, and the rects/full endpoint
606
+ * split keeps permission tiers in disjoint cache namespaces.
607
+ *
608
+ * Staleness rides the standard versioned-read signal: a first slice
609
+ * answered `NotFound` means OUR manifest is stale → refresh and retry
610
+ * once; a continuation answered `NotFound` means the document changed
611
+ * mid-search → `InvalidArg`, same contract as the local engine's cursor.
612
+ */
613
+ var CloudDocumentSearchService = class {
614
+ constructor(http, docId, layerName, isClosed, manifest) {
615
+ this.http = http;
616
+ this.docId = docId;
617
+ this.layerName = layerName;
618
+ this.isClosed = isClosed;
619
+ this.manifest = manifest;
620
+ }
621
+ query(request) {
622
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document not open: ${this.docId}`));
623
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
624
+ const path = (request.mode ?? "full") === "rects" ? _embedpdf_engine_core_wire.wirePaths.layerSearchRects : _embedpdf_engine_core_wire.wirePaths.layerSearchFull;
625
+ const get = (token) => this.http.getJson(path(this.docId, this.layerName, token), (raw) => _embedpdf_engine_core_wire.SearchSliceSchema.parse(raw), signal);
626
+ const query = (0, _embedpdf_engine_core_runtime.canonicalSearchQuery)(request.query);
627
+ if (request.cursor !== void 0) {
628
+ let token;
629
+ try {
630
+ token = (0, _embedpdf_engine_core_wire.decodeSearchToken)(request.cursor);
631
+ } catch {
632
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "malformed search cursor");
633
+ }
634
+ if (queryIdentity(token.query) !== queryIdentity(query)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "search cursor belongs to a different query — restart the search");
635
+ if (request.startPage !== void 0 && request.startPage !== token.startPage) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "startPage conflicts with the cursor — omit startPage when resuming");
636
+ try {
637
+ return await get(request.cursor);
638
+ } catch (err) {
639
+ if (_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "document changed since this search cursor was issued — restart the search");
640
+ throw err;
641
+ }
642
+ }
643
+ const mint = (epoch) => (0, _embedpdf_engine_core_wire.encodeSearchToken)({
644
+ epoch,
645
+ query,
646
+ ...request.startPage !== void 0 ? { startPage: request.startPage } : {},
647
+ skip: 0,
648
+ ...request.budget !== void 0 ? { budget: request.budget } : {}
649
+ });
650
+ try {
651
+ return await get(mint((0, _embedpdf_engine_core_runtime.searchContentEpoch)(await this.manifest.get(signal))));
652
+ } catch (err) {
653
+ if (!_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw err;
654
+ return await get(mint((0, _embedpdf_engine_core_runtime.searchContentEpoch)(await this.manifest.refresh(signal))));
655
+ }
656
+ });
657
+ }
658
+ };
659
+ /** Query identity for cursor validation (option order pinned by hand). */
660
+ function queryIdentity(query) {
661
+ return JSON.stringify([
662
+ query.regex ? "r" : "l",
663
+ query.text,
664
+ query.matchCase ? 1 : 0,
665
+ query.matchDiacritics ? 1 : 0,
666
+ query.wholeWord ? 1 : 0
667
+ ]);
668
+ }
669
+ //#endregion
670
+ //#region src/transport/decodeUnverifiedClaims.ts
671
+ function decodeUnverifiedClaims(token) {
672
+ const parts = token.split(".");
673
+ if (parts.length !== 3) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "malformed jwt: expected 3 segments");
674
+ const payloadB64Url = parts[1];
675
+ let bytes;
676
+ try {
677
+ bytes = base64UrlDecode(payloadB64Url);
678
+ } catch (err) {
679
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `malformed jwt payload: ${err.message ?? err}`);
680
+ }
681
+ let json;
682
+ try {
683
+ json = new TextDecoder().decode(bytes);
684
+ } catch (err) {
685
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `jwt payload is not valid utf-8: ${err.message ?? err}`);
686
+ }
687
+ let parsed;
688
+ try {
689
+ parsed = JSON.parse(json);
690
+ } catch (err) {
691
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `jwt payload is not valid json: ${err.message ?? err}`);
692
+ }
693
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "jwt payload must be a json object");
694
+ return parsed;
695
+ }
696
+ /**
697
+ * Decode a base64url string into a `Uint8Array`. Works in both Node
698
+ * and modern browsers; we avoid `Buffer` so the SDK stays
699
+ * browser-friendly. `atob` lives on the global in Node 16+ as well.
700
+ */
701
+ function base64UrlDecode(b64url) {
702
+ const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
703
+ const pad = b64.length % 4;
704
+ const padded = pad === 0 ? b64 : b64 + "=".repeat(4 - pad);
705
+ const bin = atob(padded);
706
+ const out = new Uint8Array(bin.length);
707
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
708
+ return out;
709
+ }
710
+ //#endregion
711
+ //#region src/document/CloudDocumentSecurityService.ts
712
+ var CloudDocumentSecurityService = class {
713
+ constructor(http, docId, layerName, initialHead, view, initialToken = null) {
714
+ this.http = http;
715
+ this.docId = docId;
716
+ this.layerName = layerName;
717
+ this.view = view;
718
+ _defineProperty(this, "state", void 0);
719
+ _defineProperty(this, "access", null);
720
+ _defineProperty(this, "tokenScope", void 0);
721
+ _defineProperty(this, "tokenIdentity", void 0);
722
+ this.state = (0, _embedpdf_engine_core_runtime.securityStateFromHead)(initialHead);
723
+ const claims = initialToken ? safeDecodeClaims(initialToken) : null;
724
+ this.tokenScope = Array.isArray(claims?.scope) ? claims.scope : [];
725
+ this.tokenIdentity = claims ? identityFromClaims(claims) : null;
726
+ }
727
+ get current() {
728
+ return this.state;
729
+ }
730
+ get currentAccess() {
731
+ return this.access;
732
+ }
733
+ /**
734
+ * Expanded capability set. Cloud-canonical post-/access; otherwise
735
+ * computed locally from the JWT scope + /head's pdf bits using the
736
+ * SAME `expandRawScope` helper engine-local calls — so the value
737
+ * matches across engines bit-for-bit on the same inputs.
738
+ */
739
+ get effectiveScope() {
740
+ if (this.access) return this.access.effectiveScope;
741
+ const bits = (0, _embedpdf_engine_core_runtime.decodePdfBits)(this.state.permissions.bits);
742
+ return Array.from((0, _embedpdf_engine_core_runtime.expandRawScope)(this.tokenScope, bits)).sort();
743
+ }
744
+ /**
745
+ * Wildcard-aware authorization check — mirrors what the server route layer
746
+ * enforces with. `effectiveScope` alone can't gate UI: it enumerates concrete
747
+ * grants and drops the `*` admin wildcard. So we honor a server-canonical
748
+ * concrete grant when present, then fall back to the same `checkCapability`
749
+ * predicate (which short-circuits `*`) against the JWT scope + /head bits.
750
+ */
751
+ allows(cap) {
752
+ if (this.access?.effectiveScope.includes(cap)) return true;
753
+ const bits = (0, _embedpdf_engine_core_runtime.decodePdfBits)(this.state.permissions.bits);
754
+ return (0, _embedpdf_engine_core_runtime.checkCapability)(cap, this.tokenScope, bits);
755
+ }
756
+ /**
757
+ * Identity of the current caller, or null when anonymous.
758
+ *
759
+ * Cloud-canonical post-/access; otherwise the identity claims from
760
+ * the JWT itself. Both are the same shape — the post-/access
761
+ * version is just refreshed to reflect any server-side identity
762
+ * augmentation (rare; reserved for future tenant hooks).
763
+ */
764
+ get identity() {
765
+ return this.access?.identity ?? this.tokenIdentity;
766
+ }
767
+ /**
768
+ * High-level "should I prompt for a password?" — single source of
769
+ * truth, computed via `passwordPromptFromState` so the answer is
770
+ * identical to what the local engine would say for the same
771
+ * security state. See `passwordPromptFromState` for the rules.
772
+ */
773
+ get passwordPrompt() {
774
+ return (0, _embedpdf_engine_core_runtime.passwordPromptFromState)(this.state);
775
+ }
776
+ unlock(input) {
777
+ if (this.view.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
778
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
779
+ return await this.postAccess(signal, {
780
+ password: input.password,
781
+ mode: input.mode ?? "any"
782
+ });
783
+ });
784
+ }
785
+ /**
786
+ * Cloud-internal: call /v1/access with no password to establish
787
+ * a CDN-credentialed session. Used by `CloudEngine.open` when
788
+ * /head's `access.reasons` includes 'cdn' but NOT 'password' —
789
+ * the server accepts an authenticated /access POST without a
790
+ * password and returns the signed-URL block.
791
+ *
792
+ * Not on the public `DocumentSecurityService` interface — the
793
+ * "unlock" verb implies user action, and this path is automatic.
794
+ * Local engine has no equivalent (there's no CDN concept).
795
+ */
796
+ establishAccess() {
797
+ if (this.view.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
798
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
799
+ return await this.postAccess(signal, { mode: "any" });
800
+ });
801
+ }
802
+ /**
803
+ * Single POST /v1/access implementation shared by `unlock()` (with
804
+ * password) and `establishAccess()` (no password). Updates cached
805
+ * security state, caches the access block, and pushes the CDN
806
+ * binding into the HttpClient so subsequent fetches apply CDN
807
+ * tokens via `applyCdnAccess`.
808
+ *
809
+ * `none` CDN adapter → the access block has null overrides/policies
810
+ * and `applyCdnAccess` short-circuits to origin; safe to call always.
811
+ */
812
+ async postAccess(signal, body) {
813
+ const response = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.access, {
814
+ docId: this.docId,
815
+ layerName: this.layerName,
816
+ ...body.password ? { password: body.password } : {},
817
+ mode: body.mode
818
+ }, (raw) => _embedpdf_engine_core_wire.AccessResponseSchema.parse(raw), signal);
819
+ this.state = response.security;
820
+ this.access = {
821
+ cdn: response.cdn,
822
+ passwordGrant: response.passwordGrant,
823
+ pdfPermissions: response.pdfPermissions,
824
+ scope: response.scope,
825
+ effectiveScope: response.effectiveScope,
826
+ identity: response.identity,
827
+ originPasswordPolicy: response.originPasswordPolicy,
828
+ expiresAt: response.expiresAt,
829
+ ...response.renderPolicy ? { renderPolicy: response.renderPolicy } : {}
830
+ };
831
+ this.http.setCdnAccess({
832
+ cdn: response.cdn,
833
+ docId: this.docId,
834
+ layerName: this.layerName
835
+ });
836
+ return {
837
+ security: this.state,
838
+ access: this.access ?? void 0
839
+ };
840
+ }
841
+ };
842
+ /**
843
+ * Decode the JWT's payload without verifying. The SDK never verifies
844
+ * tokens client-side — the server is the verifier of record — but it
845
+ * needs the `scope` and identity claims for the local-fallback path
846
+ * of `effectiveScope` / `identity`. Returns null if the token is
847
+ * malformed; the security service treats that as "no claims known".
848
+ */
849
+ function safeDecodeClaims(token) {
850
+ try {
851
+ return decodeUnverifiedClaims(token);
852
+ } catch {
853
+ return null;
854
+ }
855
+ }
856
+ function identityFromClaims(claims) {
857
+ const out = {};
858
+ if (typeof claims["user_id"] === "string") out.user_id = claims["user_id"];
859
+ if (typeof claims["group_id"] === "string") out.group_id = claims["group_id"];
860
+ if (typeof claims["display_name"] === "string") out.display_name = claims["display_name"];
861
+ if (Array.isArray(claims["groups"])) {
862
+ const groups = claims["groups"].filter((g) => typeof g === "string");
863
+ if (groups.length > 0) out.groups = groups;
864
+ }
865
+ return Object.keys(out).length > 0 ? out : null;
866
+ }
867
+ //#endregion
868
+ //#region src/document/CloudMetadataService.ts
869
+ var CloudMetadataService = class {
870
+ constructor(http, docId, layerName, isClosed, manifest, publisher) {
871
+ this.http = http;
872
+ this.docId = docId;
873
+ this.layerName = layerName;
874
+ this.isClosed = isClosed;
875
+ this.manifest = manifest;
876
+ this.publisher = publisher;
877
+ }
878
+ /**
879
+ * Document metadata read. The Info-dict JSON lives at the
880
+ * content-addressed `/metadata@metadataVersion=N` leaf (not in the
881
+ * manifest); the manifest only publishes the `metadataVersion` pointer.
882
+ * So `read()` pulls `metadataVersion` from the cached manifest, fetches
883
+ * the leaf, and on a 404 (stale pointer) transparently refreshes the
884
+ * manifest and retries once. `metadataVersion` bumps only on metadata
885
+ * writes, so this leaf stays cached across page and annotation edits.
886
+ */
887
+ read() {
888
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
889
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
890
+ const buildPath = async (s) => {
891
+ const manifest = await this.manifest.get(s);
892
+ return planesInherited(manifest, ["metadata"]) ? _embedpdf_engine_core_wire.wirePaths.docMetadata(this.docId, manifest.metadataVersion) : _embedpdf_engine_core_wire.wirePaths.layerMetadata(this.docId, this.layerName, manifest.metadataVersion);
893
+ };
894
+ return this.http.getJsonWithRefresh(buildPath, (raw) => _embedpdf_engine_core_wire.DocumentMetadataSchema.parse(raw), async (s) => {
895
+ await this.manifest.refresh(s);
896
+ }, signal);
897
+ });
898
+ }
899
+ update(patch) {
900
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
901
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
902
+ const result = await this.http.postJson(_embedpdf_engine_core_wire.wirePaths.layerMetadataUpdate(this.docId, this.layerName), patch, (raw) => _embedpdf_engine_core_wire.MetadataUpdateResultSchema.parse(raw), signal);
903
+ if (result.cache) this.manifest.applyMetadata(result.cache);
904
+ this.publisher.publishLocal({
905
+ type: "metadata.updated",
906
+ ...result
907
+ });
908
+ return result;
909
+ });
910
+ }
911
+ };
912
+ //#endregion
913
+ //#region src/document/CloudPageAnnotationsService.ts
914
+ /**
915
+ * Cloud-side page annotation service. Mirrors the local wiring: each
916
+ * call produces an `AbortablePromise` that propagates `signal.abort()`
917
+ * down to `fetch` and validates the JSON response with the wire-stable
918
+ * Zod schema.
919
+ *
920
+ * Reads use immutable versioned layer URLs discovered from the
921
+ * manifest. Mutations use unversioned layer URLs and are never cached.
922
+ */
923
+ var CloudPageAnnotationsService = class {
924
+ constructor(http, docId, layerName, pageObjectNumber, isClosed, manifest, publisher) {
925
+ this.http = http;
926
+ this.docId = docId;
927
+ this.layerName = layerName;
928
+ this.pageObjectNumber = pageObjectNumber;
929
+ this.isClosed = isClosed;
930
+ this.manifest = manifest;
931
+ this.publisher = publisher;
932
+ }
933
+ list() {
934
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
935
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
936
+ const buildPath = async (s) => {
937
+ const manifest = await this.manifest.get(s);
938
+ const page = manifest.pages.find((p) => p.state.pageObjectNumber === this.pageObjectNumber);
939
+ if (!page) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no page with object number ${this.pageObjectNumber} in document ${this.docId}`);
940
+ return planesInherited(manifest, ["annotations"]) ? _embedpdf_engine_core_wire.wirePaths.docPageAnnotations(this.docId, this.pageObjectNumber, page.cache.annotationVersion) : _embedpdf_engine_core_wire.wirePaths.layerPageAnnotations(this.docId, this.layerName, this.pageObjectNumber, page.cache.annotationVersion);
941
+ };
942
+ return this.http.getJsonWithRefresh(buildPath, (raw) => _embedpdf_engine_core_wire.AnnotationListPageSnapshotSchema.parse(raw), async (s) => {
943
+ await this.manifest.refresh(s);
944
+ }, signal);
945
+ });
946
+ }
947
+ renderAppearances(_options) {
948
+ return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "annotations.renderAppearances() raw rasters are not available in the cloud engine; use renderAppearanceImages()"));
949
+ }
950
+ renderAppearanceImages(options = {}) {
951
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
952
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
953
+ const format = options.format ?? "webp";
954
+ const buildPath = async (s) => {
955
+ const manifest = await this.manifest.get(s);
956
+ const page = manifest.pages.find((p) => p.state.pageObjectNumber === this.pageObjectNumber);
957
+ if (!page) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no page with object number ${this.pageObjectNumber} in document ${this.docId}`);
958
+ const wireToken = (0, _embedpdf_engine_core_wire.annotationAppearancesImageOptionsToWire)({
959
+ ...options,
960
+ format
961
+ }, { annotationVersion: page.cache.annotationVersion });
962
+ return planesInherited(manifest, ["annotations"]) ? _embedpdf_engine_core_wire.wirePaths.docPageAnnotationAppearances(this.docId, this.pageObjectNumber, wireToken) : _embedpdf_engine_core_wire.wirePaths.layerPageAnnotationAppearances(this.docId, this.layerName, this.pageObjectNumber, wireToken);
963
+ };
964
+ return parseAppearanceForm(await this.http.getFormDataWithRefresh(buildPath, async (s) => {
965
+ await this.manifest.refresh(s);
966
+ }, signal));
967
+ });
968
+ }
969
+ /**
970
+ * Decode and return the file embedded in a FileAttachment annotation.
971
+ * A read over the immutable `attachment-files` leaf, pinned by the
972
+ * manifest's `attachmentsVersion` (annotation-level files re-key on the
973
+ * same pin as document-level ones), with the same stale-404 refresh
974
+ * retry as {@link list}. Weak `index` refs cannot be spliced into a GET
975
+ * URL (no body to carry the revision), so bytes reads require a stable
976
+ * id — the same `:annotKey` routing update()/delete() use.
977
+ */
978
+ downloadFile(ref) {
979
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
980
+ if (ref.pageObjectNumber !== this.pageObjectNumber) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `ref.pageObjectNumber ${ref.pageObjectNumber} != page ${this.pageObjectNumber}`));
981
+ if (ref.kind === "index") return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "downloadFile requires a stable ref (objectNumber or nm); index refs cannot address the content-addressed file leaf"));
982
+ const annotKey = (0, _embedpdf_engine_core_runtime.encodeStableIdKey)(refToStableId(ref));
983
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
984
+ const buildPath = async (s) => {
985
+ const manifest = await this.manifest.get(s);
986
+ return planesInherited(manifest, ["annotations", "attachments"]) ? _embedpdf_engine_core_wire.wirePaths.docAnnotationFile(this.docId, this.pageObjectNumber, annotKey, manifest.attachmentsVersion) : _embedpdf_engine_core_wire.wirePaths.layerAnnotationFile(this.docId, this.layerName, this.pageObjectNumber, annotKey, manifest.attachmentsVersion);
987
+ };
988
+ return parseAttachmentContent(await this.http.getFileWithRefresh(buildPath, async (s) => {
989
+ await this.manifest.refresh(s);
990
+ }, signal));
991
+ });
992
+ }
993
+ create(draft) {
994
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
995
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
996
+ const { wire, resources } = await (0, _embedpdf_engine_core_runtime.normalizeAnnotationDraft)(draft);
997
+ const path = _embedpdf_engine_core_wire.wirePaths.layerPageAnnotationsCreate(this.docId, this.layerName, this.pageObjectNumber);
998
+ const parse = (raw) => _embedpdf_engine_core_wire.AnnotationCreateResultSchema.parse(raw);
999
+ const result = hasResources(resources) ? await this.http.postMultipartJson(path, buildMutationForm(wire, resources), parse, signal) : await this.http.postJson(path, wire, parse, signal);
1000
+ return this.absorbMutation(result, "annotation.created");
1001
+ });
1002
+ }
1003
+ update(ref, patch) {
1004
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
1005
+ if (ref.pageObjectNumber !== this.pageObjectNumber) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `ref.pageObjectNumber ${ref.pageObjectNumber} != page ${this.pageObjectNumber}`));
1006
+ if (ref.kind === "index") {
1007
+ const path = _embedpdf_engine_core_wire.wirePaths.layerAnnotationByKey(this.docId, this.layerName, ref.pageObjectNumber, "index");
1008
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1009
+ const { wire, resources } = await (0, _embedpdf_engine_core_runtime.normalizeAnnotationPatch)(patch);
1010
+ const result = await this.patchMutation(path, {
1011
+ ref,
1012
+ patch: wire
1013
+ }, resources, signal);
1014
+ return this.absorbMutation(result, "annotation.updated");
1015
+ });
1016
+ }
1017
+ const stableKey = (0, _embedpdf_engine_core_runtime.encodeStableIdKey)(refToStableId(ref));
1018
+ const path = _embedpdf_engine_core_wire.wirePaths.layerAnnotationByKey(this.docId, this.layerName, ref.pageObjectNumber, stableKey);
1019
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1020
+ const { wire, resources } = await (0, _embedpdf_engine_core_runtime.normalizeAnnotationPatch)(patch);
1021
+ const result = await this.patchMutation(path, { patch: wire }, resources, signal);
1022
+ return this.absorbMutation(result, "annotation.updated");
1023
+ });
1024
+ }
1025
+ /** PATCH as plain JSON, or as multipart when the patch carried binaries. */
1026
+ patchMutation(path, body, resources, signal) {
1027
+ const parse = (raw) => _embedpdf_engine_core_wire.AnnotationUpdateResultSchema.parse(raw);
1028
+ if (hasResources(resources)) return this.http.patchMultipartJson(path, buildMutationForm(body, resources), parse, signal);
1029
+ return this.http.patchJson(path, body, parse, signal);
1030
+ }
1031
+ delete(ref) {
1032
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
1033
+ if (ref.pageObjectNumber !== this.pageObjectNumber) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `ref.pageObjectNumber ${ref.pageObjectNumber} != page ${this.pageObjectNumber}`));
1034
+ if (ref.kind === "index") {
1035
+ const path = _embedpdf_engine_core_wire.wirePaths.layerAnnotationByKey(this.docId, this.layerName, ref.pageObjectNumber, "index");
1036
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1037
+ const result = await this.http.patchJson(path, {
1038
+ ref,
1039
+ op: "delete"
1040
+ }, (raw) => _embedpdf_engine_core_wire.AnnotationDeleteResultSchema.parse(raw), signal);
1041
+ return this.absorbMutation(result, "annotation.deleted");
1042
+ });
1043
+ }
1044
+ const stableKey = (0, _embedpdf_engine_core_runtime.encodeStableIdKey)(refToStableId(ref));
1045
+ const path = _embedpdf_engine_core_wire.wirePaths.layerAnnotationByKey(this.docId, this.layerName, ref.pageObjectNumber, stableKey);
1046
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1047
+ const result = await this.http.deleteJson(path, (raw) => _embedpdf_engine_core_wire.AnnotationDeleteResultSchema.parse(raw), signal);
1048
+ return this.absorbMutation(result, "annotation.deleted");
1049
+ });
1050
+ }
1051
+ move(refs, toIndex) {
1052
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
1053
+ for (const r of refs) if (r.pageObjectNumber !== this.pageObjectNumber) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `move ref points at page ${r.pageObjectNumber}; service is bound to page ${this.pageObjectNumber}`));
1054
+ const path = _embedpdf_engine_core_wire.wirePaths.layerPageAnnotationsMove(this.docId, this.layerName, this.pageObjectNumber);
1055
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1056
+ const result = await this.http.postJson(path, {
1057
+ refs,
1058
+ toIndex
1059
+ }, (raw) => _embedpdf_engine_core_wire.AnnotationMoveResultSchema.parse(raw), signal);
1060
+ return this.absorbMutation(result, "annotation.moved");
1061
+ });
1062
+ }
1063
+ /**
1064
+ * Patch the cached manifest, then publish the mutation to the document's
1065
+ * event stream (in that order — listeners reading the manifest in their
1066
+ * callback must see post-mutation state). Each call site pairs the event
1067
+ * `type` with the matching result by construction; the cast localizes that
1068
+ * pairing here instead of widening every site.
1069
+ */
1070
+ absorbMutation(result, type) {
1071
+ this.manifest.apply(result.meta, ["annotations"]);
1072
+ this.publisher.publishLocal({
1073
+ type,
1074
+ pageObjectNumber: this.pageObjectNumber,
1075
+ ...result
1076
+ });
1077
+ return result;
1078
+ }
1079
+ };
1080
+ /**
1081
+ * Parse the appearance `multipart/form-data` response into the same
1082
+ * `AnnotationAppearanceImagesResult` shape the local engine produces. The
1083
+ * `manifest` part is validated against the wire schema; each image part is
1084
+ * wrapped in a `PageImageHandle` backed by the in-memory blob we already
1085
+ * downloaded.
1086
+ */
1087
+ async function parseAppearanceForm(form) {
1088
+ const manifestRaw = form.get("manifest");
1089
+ if (typeof manifestRaw !== "string") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.WireFormat, "appearance response missing JSON manifest part");
1090
+ let parsedJson;
1091
+ try {
1092
+ parsedJson = JSON.parse(manifestRaw);
1093
+ } catch (err) {
1094
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.WireFormat, `appearance manifest is not valid JSON: ${err?.message ?? err}`);
1095
+ }
1096
+ const manifest = _embedpdf_engine_core_wire.AnnotationAppearanceManifestSchema.parse(parsedJson);
1097
+ const appearances = await Promise.all(manifest.appearances.map(async (entry) => {
1098
+ const partValue = form.get(entry.part);
1099
+ if (partValue === null || typeof partValue === "string") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.WireFormat, `appearance response missing image part "${entry.part}"`);
1100
+ const blob = partValue;
1101
+ const bytes = new Uint8Array(await blob.arrayBuffer());
1102
+ const image = (0, _embedpdf_engine_core_runtime.createPageImageHandle)({
1103
+ width: entry.width,
1104
+ height: entry.height,
1105
+ format: entry.format,
1106
+ contentType: entry.contentType,
1107
+ source: {
1108
+ kind: "bytes",
1109
+ bytes
1110
+ }
1111
+ }, { async blob() {
1112
+ return blob;
1113
+ } });
1114
+ return {
1115
+ ref: entry.ref,
1116
+ mode: entry.mode,
1117
+ rect: entry.rect,
1118
+ image
1119
+ };
1120
+ }));
1121
+ return {
1122
+ pageState: manifest.pageState,
1123
+ appearances
1124
+ };
1125
+ }
1126
+ /**
1127
+ * Local helper: project a non-index `AnnotationRef` into the matching
1128
+ * `AnnotationStableId` shape so we can route by stable key. The compiler
1129
+ * narrows on `ref.kind` here so we can't accidentally pass an index ref.
1130
+ */
1131
+ function refToStableId(ref) {
1132
+ if (ref.kind === "objectNumber") return {
1133
+ kind: "objectNumber",
1134
+ value: ref.annotObjectNumber
1135
+ };
1136
+ return {
1137
+ kind: "nm",
1138
+ value: ref.nm
1139
+ };
1140
+ }
1141
+ //#endregion
1142
+ //#region src/document/CloudPageGeometryService.ts
1143
+ var CloudPageGeometryService = class {
1144
+ constructor(http, docId, layerName, pageObjectNumber, isClosed, manifest) {
1145
+ this.http = http;
1146
+ this.docId = docId;
1147
+ this.layerName = layerName;
1148
+ this.pageObjectNumber = pageObjectNumber;
1149
+ this.isClosed = isClosed;
1150
+ this.manifest = manifest;
1151
+ }
1152
+ read() {
1153
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
1154
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1155
+ const buildPath = async (s) => {
1156
+ const manifest = await this.manifest.get(s);
1157
+ const page = manifest.pages.find((p) => p.state.pageObjectNumber === this.pageObjectNumber);
1158
+ if (!page) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no page with object number ${this.pageObjectNumber} in document ${this.docId}`);
1159
+ return planesInherited(manifest, ["content"]) ? _embedpdf_engine_core_wire.wirePaths.docPageGeometry(this.docId, this.pageObjectNumber, page.cache.contentVersion) : _embedpdf_engine_core_wire.wirePaths.layerPageGeometry(this.docId, this.layerName, this.pageObjectNumber, page.cache.contentVersion);
1160
+ };
1161
+ return this.http.getJsonWithRefresh(buildPath, (raw) => _embedpdf_engine_core_wire.PageGeometrySnapshotSchema.parse(raw), async (s) => {
1162
+ await this.manifest.refresh(s);
1163
+ }, signal);
1164
+ });
1165
+ }
1166
+ };
1167
+ //#endregion
1168
+ //#region src/document/CloudPageRenderService.ts
1169
+ var CloudPageRenderService = class {
1170
+ constructor(http, docId, layerName, pageObjectNumber, isClosed, manifest) {
1171
+ this.http = http;
1172
+ this.docId = docId;
1173
+ this.layerName = layerName;
1174
+ this.pageObjectNumber = pageObjectNumber;
1175
+ this.isClosed = isClosed;
1176
+ this.manifest = manifest;
1177
+ }
1178
+ image(options = {}) {
1179
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
1180
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1181
+ const format = normalizeFormat(options.format);
1182
+ const includeAnnotations = options.includeAnnotations ?? true;
1183
+ const buildPath = async (s) => {
1184
+ const manifest = await this.manifest.get(s);
1185
+ const page = manifest.pages.find((p) => p.state.pageObjectNumber === this.pageObjectNumber);
1186
+ if (!page) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no page with object number ${this.pageObjectNumber} in document ${this.docId}`);
1187
+ const wireToken = (0, _embedpdf_engine_core_wire.renderImageOptionsToWire)({
1188
+ ...options,
1189
+ format
1190
+ }, {
1191
+ contentVersion: page.cache.contentVersion,
1192
+ ...includeAnnotations ? { annotationVersion: page.cache.annotationVersion } : {}
1193
+ });
1194
+ if (includeAnnotations) return planesInherited(manifest, ["content", "annotations"]) ? _embedpdf_engine_core_wire.wirePaths.docPageRenderAnnotated(this.docId, this.pageObjectNumber, wireToken) : _embedpdf_engine_core_wire.wirePaths.layerPageRenderAnnotated(this.docId, this.layerName, this.pageObjectNumber, wireToken);
1195
+ return planesInherited(manifest, ["content"]) ? _embedpdf_engine_core_wire.wirePaths.docPageRender(this.docId, this.pageObjectNumber, wireToken) : _embedpdf_engine_core_wire.wirePaths.layerPageRender(this.docId, this.layerName, this.pageObjectNumber, wireToken);
1196
+ };
1197
+ const requestPath = await buildPath(signal);
1198
+ return createCloudPageImageHandle({
1199
+ format,
1200
+ contentType: `image/${format}`,
1201
+ source: {
1202
+ kind: "url",
1203
+ url: this.http.absoluteUrl(requestPath)
1204
+ }
1205
+ }, this.http, buildPath, async (s) => {
1206
+ await this.manifest.refresh(s);
1207
+ });
1208
+ });
1209
+ }
1210
+ raw(_options) {
1211
+ return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "render.raw() is not available in the cloud engine; use render.image()"));
1212
+ }
1213
+ };
1214
+ function createCloudPageImageHandle(result, http, buildPath, onStaleVersion) {
1215
+ return (0, _embedpdf_engine_core_runtime.createPageImageHandle)(result, { blob: (signal) => http.getBlobWithRefresh(buildPath, onStaleVersion, signal ?? new AbortController().signal) });
1216
+ }
1217
+ function normalizeFormat(format) {
1218
+ if (format === void 0) return "webp";
1219
+ if (format === "png" || format === "webp") return format;
1220
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `cloud render.image() supports only "png" and "webp" (got "${format}")`);
1221
+ }
1222
+ //#endregion
1223
+ //#region src/document/CloudPageTextService.ts
1224
+ /**
1225
+ * Cloud-side per-page text service. `read()` fetches the
1226
+ * content-addressed URL `/v1/docs/:id/text/pages/:pon/data@contentVersion=N`, where
1227
+ * `:P` is the page's current `contentVersion` from the cached
1228
+ * manifest. On a 404 (stale version) the SDK transparently refreshes
1229
+ * `/head` + `/manifest@docVersion=N`, rebuilds the URL with the fresh `cN`,
1230
+ * and retries exactly once.
1231
+ */
1232
+ var CloudPageTextService = class {
1233
+ constructor(http, docId, layerName, pageObjectNumber, isClosed, manifest) {
1234
+ this.http = http;
1235
+ this.docId = docId;
1236
+ this.layerName = layerName;
1237
+ this.pageObjectNumber = pageObjectNumber;
1238
+ this.isClosed = isClosed;
1239
+ this.manifest = manifest;
1240
+ }
1241
+ read() {
1242
+ if (this.isClosed()) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.docId} is closed`));
1243
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1244
+ const buildPath = async (s) => {
1245
+ const manifest = await this.manifest.get(s);
1246
+ const page = manifest.pages.find((p) => p.state.pageObjectNumber === this.pageObjectNumber);
1247
+ if (!page) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no page with object number ${this.pageObjectNumber} in document ${this.docId}`);
1248
+ return planesInherited(manifest, ["content"]) ? _embedpdf_engine_core_wire.wirePaths.docPageText(this.docId, this.pageObjectNumber, page.cache.contentVersion) : _embedpdf_engine_core_wire.wirePaths.layerPageText(this.docId, this.layerName, this.pageObjectNumber, page.cache.contentVersion);
1249
+ };
1250
+ return this.http.getJsonWithRefresh(buildPath, (raw) => _embedpdf_engine_core_wire.PageTextSnapshotSchema.parse(raw), async (s) => {
1251
+ await this.manifest.refresh(s);
1252
+ }, signal);
1253
+ });
1254
+ }
1255
+ };
1256
+ //#endregion
1257
+ //#region src/document/CloudPageHandle.ts
1258
+ var CloudPageHandle = class {
1259
+ constructor(pageObjectNumber, pageIndex, http, docId, layerName, isClosed, manifest, publisher) {
1260
+ this.pageObjectNumber = pageObjectNumber;
1261
+ this.pageIndex = pageIndex;
1262
+ _defineProperty(this, "annotations", void 0);
1263
+ _defineProperty(this, "text", void 0);
1264
+ _defineProperty(this, "geometry", void 0);
1265
+ _defineProperty(this, "render", void 0);
1266
+ this.annotations = new CloudPageAnnotationsService(http, docId, layerName, pageObjectNumber, isClosed, manifest, publisher);
1267
+ this.text = new CloudPageTextService(http, docId, layerName, pageObjectNumber, isClosed, manifest);
1268
+ this.geometry = new CloudPageGeometryService(http, docId, layerName, pageObjectNumber, isClosed, manifest);
1269
+ this.render = new CloudPageRenderService(http, docId, layerName, pageObjectNumber, isClosed, manifest);
1270
+ }
1271
+ };
1272
+ //#endregion
1273
+ //#region src/realtime/auditRowToEvent.ts
1274
+ /**
1275
+ * Translate a remote audit row into a `DocumentEvent` — pure, so the
1276
+ * exactly-once and verbatim-payload invariants are unit-testable without a
1277
+ * server. Returns `null` for kinds this engine version doesn't know
1278
+ * (a NEWER server's events degrade to "ignored", never to a crash).
1279
+ *
1280
+ * Context-field fidelity differs by op, by design of the audit row:
1281
+ * - rotate/delete: `affectedPages` is exactly the op's page set; rotation
1282
+ * is recovered from the layout (it's absolute — every affected page
1283
+ * carries the value).
1284
+ * - move: the originator knows which block it moved; the audit row only
1285
+ * records the resulting order, so `pageObjectNumbers` is the full new
1286
+ * order and `destIndex` is absent (remote consumers use `layout`).
1287
+ */
1288
+ function auditRowToEvent(row, mySessionId) {
1289
+ if (row.originSessionId === mySessionId) return null;
1290
+ const origin = {
1291
+ kind: "remote",
1292
+ sessionId: row.originSessionId ?? `unknown:${row.sub}`,
1293
+ sub: row.sub,
1294
+ ts: row.ts,
1295
+ serverId: row.id
1296
+ };
1297
+ switch (row.kind) {
1298
+ case "annot.create": return {
1299
+ type: "annotation.created",
1300
+ pageObjectNumber: row.pageObjectNumber ?? row.affectedPages[0] ?? 0,
1301
+ origin,
1302
+ ...row.payload
1303
+ };
1304
+ case "annot.update": return {
1305
+ type: "annotation.updated",
1306
+ pageObjectNumber: row.pageObjectNumber ?? row.affectedPages[0] ?? 0,
1307
+ origin,
1308
+ ...row.payload
1309
+ };
1310
+ case "annot.delete": return {
1311
+ type: "annotation.deleted",
1312
+ pageObjectNumber: row.pageObjectNumber ?? row.affectedPages[0] ?? 0,
1313
+ origin,
1314
+ ...row.payload
1315
+ };
1316
+ case "annot.move": return {
1317
+ type: "annotation.moved",
1318
+ pageObjectNumber: row.pageObjectNumber ?? row.affectedPages[0] ?? 0,
1319
+ origin,
1320
+ ...row.payload
1321
+ };
1322
+ case "pages.move": return {
1323
+ type: "pages.moved",
1324
+ pageObjectNumbers: row.affectedPages,
1325
+ origin,
1326
+ ...row.payload
1327
+ };
1328
+ case "pages.rotate": {
1329
+ const payload = row.payload;
1330
+ const rotation = payload.layout.pages.find((page) => page.pageObjectNumber === row.affectedPages[0])?.rotation ?? 0;
1331
+ return {
1332
+ type: "pages.rotated",
1333
+ pageObjectNumbers: row.affectedPages,
1334
+ rotation,
1335
+ origin,
1336
+ ...payload
1337
+ };
1338
+ }
1339
+ case "pages.delete": return {
1340
+ type: "pages.deleted",
1341
+ pageObjectNumbers: row.affectedPages,
1342
+ origin,
1343
+ ...row.payload
1344
+ };
1345
+ case "pages.flatten": return {
1346
+ type: "pages.flattened",
1347
+ origin,
1348
+ ...row.payload
1349
+ };
1350
+ case "metadata.update": return {
1351
+ type: "metadata.updated",
1352
+ origin,
1353
+ ...row.payload
1354
+ };
1355
+ case "attachment.create": return {
1356
+ type: "attachment.created",
1357
+ origin,
1358
+ ...row.payload
1359
+ };
1360
+ case "attachment.delete": return {
1361
+ type: "attachment.deleted",
1362
+ origin,
1363
+ ...row.payload
1364
+ };
1365
+ case "form.setValue":
1366
+ case "form.reset": return {
1367
+ type: "form.valueChanged",
1368
+ origin,
1369
+ ...row.payload
1370
+ };
1371
+ case "form.import": return {
1372
+ type: "form.imported",
1373
+ origin,
1374
+ ...row.payload
1375
+ };
1376
+ case "form.repair": return {
1377
+ type: "form.repaired",
1378
+ origin,
1379
+ ...row.payload
1380
+ };
1381
+ case "form.createField": return {
1382
+ type: "form.fieldCreated",
1383
+ origin,
1384
+ ...row.payload
1385
+ };
1386
+ case "form.updateField": return {
1387
+ type: "form.fieldUpdated",
1388
+ origin,
1389
+ ...row.payload
1390
+ };
1391
+ case "form.deleteField": return {
1392
+ type: "form.fieldDeleted",
1393
+ origin,
1394
+ ...row.payload
1395
+ };
1396
+ case "form.attachWidget": return {
1397
+ type: "form.widgetAttached",
1398
+ origin,
1399
+ ...row.payload
1400
+ };
1401
+ case "form.detachWidget": return {
1402
+ type: "form.widgetDetached",
1403
+ origin,
1404
+ ...row.payload
1405
+ };
1406
+ case "form.applyEffects": return {
1407
+ type: "form.effectsApplied",
1408
+ origin,
1409
+ ...row.payload
1410
+ };
1411
+ default: return null;
1412
+ }
1413
+ }
1414
+ //#endregion
1415
+ //#region src/realtime/SseClient.ts
1416
+ /**
1417
+ * Fetch-based SSE consumer. Browser-native `EventSource` cannot send an
1418
+ * `Authorization` header, so the stream rides the same `HttpClient` as
1419
+ * every other request (same token provider, same session header).
1420
+ *
1421
+ * Resilience model: the cursor is the only state that matters. Every
1422
+ * (re)connect sends `Last-Event-ID: cursor`; the server replays the gap
1423
+ * from the audit log. So a dropped stream costs latency, never events —
1424
+ * the client needs no buffering and no acknowledgements.
1425
+ *
1426
+ * - network drop / server close / `auth-expiring` → reconnect with
1427
+ * jittered exponential backoff (500ms → 30s; reset after a stream
1428
+ * that lived ≥30s). `auth-expiring` reconnects pick up a fresh token
1429
+ * from the HttpClient's provider automatically.
1430
+ * - 401/403 → permanent: `onAuthLost`, no retry loop against a dead
1431
+ * credential.
1432
+ */
1433
+ var SseClient = class {
1434
+ constructor(opts) {
1435
+ this.opts = opts;
1436
+ _defineProperty(this, "cursor", void 0);
1437
+ _defineProperty(this, "closed", false);
1438
+ _defineProperty(this, "abort", null);
1439
+ _defineProperty(this, "retryTimer", null);
1440
+ _defineProperty(this, "retryDelayMs", 500);
1441
+ this.cursor = opts.initialCursor;
1442
+ }
1443
+ open() {
1444
+ if (this.closed) return;
1445
+ this.run();
1446
+ }
1447
+ close() {
1448
+ this.closed = true;
1449
+ if (this.retryTimer) clearTimeout(this.retryTimer);
1450
+ this.abort?.abort();
1451
+ }
1452
+ get lastEventId() {
1453
+ return this.cursor;
1454
+ }
1455
+ async run() {
1456
+ const startedAt = Date.now();
1457
+ this.abort = new AbortController();
1458
+ try {
1459
+ const res = await this.opts.http.stream(this.opts.path, this.cursor !== null ? { "Last-Event-ID": String(this.cursor) } : {}, this.abort.signal);
1460
+ if (res.status === 401 || res.status === 403) {
1461
+ this.closed = true;
1462
+ this.opts.onAuthLost();
1463
+ return;
1464
+ }
1465
+ if (!res.ok || !res.body) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Network, `events stream HTTP ${res.status}`);
1466
+ await this.consume(res.body);
1467
+ if (Date.now() - startedAt >= 3e4) this.retryDelayMs = 500;
1468
+ } catch {}
1469
+ if (this.closed) return;
1470
+ const jitter = this.retryDelayMs * (.5 + Math.random() * .5);
1471
+ this.retryTimer = setTimeout(() => void this.run(), jitter);
1472
+ this.retryDelayMs = Math.min(this.retryDelayMs * 2, 3e4);
1473
+ }
1474
+ async consume(body) {
1475
+ const reader = body.getReader();
1476
+ const decoder = new TextDecoder();
1477
+ let buffer = "";
1478
+ for (;;) {
1479
+ const { done, value } = await reader.read();
1480
+ if (done || this.closed) return;
1481
+ buffer += decoder.decode(value, { stream: true });
1482
+ for (;;) {
1483
+ const sep = buffer.indexOf("\n\n");
1484
+ if (sep < 0) break;
1485
+ const block = buffer.slice(0, sep);
1486
+ buffer = buffer.slice(sep + 2);
1487
+ this.dispatch(block);
1488
+ }
1489
+ }
1490
+ }
1491
+ dispatch(block) {
1492
+ if (block.startsWith(":")) return;
1493
+ let id = null;
1494
+ let event = "message";
1495
+ const data = [];
1496
+ for (const line of block.split("\n")) if (line.startsWith("id:")) id = Number(line.slice(3).trim());
1497
+ else if (line.startsWith("event:")) event = line.slice(6).trim();
1498
+ else if (line.startsWith("data:")) data.push(line.slice(5).trim());
1499
+ if (id !== null && Number.isFinite(id)) this.cursor = id;
1500
+ if (event === "mutation") try {
1501
+ this.opts.onRow(JSON.parse(data.join("\n")));
1502
+ } catch {}
1503
+ else if (event === "full-refresh") this.opts.onFullRefresh();
1504
+ else if (event === "auth-revoked") {
1505
+ this.closed = true;
1506
+ this.abort?.abort();
1507
+ this.opts.onAuthLost();
1508
+ }
1509
+ }
1510
+ };
1511
+ //#endregion
1512
+ //#region src/document/CloudDocumentHandle.ts
1513
+ var CloudDocumentHandle = class {
1514
+ constructor(http, id, layerName = _embedpdf_engine_core_wire.DEFAULT_LAYER_NAME, initialHead, initialToken = null, sessionId = `cloud:anon:${id}`) {
1515
+ this.http = http;
1516
+ this.layerName = layerName;
1517
+ _defineProperty(this, "id", void 0);
1518
+ _defineProperty(this, "capabilities", {
1519
+ weakAnnotationEditSessions: "required",
1520
+ pageEditSessions: "unsupported"
1521
+ });
1522
+ _defineProperty(this, "metadata", void 0);
1523
+ _defineProperty(this, "annotations", void 0);
1524
+ _defineProperty(this, "actions", void 0);
1525
+ _defineProperty(this, "attachments", void 0);
1526
+ _defineProperty(this, "forms", void 0);
1527
+ _defineProperty(this, "search", void 0);
1528
+ _defineProperty(this, "pages", void 0);
1529
+ _defineProperty(this, "redaction", void 0);
1530
+ _defineProperty(this, "security", void 0);
1531
+ _defineProperty(this, "render", void 0);
1532
+ _defineProperty(this, "events", void 0);
1533
+ _defineProperty(this, "publisher", void 0);
1534
+ _defineProperty(this, "hub", void 0);
1535
+ _defineProperty(this, "sessionId", void 0);
1536
+ _defineProperty(this, "sseClient", null);
1537
+ _defineProperty(this, "sseSubscribers", 0);
1538
+ _defineProperty(this, "closed", false);
1539
+ _defineProperty(this, "manifestCache", null);
1540
+ _defineProperty(this, "inflightManifest", null);
1541
+ _defineProperty(this, "manifestFloorVersion", 0);
1542
+ _defineProperty(this, "pendingInitialHead", void 0);
1543
+ _defineProperty(this, "manifestAccessor", void 0);
1544
+ this.id = id;
1545
+ this.pendingInitialHead = initialHead ?? null;
1546
+ const security = new CloudDocumentSecurityService(http, id, layerName, initialHead ?? fallbackUnknownHead(id), { isClosed: () => this.closed }, initialToken);
1547
+ this.security = security;
1548
+ this.render = { policy: async () => {
1549
+ const advertised = (security.currentAccess ?? (await security.establishAccess()).access ?? null)?.renderPolicy;
1550
+ if (!advertised) return _embedpdf_engine_core_runtime.CONTINUOUS_RENDER_POLICY;
1551
+ return {
1552
+ kind: "lattice",
1553
+ fullPage: { widths: advertised.fullPage.widths },
1554
+ ...advertised.tiles ? { tiles: advertised.tiles } : {},
1555
+ ...advertised.appearances ? { appearances: advertised.appearances } : {},
1556
+ ...advertised.maxRenderPixels !== void 0 ? { maxRenderPixels: advertised.maxRenderPixels } : {},
1557
+ formats: advertised.formats,
1558
+ background: advertised.background,
1559
+ enforced: advertised.enforced
1560
+ };
1561
+ } };
1562
+ const hub = new _embedpdf_engine_services.EventHub();
1563
+ this.hub = hub;
1564
+ this.sessionId = sessionId;
1565
+ this.events = {
1566
+ subscribe: (listener) => {
1567
+ const unsubscribe = hub.subscribe(listener);
1568
+ this.retainRemoteStream();
1569
+ let released = false;
1570
+ return () => {
1571
+ unsubscribe();
1572
+ if (!released) {
1573
+ released = true;
1574
+ this.releaseRemoteStream();
1575
+ }
1576
+ };
1577
+ },
1578
+ lastServerId: () => hub.lastServerId()
1579
+ };
1580
+ this.publisher = new _embedpdf_engine_services.SessionEventPublisher(hub, sessionId);
1581
+ this.manifestAccessor = {
1582
+ get: (signal) => this.getManifest(signal),
1583
+ refresh: (signal) => this.refreshManifest(signal),
1584
+ apply: (meta, owns) => this.absorbMutation(meta, owns),
1585
+ applyPageStructure: (cache) => this.absorbPageStructure(cache),
1586
+ applyPageDelete: (cache, deletedPages) => this.absorbPageDelete(cache, deletedPages),
1587
+ applyMetadata: (cache) => this.absorbMetadata(cache),
1588
+ applyAttachments: (cache) => this.absorbAttachments(cache)
1589
+ };
1590
+ this.metadata = new CloudMetadataService(http, id, layerName, () => this.closed, this.manifestAccessor, this.publisher);
1591
+ this.annotations = new CloudDocumentAnnotationsService(http, id, layerName, () => this.closed, this.manifestAccessor);
1592
+ this.actions = new CloudDocumentActionsService(http, id, layerName, () => this.closed, this.manifestAccessor);
1593
+ this.attachments = new CloudDocumentAttachmentsService(http, id, layerName, () => this.closed, this.manifestAccessor, this.publisher);
1594
+ this.forms = new CloudDocumentFormsService(http, id, layerName, () => this.closed, this.manifestAccessor, this.publisher);
1595
+ this.search = new CloudDocumentSearchService(http, id, layerName, () => this.closed, this.manifestAccessor);
1596
+ this.pages = new CloudDocumentPagesService(http, id, layerName, () => this.closed, this.manifestAccessor, this.publisher);
1597
+ this.redaction = new CloudDocumentRedactionService(http, id, layerName, () => this.closed, this.manifestAccessor, this.publisher);
1598
+ }
1599
+ page(pageObjectNumber) {
1600
+ return new CloudPageHandle(pageObjectNumber, -1, this.http, this.id, this.layerName, () => this.closed, this.manifestAccessor, this.publisher);
1601
+ }
1602
+ download(opts = {}) {
1603
+ if (this.closed) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.id} is closed`));
1604
+ const mode = opts.mode ?? _embedpdf_engine_core_runtime.DEFAULT_PDF_SAVE_MODE;
1605
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
1606
+ const buildPath = async () => {
1607
+ const manifest = await this.getManifest(signal);
1608
+ return _embedpdf_engine_core_wire.wirePaths.layerDownloadVersioned(this.id, this.layerName, {
1609
+ docVersion: manifest.docVersion,
1610
+ mode
1611
+ });
1612
+ };
1613
+ try {
1614
+ return await this.http.getBytes(await buildPath(), signal);
1615
+ } catch (err) {
1616
+ if (!_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw err;
1617
+ await this.refreshManifest(signal);
1618
+ return this.http.getBytes(await buildPath(), signal);
1619
+ }
1620
+ });
1621
+ }
1622
+ /**
1623
+ * Return the cached manifest, fetching cold-cache once if needed.
1624
+ * Concurrent callers share a single inflight request (singleflight)
1625
+ * so an N-page handle that opens N services in parallel still
1626
+ * triggers exactly one `/head` + `/manifest@docVersion=N` round-trip.
1627
+ */
1628
+ async getManifest(signal) {
1629
+ if (this.manifestCache) return this.manifestCache;
1630
+ if (!this.inflightManifest) this.startManifestFetch({ allowInitialHead: true });
1631
+ const promise = this.inflightManifest;
1632
+ if (!promise) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `manifest fetch was not started for document ${this.id}`);
1633
+ return awaitSignal(promise, signal);
1634
+ }
1635
+ /**
1636
+ * Force re-fetch. Used by the transparent-retry path when a leaf
1637
+ * URL returns 404 (stale version). Replaces `manifestCache`
1638
+ * wholesale so the next `getManifest()` is a Map lookup.
1639
+ */
1640
+ async refreshManifest(signal) {
1641
+ return awaitSignal(this.startManifestFetch({ allowInitialHead: false }), signal);
1642
+ }
1643
+ /**
1644
+ * Monotone plane-scope flip: mark the planes a mutation OWNS as layer-scoped in
1645
+ * the cached manifest. Scopes only ever move base → layer (no revert op
1646
+ * exists), so flipping is safe under ANY event ordering — a duplicate or
1647
+ * out-of-order event still proves the plane diverged at some point — and
1648
+ * the manifest fetch stays the authoritative source (the 404 → refresh
1649
+ * rail heals any miss, e.g. a future mutation kind this client doesn't
1650
+ * know about).
1651
+ */
1652
+ flipScopes(owns) {
1653
+ const cache = this.manifestCache;
1654
+ const scopes = cache?.scopes;
1655
+ if (!cache || !scopes) return;
1656
+ if (owns.every((plane) => scopes[plane] === "layer")) return;
1657
+ const next = { ...scopes };
1658
+ for (const plane of owns) next[plane] = "layer";
1659
+ this.manifestCache = {
1660
+ ...cache,
1661
+ scopes: next
1662
+ };
1663
+ }
1664
+ absorbMutation(meta, owns) {
1665
+ this.flipScopes(owns);
1666
+ const delta = meta.cacheDelta;
1667
+ if (delta) this.manifestFloorVersion = Math.max(this.manifestFloorVersion, delta.docVersion);
1668
+ this.inflightManifest = null;
1669
+ if (!this.manifestCache) return;
1670
+ if (delta) {
1671
+ if (delta.docVersion <= this.manifestCache.docVersion) return;
1672
+ if (delta.previousDocVersion !== this.manifestCache.docVersion) {
1673
+ this.manifestCache = null;
1674
+ return;
1675
+ }
1676
+ }
1677
+ const byPageObjectNumber = new Map(this.manifestCache.pages.map((page) => [page.state.pageObjectNumber, page]));
1678
+ for (const pageState of meta.affectedPages) {
1679
+ const existing = byPageObjectNumber.get(pageState.pageObjectNumber);
1680
+ if (existing) byPageObjectNumber.set(pageState.pageObjectNumber, {
1681
+ ...existing,
1682
+ state: pageState
1683
+ });
1684
+ }
1685
+ if (delta) for (const page of delta.pages) {
1686
+ const existing = byPageObjectNumber.get(page.pageObjectNumber);
1687
+ if (existing) byPageObjectNumber.set(page.pageObjectNumber, {
1688
+ ...existing,
1689
+ cache: page.cache
1690
+ });
1691
+ }
1692
+ this.manifestCache = {
1693
+ ...this.manifestCache,
1694
+ docVersion: delta?.docVersion ?? this.manifestCache.docVersion,
1695
+ pages: Array.from(byPageObjectNumber.values()).sort((a, b) => a.state.pageObjectNumber - b.state.pageObjectNumber)
1696
+ };
1697
+ }
1698
+ /**
1699
+ * Patch the cached manifest after a set-preserving page-structure op (move,
1700
+ * rotate). Both are purely structural: they advance `docVersion` (so leaf
1701
+ * URLs re-resolve) and `layoutVersion` (so the /layout leaf re-fetches),
1702
+ * but leave every per-page pin untouched — a rotate renders the SAME
1703
+ * normalized bitmaps, a move the same pages. We raise the floor
1704
+ * unconditionally and, when our cache is exactly one version behind,
1705
+ * advance it in place; otherwise we drop it and refetch lazily.
1706
+ */
1707
+ absorbPageStructure(cache) {
1708
+ this.flipScopes(["layout"]);
1709
+ this.manifestFloorVersion = Math.max(this.manifestFloorVersion, cache.docVersion);
1710
+ this.inflightManifest = null;
1711
+ if (!this.manifestCache) return;
1712
+ if (cache.docVersion <= this.manifestCache.docVersion) return;
1713
+ if (cache.previousDocVersion !== this.manifestCache.docVersion) {
1714
+ this.manifestCache = null;
1715
+ return;
1716
+ }
1717
+ this.manifestCache = {
1718
+ ...this.manifestCache,
1719
+ docVersion: cache.docVersion,
1720
+ layoutVersion: cache.layoutVersion
1721
+ };
1722
+ }
1723
+ /**
1724
+ * Patch the cached manifest after a page delete: the shared structural
1725
+ * advance plus dropping the deleted pages' manifest rows, so no leaf URL
1726
+ * for a retired PON can be built from the cache (a stale request would
1727
+ * 404 anyway — this keeps the failure local and instant).
1728
+ */
1729
+ absorbPageDelete(cache, deletedPages) {
1730
+ this.flipScopes([
1731
+ "layout",
1732
+ "content",
1733
+ "annotations"
1734
+ ]);
1735
+ this.manifestFloorVersion = Math.max(this.manifestFloorVersion, cache.docVersion);
1736
+ this.inflightManifest = null;
1737
+ if (!this.manifestCache) return;
1738
+ if (cache.docVersion <= this.manifestCache.docVersion) return;
1739
+ if (cache.previousDocVersion !== this.manifestCache.docVersion) {
1740
+ this.manifestCache = null;
1741
+ return;
1742
+ }
1743
+ const deleted = new Set(deletedPages);
1744
+ this.manifestCache = {
1745
+ ...this.manifestCache,
1746
+ docVersion: cache.docVersion,
1747
+ layoutVersion: cache.layoutVersion,
1748
+ pages: this.manifestCache.pages.filter((page) => !deleted.has(page.state.pageObjectNumber))
1749
+ };
1750
+ }
1751
+ /**
1752
+ * Patch the cached manifest after a metadata write. Symmetric with
1753
+ * {@link absorbPageMove}: a metadata edit advances `docVersion` (so leaf
1754
+ * URLs re-resolve) and `metadataVersion` (so the /metadata leaf re-fetches),
1755
+ * but leaves `layoutVersion` and every per-page pin untouched. Raise the
1756
+ * floor unconditionally and, when our cache is exactly one version behind,
1757
+ * advance it in place; otherwise drop it and refetch lazily.
1758
+ */
1759
+ absorbMetadata(cache) {
1760
+ this.flipScopes(["metadata"]);
1761
+ this.manifestFloorVersion = Math.max(this.manifestFloorVersion, cache.docVersion);
1762
+ this.inflightManifest = null;
1763
+ if (!this.manifestCache) return;
1764
+ if (cache.docVersion <= this.manifestCache.docVersion) return;
1765
+ if (cache.previousDocVersion !== this.manifestCache.docVersion) {
1766
+ this.manifestCache = null;
1767
+ return;
1768
+ }
1769
+ this.manifestCache = {
1770
+ ...this.manifestCache,
1771
+ docVersion: cache.docVersion,
1772
+ metadataVersion: cache.metadataVersion
1773
+ };
1774
+ }
1775
+ /**
1776
+ * Patch the cached manifest after an attachment create/delete. Symmetric
1777
+ * with {@link absorbMetadata}: an attachment write advances `docVersion`
1778
+ * (so leaf URLs re-resolve) and `attachmentsVersion` (so the /attachments
1779
+ * listing and /attachment-files leaves re-fetch), but leaves
1780
+ * `layoutVersion` and every per-page pin untouched. Raise the floor
1781
+ * unconditionally and, when our cache is exactly one version behind,
1782
+ * advance it in place; otherwise drop it and refetch lazily.
1783
+ */
1784
+ absorbAttachments(cache) {
1785
+ this.flipScopes(["attachments"]);
1786
+ this.manifestFloorVersion = Math.max(this.manifestFloorVersion, cache.docVersion);
1787
+ this.inflightManifest = null;
1788
+ if (!this.manifestCache) return;
1789
+ if (cache.docVersion <= this.manifestCache.docVersion) return;
1790
+ if (cache.previousDocVersion !== this.manifestCache.docVersion) {
1791
+ this.manifestCache = null;
1792
+ return;
1793
+ }
1794
+ this.manifestCache = {
1795
+ ...this.manifestCache,
1796
+ docVersion: cache.docVersion,
1797
+ attachmentsVersion: cache.attachmentsVersion
1798
+ };
1799
+ }
1800
+ startManifestFetch(opts) {
1801
+ const ctrl = new AbortController();
1802
+ const promise = this.fetchManifest(ctrl.signal, opts);
1803
+ this.inflightManifest = promise;
1804
+ promise.then((manifest) => {
1805
+ if (manifest.docVersion >= this.manifestFloorVersion && (!this.manifestCache || manifest.docVersion >= this.manifestCache.docVersion)) this.manifestCache = manifest;
1806
+ }).catch(() => void 0).finally(() => {
1807
+ if (this.inflightManifest === promise) this.inflightManifest = null;
1808
+ });
1809
+ return promise;
1810
+ }
1811
+ async fetchManifest(signal, opts) {
1812
+ if (this.closed) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocNotOpen, `document ${this.id} is closed`);
1813
+ const head = opts.allowInitialHead ? this.consumeInitialHead() : null;
1814
+ if (head) try {
1815
+ return await this.fetchManifestForHead(head, signal);
1816
+ } catch (err) {
1817
+ if (!_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw err;
1818
+ }
1819
+ const freshHead = await this.http.getJson(_embedpdf_engine_core_wire.wirePaths.layerHead(this.id, this.layerName), (raw) => _embedpdf_engine_core_wire.DocumentHeadSchema.parse(raw), signal);
1820
+ return this.fetchManifestForHead(freshHead, signal);
1821
+ }
1822
+ consumeInitialHead() {
1823
+ const head = this.pendingInitialHead;
1824
+ this.pendingInitialHead = null;
1825
+ return head;
1826
+ }
1827
+ fetchManifestForHead(head, signal) {
1828
+ return this.http.getJson(_embedpdf_engine_core_wire.wirePaths.layerManifest(this.id, this.layerName, head.docVersion), (raw) => _embedpdf_engine_core_wire.DocumentManifestSchema.parse(raw), signal);
1829
+ }
1830
+ close() {
1831
+ if (this.closed) return _embedpdf_engine_core_runtime.AbortablePromise.resolveValue(void 0);
1832
+ this.closed = true;
1833
+ this.sseClient?.close();
1834
+ this.sseClient = null;
1835
+ this.manifestCache = null;
1836
+ this.inflightManifest = null;
1837
+ return _embedpdf_engine_core_runtime.AbortablePromise.resolveValue(void 0);
1838
+ }
1839
+ retainRemoteStream() {
1840
+ this.sseSubscribers += 1;
1841
+ if (this.sseSubscribers > 1 || this.closed || this.sseClient) return;
1842
+ this.sseClient = new SseClient({
1843
+ http: this.http,
1844
+ path: _embedpdf_engine_core_wire.wirePaths.layerEvents(this.id, this.layerName),
1845
+ initialCursor: this.hub.lastServerId() ?? this.manifestCache?.auditHead ?? null,
1846
+ onRow: (row) => {
1847
+ const event = auditRowToEvent(row, this.sessionId);
1848
+ if (!event) return;
1849
+ this.absorbRemoteEvent(event);
1850
+ this.hub.publish(event);
1851
+ },
1852
+ onFullRefresh: () => {
1853
+ this.manifestCache = null;
1854
+ this.inflightManifest = null;
1855
+ },
1856
+ onAuthLost: () => {
1857
+ this.sseClient = null;
1858
+ }
1859
+ });
1860
+ this.sseClient.open();
1861
+ }
1862
+ releaseRemoteStream() {
1863
+ this.sseSubscribers = Math.max(0, this.sseSubscribers - 1);
1864
+ if (this.sseSubscribers === 0 && this.sseClient) {
1865
+ this.sseClient.close();
1866
+ this.sseClient = null;
1867
+ }
1868
+ }
1869
+ /** Patch the cached manifest from a REMOTE event's coherence pins — the
1870
+ * same absorb rails local mutations use, so reads stay warm no matter
1871
+ * whose hand caused the change. */
1872
+ absorbRemoteEvent(event) {
1873
+ switch (event.type) {
1874
+ case "annotation.created":
1875
+ case "annotation.updated":
1876
+ case "annotation.deleted":
1877
+ case "annotation.moved":
1878
+ this.absorbMutation(event.meta, ["annotations"]);
1879
+ return;
1880
+ case "pages.moved":
1881
+ case "pages.rotated":
1882
+ if (event.cache) this.absorbPageStructure(event.cache);
1883
+ return;
1884
+ case "pages.deleted":
1885
+ if (event.cache) this.absorbPageDelete(event.cache, event.pageObjectNumbers);
1886
+ return;
1887
+ case "metadata.updated":
1888
+ if (event.cache) this.absorbMetadata(event.cache);
1889
+ return;
1890
+ case "attachment.created":
1891
+ case "attachment.deleted":
1892
+ if (event.cache) this.absorbAttachments(event.cache);
1893
+ return;
1894
+ case "form.valueChanged":
1895
+ case "form.imported":
1896
+ case "form.repaired":
1897
+ case "form.fieldCreated":
1898
+ case "form.fieldUpdated":
1899
+ case "form.fieldDeleted":
1900
+ case "form.widgetAttached":
1901
+ case "form.widgetDetached":
1902
+ this.absorbMutation(event.meta, ["annotations"]);
1903
+ return;
1904
+ case "form.effectsApplied":
1905
+ if (event.meta) this.absorbMutation(event.meta, ["annotations"]);
1906
+ return;
1907
+ case "pages.flattened":
1908
+ if (event.meta) this.absorbMutation(event.meta, ["content", "annotations"]);
1909
+ return;
1910
+ case "redaction.applied":
1911
+ if (event.meta) this.absorbMutation(event.meta, ["content", "annotations"]);
1912
+ return;
1913
+ }
1914
+ }
1915
+ };
1916
+ function fallbackUnknownHead(id) {
1917
+ return {
1918
+ id,
1919
+ baseSha: "",
1920
+ storageSizeBytes: 0,
1921
+ docVersion: 1,
1922
+ state: "ready",
1923
+ encryption: {
1924
+ state: "unknown",
1925
+ requiresPassword: null
1926
+ },
1927
+ permissions: {
1928
+ known: false,
1929
+ bits: null,
1930
+ allAllowed: null,
1931
+ openedAs: null,
1932
+ securityHandlerRevision: null,
1933
+ canUpgradeToOwner: false
1934
+ },
1935
+ access: {
1936
+ required: true,
1937
+ reasons: ["permissions-unknown"],
1938
+ endpoint: _embedpdf_engine_core_wire.wirePaths.access
1939
+ }
1940
+ };
1941
+ }
1942
+ function awaitSignal(promise, signal) {
1943
+ if (signal.aborted) return Promise.reject(new _embedpdf_engine_core_runtime.AbortError(signal.reason));
1944
+ return new Promise((resolve, reject) => {
1945
+ const onAbort = () => {
1946
+ reject(new _embedpdf_engine_core_runtime.AbortError(signal.reason));
1947
+ };
1948
+ signal.addEventListener("abort", onAbort, { once: true });
1949
+ promise.then((value) => {
1950
+ signal.removeEventListener("abort", onAbort);
1951
+ resolve(value);
1952
+ }, (reason) => {
1953
+ signal.removeEventListener("abort", onAbort);
1954
+ reject(reason);
1955
+ });
1956
+ });
1957
+ }
1958
+ //#endregion
1959
+ //#region src/transport/HttpClient.ts
1960
+ var HttpClient = class HttpClient {
1961
+ constructor(opts) {
1962
+ _defineProperty(this, "baseUrl", void 0);
1963
+ _defineProperty(this, "tokenFn", void 0);
1964
+ _defineProperty(this, "fetchFn", void 0);
1965
+ _defineProperty(this, "sessionId", void 0);
1966
+ _defineProperty(this, "cdnBinding", null);
1967
+ this.baseUrl = opts.baseUrl.replace(/\/$/, "");
1968
+ const token = opts.token;
1969
+ this.tokenFn = token === void 0 ? null : typeof token === "function" ? token : () => token;
1970
+ this.sessionId = opts.sessionId ?? null;
1971
+ this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
1972
+ }
1973
+ /**
1974
+ * Return a clone of this client bound to a different bearer. Used
1975
+ * by `CloudEngine.open` to mint a per-handle client carrying the
1976
+ * per-open token, so each opened document's RPCs go out under
1977
+ * the right authorization without disturbing the engine-level
1978
+ * token.
1979
+ */
1980
+ withToken(token) {
1981
+ return new HttpClient({
1982
+ baseUrl: this.baseUrl,
1983
+ token,
1984
+ ...this.sessionId ? { sessionId: this.sessionId } : {},
1985
+ fetch: this.fetchFn
1986
+ });
1987
+ }
1988
+ /**
1989
+ * Raw streaming GET (the SSE channel). Same auth/session headers and
1990
+ * error mapping as every other request; the caller owns the body stream.
1991
+ */
1992
+ async stream(path, headers, signal) {
1993
+ return this.request(path, {
1994
+ method: "GET",
1995
+ headers: new Headers({
1996
+ Accept: "text/event-stream",
1997
+ ...headers
1998
+ }),
1999
+ signal
2000
+ });
2001
+ }
2002
+ /**
2003
+ * Install (or clear) the per-session CDN binding. Pass `null` to
2004
+ * route everything to origin again — useful on session close or
2005
+ * when an adapter swap happens mid-session (rare).
2006
+ *
2007
+ * The HttpClient stays dumb about WHEN to do this; the cloud
2008
+ * document handle pushes the binding in after /access succeeds.
2009
+ *
2010
+ * Side effect: in the browser, signedCookies are written to
2011
+ * `document.cookie` once per setCdnAccess call so the CDN edge
2012
+ * receives them on subsequent fetches. Node side ignores cookies
2013
+ * for now (cookie jar wiring deferred until needed for tests).
2014
+ */
2015
+ setCdnAccess(binding) {
2016
+ this.cdnBinding = binding;
2017
+ if (binding?.cdn?.signedCookies && typeof globalThis.document !== "undefined") for (const cookie of binding.cdn.signedCookies) {
2018
+ const parts = [`${cookie.name}=${cookie.value}`];
2019
+ parts.push(`Path=${cookie.path ?? "/"}`);
2020
+ if (cookie.domain) parts.push(`Domain=${cookie.domain}`);
2021
+ if (cookie.expires) parts.push(`Expires=${(/* @__PURE__ */ new Date(cookie.expires * 1e3)).toUTCString()}`);
2022
+ parts.push("SameSite=None", "Secure");
2023
+ globalThis.document.cookie = parts.join("; ");
2024
+ }
2025
+ }
2026
+ /** Current CDN binding (or null). Exposed for diagnostics/tests. */
2027
+ get currentCdnBinding() {
2028
+ return this.cdnBinding;
2029
+ }
2030
+ absoluteUrl(path) {
2031
+ return `${this.baseUrl}${path}`;
2032
+ }
2033
+ async getBlob(path, signal) {
2034
+ const res = await this.request(path, {
2035
+ method: "GET",
2036
+ signal
2037
+ });
2038
+ if (!res.ok) await this.throwFromBody(res);
2039
+ return await res.blob();
2040
+ }
2041
+ async getBytes(path, signal) {
2042
+ const res = await this.request(path, {
2043
+ method: "GET",
2044
+ headers: new Headers({ Accept: "application/pdf" }),
2045
+ signal
2046
+ });
2047
+ if (!res.ok) await this.throwFromBody(res);
2048
+ return new Uint8Array(await res.arrayBuffer());
2049
+ }
2050
+ /**
2051
+ * Resolve the current bearer token. Awaits the user-supplied
2052
+ * token factory if it returns a promise. Used by the cloud
2053
+ * engine to read doc-scoped claims **without** verifying — the
2054
+ * server is the verifier of record. Throws when no token is
2055
+ * configured.
2056
+ */
2057
+ async currentToken() {
2058
+ if (!this.tokenFn) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "http client has no token configured");
2059
+ return await this.tokenFn();
2060
+ }
2061
+ async getJson(path, parser, signal) {
2062
+ const res = await this.request(path, {
2063
+ method: "GET",
2064
+ signal
2065
+ });
2066
+ return await this.parseJsonResponse(res, parser);
2067
+ }
2068
+ /**
2069
+ * GET a versioned URL with transparent stale-version recovery. If
2070
+ * the server returns 404 (the well-known "this version is gone"
2071
+ * signal for content-addressed reads), call `onStaleVersion` (which
2072
+ * the caller wires to a manifest refresh), then re-resolve `buildPath`
2073
+ * and retry the GET exactly once. Any non-404 error propagates.
2074
+ *
2075
+ * The retry budget is hard-capped at 1: after a refresh, if the
2076
+ * server's idea of the current version is still ahead of the
2077
+ * client's view, the second request fails normally — this matches
2078
+ * the CDN cache invariant ("a versioned URL's bytes are immutable
2079
+ * for its lifetime").
2080
+ */
2081
+ async getJsonWithRefresh(buildPath, parser, onStaleVersion, signal) {
2082
+ const initialPath = await buildPath(signal);
2083
+ try {
2084
+ return await this.getJson(initialPath, parser, signal);
2085
+ } catch (err) {
2086
+ if (!_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw err;
2087
+ await onStaleVersion(signal);
2088
+ const retryPath = await buildPath(signal);
2089
+ return await this.getJson(retryPath, parser, signal);
2090
+ }
2091
+ }
2092
+ /**
2093
+ * GET a `multipart/form-data` body and parse it with the platform's
2094
+ * `Response.formData()` (works in browsers and undici). Text parts come
2095
+ * back as strings, parts with a filename as `Blob`s.
2096
+ */
2097
+ async getFormData(path, signal) {
2098
+ const res = await this.request(path, {
2099
+ method: "GET",
2100
+ headers: new Headers({ Accept: "multipart/form-data" }),
2101
+ signal
2102
+ });
2103
+ if (!res.ok) await this.throwFromBody(res);
2104
+ return await res.formData();
2105
+ }
2106
+ /**
2107
+ * `getFormData` with the same single-retry stale-version recovery as
2108
+ * {@link getJsonWithRefresh}: a 404 on a versioned URL triggers
2109
+ * `onStaleVersion` (a manifest refresh) and one re-resolve + retry.
2110
+ */
2111
+ async getFormDataWithRefresh(buildPath, onStaleVersion, signal) {
2112
+ const initialPath = await buildPath(signal);
2113
+ try {
2114
+ return await this.getFormData(initialPath, signal);
2115
+ } catch (err) {
2116
+ if (!_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw err;
2117
+ await onStaleVersion(signal);
2118
+ const retryPath = await buildPath(signal);
2119
+ return await this.getFormData(retryPath, signal);
2120
+ }
2121
+ }
2122
+ /**
2123
+ * `getBlob` with the same single-retry stale-version recovery as
2124
+ * {@link getJsonWithRefresh}: a 404 on a versioned URL triggers
2125
+ * `onStaleVersion` (a manifest refresh) and one re-resolve + retry.
2126
+ * Render blobs ride this rail: a scope flip re-resolves the
2127
+ * doc-vs-layer path family instead of failing on a stale URL.
2128
+ */
2129
+ async getBlobWithRefresh(buildPath, onStaleVersion, signal) {
2130
+ const initialPath = await buildPath(signal);
2131
+ try {
2132
+ return await this.getBlob(initialPath, signal);
2133
+ } catch (err) {
2134
+ if (!_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw err;
2135
+ await onStaleVersion(signal);
2136
+ const retryPath = await buildPath(signal);
2137
+ return await this.getBlob(retryPath, signal);
2138
+ }
2139
+ }
2140
+ /** GET raw bytes plus the response headers (attachment-file leaves). */
2141
+ async getFile(path, signal) {
2142
+ const res = await this.request(path, {
2143
+ method: "GET",
2144
+ signal
2145
+ });
2146
+ if (!res.ok) await this.throwFromBody(res);
2147
+ return {
2148
+ bytes: new Uint8Array(await res.arrayBuffer()),
2149
+ headers: res.headers
2150
+ };
2151
+ }
2152
+ /**
2153
+ * `getFile` with the same single-retry stale-version recovery as
2154
+ * {@link getJsonWithRefresh}: a 404 on a versioned URL triggers
2155
+ * `onStaleVersion` (a manifest refresh) and one re-resolve + retry.
2156
+ */
2157
+ async getFileWithRefresh(buildPath, onStaleVersion, signal) {
2158
+ const initialPath = await buildPath(signal);
2159
+ try {
2160
+ return await this.getFile(initialPath, signal);
2161
+ } catch (err) {
2162
+ if (!_embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.NotFound)) throw err;
2163
+ await onStaleVersion(signal);
2164
+ const retryPath = await buildPath(signal);
2165
+ return await this.getFile(retryPath, signal);
2166
+ }
2167
+ }
2168
+ async postMultipartJson(path, body, parser, signal) {
2169
+ const res = await this.request(path, {
2170
+ method: "POST",
2171
+ body,
2172
+ signal
2173
+ });
2174
+ return await this.parseJsonResponse(res, parser);
2175
+ }
2176
+ /** PATCH counterpart of {@link postMultipartJson} (binary-carrying mutations). */
2177
+ async patchMultipartJson(path, body, parser, signal) {
2178
+ const res = await this.request(path, {
2179
+ method: "PATCH",
2180
+ body,
2181
+ signal
2182
+ });
2183
+ return await this.parseJsonResponse(res, parser);
2184
+ }
2185
+ async postJson(path, body, parser, signal) {
2186
+ const res = await this.requestJson(path, "POST", body, signal);
2187
+ return await this.parseJsonResponse(res, parser);
2188
+ }
2189
+ /** POST a raw binary body (FDF/XFDF import) and parse the JSON response. */
2190
+ async postBytesJson(path, bytes, contentType, parser, signal) {
2191
+ const headers = new Headers({ "Content-Type": contentType });
2192
+ const body = new Uint8Array(bytes).buffer;
2193
+ const res = await this.request(path, {
2194
+ method: "POST",
2195
+ body,
2196
+ headers,
2197
+ signal
2198
+ });
2199
+ return await this.parseJsonResponse(res, parser);
2200
+ }
2201
+ async patchJson(path, body, parser, signal) {
2202
+ const res = await this.requestJson(path, "PATCH", body, signal);
2203
+ return await this.parseJsonResponse(res, parser);
2204
+ }
2205
+ async deleteJson(path, parser, signal) {
2206
+ const res = await this.request(path, {
2207
+ method: "DELETE",
2208
+ signal
2209
+ });
2210
+ return await this.parseJsonResponse(res, parser);
2211
+ }
2212
+ async deleteEmpty(path, signal) {
2213
+ const res = await this.request(path, {
2214
+ method: "DELETE",
2215
+ signal
2216
+ });
2217
+ if (res.status === 204) return;
2218
+ if (!res.ok) await this.throwFromBody(res);
2219
+ }
2220
+ async postEmpty(path, body, signal) {
2221
+ const res = await this.requestJson(path, "POST", body, signal);
2222
+ if (res.status === 204) return;
2223
+ if (!res.ok) await this.throwFromBody(res);
2224
+ }
2225
+ requestJson(path, method, body, signal) {
2226
+ const headers = new Headers({ "Content-Type": "application/json" });
2227
+ return this.request(path, {
2228
+ method,
2229
+ body: JSON.stringify(body ?? {}),
2230
+ headers,
2231
+ signal
2232
+ });
2233
+ }
2234
+ async request(path, init) {
2235
+ const { url, extraHeader } = this.resolveOutgoing(path);
2236
+ const headers = new Headers(init.headers ?? {});
2237
+ if (this.tokenFn) {
2238
+ const token = await this.tokenFn();
2239
+ headers.set("Authorization", `Bearer ${token}`);
2240
+ }
2241
+ if (!headers.has("Accept")) headers.set("Accept", "application/json");
2242
+ if (this.sessionId) headers.set("X-Engine-Session-Id", this.sessionId);
2243
+ if (extraHeader) headers.set(extraHeader.name, extraHeader.value);
2244
+ try {
2245
+ return await this.fetchFn(url, {
2246
+ ...init,
2247
+ headers
2248
+ });
2249
+ } catch (err) {
2250
+ if (init.signal?.aborted) throw new _embedpdf_engine_core_runtime.AbortError(init.signal.reason ?? err);
2251
+ if (err?.name === "AbortError") throw new _embedpdf_engine_core_runtime.AbortError(err.message);
2252
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Network, `network error: ${err?.message ?? err}`, { cause: err });
2253
+ }
2254
+ }
2255
+ async parseJsonResponse(res, parser) {
2256
+ if (!res.ok) await this.throwFromBody(res);
2257
+ const json = await res.json().catch(() => ({}));
2258
+ try {
2259
+ return parser(json);
2260
+ } catch (err) {
2261
+ throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.WireFormat, `unexpected response shape: ${err?.message ?? err}`, { cause: err });
2262
+ }
2263
+ }
2264
+ /**
2265
+ * Decide where this request actually goes. With no CDN binding (or
2266
+ * binding's `cdn` is null) every request stays on origin. Otherwise
2267
+ * `applyCdnAccess` decides per-request based on the resource the
2268
+ * path resolves to (and whether the caller's scope granted CDN
2269
+ * access to it).
2270
+ */
2271
+ resolveOutgoing(path) {
2272
+ if (!this.cdnBinding || !this.cdnBinding.cdn) return {
2273
+ url: `${this.baseUrl}${path}`,
2274
+ extraHeader: null
2275
+ };
2276
+ const applied = (0, _embedpdf_engine_core_wire.applyCdnAccess)({
2277
+ path,
2278
+ originUrl: this.baseUrl,
2279
+ docId: this.cdnBinding.docId,
2280
+ layerName: this.cdnBinding.layerName,
2281
+ cdn: this.cdnBinding.cdn
2282
+ });
2283
+ return {
2284
+ url: applied.url,
2285
+ extraHeader: applied.routedToCdn && applied.authHeader ? applied.authHeader : null
2286
+ };
2287
+ }
2288
+ async throwFromBody(res) {
2289
+ let payload;
2290
+ try {
2291
+ payload = await res.json();
2292
+ } catch {
2293
+ payload = null;
2294
+ }
2295
+ const code = mapStatusToCode(res.status);
2296
+ if (payload && typeof payload === "object" && "error" in payload) {
2297
+ const inner = payload.error;
2298
+ const parsed = trySerialized(inner);
2299
+ if (parsed) throw new _embedpdf_engine_core_runtime.EngineError(parsed.code, parsed.message, { details: parsed.details });
2300
+ throw new _embedpdf_engine_core_runtime.EngineError(code, typeof inner === "object" && inner && "message" in inner ? String(inner.message) : `HTTP ${res.status}`, { details: { status: res.status } });
2301
+ }
2302
+ throw new _embedpdf_engine_core_runtime.EngineError(code, `HTTP ${res.status}`, { details: { status: res.status } });
2303
+ }
2304
+ };
2305
+ function mapStatusToCode(status) {
2306
+ if (status === 401) return _embedpdf_engine_core_runtime.EngineErrorCode.Unauthenticated;
2307
+ if (status === 403) return _embedpdf_engine_core_runtime.EngineErrorCode.Forbidden;
2308
+ if (status === 409) return _embedpdf_engine_core_runtime.EngineErrorCode.WeakAnnotationSessionConflict;
2309
+ if (status === 404) return _embedpdf_engine_core_runtime.EngineErrorCode.NotFound;
2310
+ if (status === 422) return _embedpdf_engine_core_runtime.EngineErrorCode.DocOpenFailed;
2311
+ if (status === 499) return _embedpdf_engine_core_runtime.EngineErrorCode.Aborted;
2312
+ if (status === 400) return _embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg;
2313
+ return _embedpdf_engine_core_runtime.EngineErrorCode.Unknown;
2314
+ }
2315
+ function trySerialized(value) {
2316
+ const result = _embedpdf_engine_core_wire.EngineErrorPayloadSchema.safeParse(value);
2317
+ return result.success ? result.data : null;
2318
+ }
2319
+ //#endregion
2320
+ //#region src/CloudEngine.ts
2321
+ /**
2322
+ * Cloud engine: speaks the same Engine interface as @embedpdf/engine
2323
+ * but routes everything through HTTPS to a remote @cloudpdf/server (or
2324
+ * CloudPDF SaaS). Identical observable contract; only the transport differs.
2325
+ */
2326
+ var CloudEngine = class CloudEngine {
2327
+ static fromOptions(opts) {
2328
+ const sessionId = `cloud:${(0, _embedpdf_engine_services.generateUuid)()}`;
2329
+ return new CloudEngine(new HttpClient({
2330
+ ...opts,
2331
+ sessionId
2332
+ }), sessionId);
2333
+ }
2334
+ constructor(http, sessionId) {
2335
+ this.http = http;
2336
+ this.sessionId = sessionId;
2337
+ _defineProperty(this, "destroyed", false);
2338
+ }
2339
+ open(input, options) {
2340
+ if (this.destroyed) return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.RuntimeUnavailable, "engine destroyed"));
2341
+ options?.scope;
2342
+ options?.identity;
2343
+ const effectivePassword = options && "password" in options ? options.password ?? null : input.password ?? null;
2344
+ if (input.kind === "token") {
2345
+ const tokenSource = input.token;
2346
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
2347
+ const docHttp = this.http.withToken(tokenSource);
2348
+ const token = await docHttp.currentToken();
2349
+ const claims = decodeUnverifiedClaims(token);
2350
+ const docId = claims.doc_id;
2351
+ if (typeof docId !== "string" || docId.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "cloud engine: token has no doc_id claim — mint a doc-scoped JWT");
2352
+ const layerName = typeof claims.layer_name === "string" && claims.layer_name.length > 0 ? claims.layer_name : _embedpdf_engine_core_wire.DEFAULT_LAYER_NAME;
2353
+ const head = await docHttp.getJson(_embedpdf_engine_core_wire.wirePaths.layerHead(docId, layerName), (raw) => _embedpdf_engine_core_wire.DocumentHeadSchema.parse(raw), signal);
2354
+ const handle = new CloudDocumentHandle(docHttp, head.id, layerName, head, token, this.sessionId);
2355
+ await maybeAutoEstablishAccess(handle, head, signal, effectivePassword);
2356
+ return handle;
2357
+ });
2358
+ }
2359
+ if (input.kind === "id") {
2360
+ const id = input.id;
2361
+ const docHttp = input.token ? this.http.withToken(input.token) : this.http;
2362
+ return _embedpdf_engine_core_runtime.AbortablePromise.run(async (signal) => {
2363
+ let layerName = input.layerName ?? _embedpdf_engine_core_wire.DEFAULT_LAYER_NAME;
2364
+ let resolvedToken = null;
2365
+ try {
2366
+ resolvedToken = await docHttp.currentToken();
2367
+ } catch {
2368
+ resolvedToken = null;
2369
+ }
2370
+ if (!input.layerName && resolvedToken) {
2371
+ const claims = decodeUnverifiedClaims(resolvedToken);
2372
+ if (typeof claims.layer_name === "string" && claims.layer_name.length > 0) layerName = claims.layer_name;
2373
+ }
2374
+ const head = await docHttp.getJson(_embedpdf_engine_core_wire.wirePaths.layerHead(id, layerName), (raw) => _embedpdf_engine_core_wire.DocumentHeadSchema.parse(raw), signal);
2375
+ const handle = new CloudDocumentHandle(docHttp, head.id, layerName, head, resolvedToken, this.sessionId);
2376
+ await maybeAutoEstablishAccess(handle, head, signal, effectivePassword);
2377
+ return handle;
2378
+ });
2379
+ }
2380
+ return _embedpdf_engine_core_runtime.AbortablePromise.rejectReason(new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `cloud engine supports OpenInput.kind === 'token' or 'id' (got '${input.kind}')`));
2381
+ }
2382
+ destroy() {
2383
+ if (this.destroyed) return _embedpdf_engine_core_runtime.AbortablePromise.resolveValue(void 0);
2384
+ this.destroyed = true;
2385
+ return _embedpdf_engine_core_runtime.AbortablePromise.resolveValue(void 0);
2386
+ }
2387
+ };
2388
+ /**
2389
+ * Post-/head access establishment, in two layers:
2390
+ *
2391
+ * 1. **Supplied password** — a password given at open is always TRIED
2392
+ * (PDFium parity: the local engine feeds it to the loader no matter
2393
+ * what). Two triggers, because `/head` only carries the `password`
2394
+ * reason when a password is needed to READ:
2395
+ * - `reasons` has `'password'` → required-to-read case
2396
+ * - `head.permissions.canUpgradeToOwner` → permission-only encrypted
2397
+ * doc; the password can still upgrade to owner. Skipping this arm
2398
+ * would silently drop supplied OWNER passwords — and diverge from
2399
+ * the local engine on identical input.
2400
+ * Outcomes follow "a password failure may only block what the password
2401
+ * was needed for": a rejection in the required case leaves the handle
2402
+ * locked (the caller's prompt takes over, showing "incorrect"); in the
2403
+ * upgrade case the document stays readable and the failure is
2404
+ * non-fatal. The one /access POST also installs the CDN binding, so a
2405
+ * successful unlock covers the `password + cdn` combined case.
2406
+ *
2407
+ * 2. **CDN-only** — unchanged: `/access` without a password, transparently.
2408
+ * A required password with NO supplied password never auto-calls; the
2409
+ * dev prompts and `unlock()` runs the same POST.
2410
+ *
2411
+ * Non-password /access failures never break `open()` (the first real
2412
+ * request still tries origin, where the JWT check produces a regular
2413
+ * Forbidden) — but they are also never mistaken for a wrong password:
2414
+ * only a DocPasswordRequired/Incorrect rejection means "wrong password".
2415
+ */
2416
+ async function maybeAutoEstablishAccess(handle, head, signal, password) {
2417
+ const reasons = new Set(head.access.reasons);
2418
+ const security = handle.security;
2419
+ if (password != null && (reasons.has("password") || head.permissions.canUpgradeToOwner)) {
2420
+ if (await settleLinked(security.unlock({
2421
+ password,
2422
+ mode: "any"
2423
+ }), signal)) return;
2424
+ if (reasons.has("password")) return;
2425
+ }
2426
+ if (!head.access.required) return;
2427
+ if (reasons.has("password")) return;
2428
+ if (!reasons.has("cdn")) return;
2429
+ await settleLinked(security.establishAccess(), signal);
2430
+ }
2431
+ /** Await an /access attempt with open()-abort linkage. Returns whether it
2432
+ * succeeded; failures are contained (see maybeAutoEstablishAccess). */
2433
+ async function settleLinked(pending, signal) {
2434
+ const onAbort = () => pending.abort(signal.reason ?? /* @__PURE__ */ new Error("aborted"));
2435
+ if (signal.aborted) onAbort();
2436
+ else signal.addEventListener("abort", onAbort, { once: true });
2437
+ try {
2438
+ await pending;
2439
+ return true;
2440
+ } catch {
2441
+ return false;
2442
+ } finally {
2443
+ signal.removeEventListener("abort", onAbort);
2444
+ }
2445
+ }
2446
+ //#endregion
2447
+ //#region src/index.ts
2448
+ function createCloudEngine(opts) {
2449
+ return CloudEngine.fromOptions(opts);
2450
+ }
2451
+ /**
2452
+ * Create a cloud {@link CloudEngine} — the drop-in counterpart of
2453
+ * `localEngine()`, so swapping local for cloud is a one-import change.
2454
+ *
2455
+ * Synchronous and cheap: no WASM to compile, no worker to spawn — the engine
2456
+ * holds an HTTP client and nothing else. Like `localEngine()`, the returned
2457
+ * instance is yours (call `destroy()` when done, usually never for a
2458
+ * module-scope singleton); pass a thunk (`engine={() => cloudEngine(...)}`)
2459
+ * to let a `<Viewer>` own the lifetime instead.
2460
+ *
2461
+ * Note the deliberate asymmetry with `localEngine()`: there is no `fonts`
2462
+ * option. Fallback fonts are a SERVER policy on the cloud (`Engine.fonts` is
2463
+ * `undefined` cloud-side), so they cannot be configured from the client. This
2464
+ * is the local-vs-cloud split, surfaced in the API.
2465
+ *
2466
+ * ```ts
2467
+ * const engine = cloudEngine({ baseUrl: 'https://pdf.example.com', token });
2468
+ * <Viewer engine={engine} plugins={[stagePlugin(), renderPlugin()]} />
2469
+ * ```
2470
+ */
2471
+ function cloudEngine(opts) {
2472
+ return CloudEngine.fromOptions(opts);
2473
+ }
2474
+ //#endregion
2475
+ Object.defineProperty(exports, "AbortError", {
2476
+ enumerable: true,
2477
+ get: function() {
2478
+ return _embedpdf_engine_core_runtime.AbortError;
2479
+ }
2480
+ });
2481
+ Object.defineProperty(exports, "AbortablePromise", {
2482
+ enumerable: true,
2483
+ get: function() {
2484
+ return _embedpdf_engine_core_runtime.AbortablePromise;
2485
+ }
2486
+ });
2487
+ exports.CloudDocumentAnnotationsService = CloudDocumentAnnotationsService;
2488
+ exports.CloudDocumentHandle = CloudDocumentHandle;
2489
+ exports.CloudDocumentPagesService = CloudDocumentPagesService;
2490
+ exports.CloudEngine = CloudEngine;
2491
+ exports.CloudMetadataService = CloudMetadataService;
2492
+ exports.CloudPageAnnotationsService = CloudPageAnnotationsService;
2493
+ exports.CloudPageGeometryService = CloudPageGeometryService;
2494
+ exports.CloudPageHandle = CloudPageHandle;
2495
+ exports.CloudPageRenderService = CloudPageRenderService;
2496
+ Object.defineProperty(exports, "EngineError", {
2497
+ enumerable: true,
2498
+ get: function() {
2499
+ return _embedpdf_engine_core_runtime.EngineError;
2500
+ }
2501
+ });
2502
+ Object.defineProperty(exports, "EngineErrorCode", {
2503
+ enumerable: true,
2504
+ get: function() {
2505
+ return _embedpdf_engine_core_runtime.EngineErrorCode;
2506
+ }
2507
+ });
2508
+ exports.HttpClient = HttpClient;
2509
+ exports.cloudEngine = cloudEngine;
2510
+ exports.createCloudEngine = createCloudEngine;
2511
+ exports.decodeUnverifiedClaims = decodeUnverifiedClaims;
2512
+
2513
+ //# sourceMappingURL=index.cjs.map