@cloudpdf/engine 3.0.0-next.7 → 3.0.0-next.9
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 +151 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -1
- package/dist/index.d.ts +52 -1
- package/dist/index.js +152 -10
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortError, AbortablePromise, AbortablePromise as AbortablePromise$1, AnnotationAppearanceImageOptions, AnnotationAppearanceImagesResult, AnnotationAppearanceRenderOptions, AnnotationAppearancesResult, AnnotationCreateResult, AnnotationDeleteResult, AnnotationDraft, AnnotationListPageSnapshot, AnnotationListSnapshotAllPages, AnnotationMoveResult, AnnotationPatch, AnnotationRef, AnnotationUpdateResult, AttachmentContent, AttachmentsCache, DocumentAccessInfo, DocumentActionsService, DocumentAnnotationsService, DocumentAnnotationsService as DocumentAnnotationsService$1, DocumentAttachmentsService, DocumentCapabilities, DocumentEventStream, DocumentFormsService, DocumentHandle, DocumentHandle as DocumentHandle$1, DocumentIdentity, DocumentMetadata, DocumentPagesService, DocumentPagesService as DocumentPagesService$1, DocumentRedactionService, DocumentRenderService, DocumentSearchService, DocumentSecurityService, DocumentSecurityService as DocumentSecurityService$1, DocumentSecurityState, DocumentUnlockInput, DocumentUnlockResult, Engine, Engine as Engine$1, EngineError, EngineError as EngineError$1, EngineErrorCode, EngineFactory, MetadataCache, MetadataPatch, MetadataService, MetadataService as MetadataService$1, MetadataUpdateResult, MutationMeta, OpenInput, OpenInput as OpenInput$1, OpenInputShare, OpenOptions, OpenOptions as OpenOptions$1, PageAnnotationsService, PageAnnotationsService as PageAnnotationsService$1, PageDeleteResult, PageFlattenResult, PageFlattenUsage, PageGeometryService, PageGeometryService as PageGeometryService$1, PageGeometrySnapshot, PageHandle, PageHandle as PageHandle$1, PageImageHandle, PageImageOptions, PageListSnapshot, PageMoveResult, PageObjectNumber, PageRaster, PageRenderOptions, PageRenderService, PageRenderService as PageRenderService$1, PageRotateResult, PageRotation, PageStructureCache, PageTextService, PageTextService as PageTextService$1, PageTextSnapshot, PdfSaveMode, PdfSaveMode as PdfSaveMode$1, SearchRequest, SearchSlice, TokenSource, WeakAnnotationEditSession } from "@embedpdf/engine-core/runtime";
|
|
1
|
+
import { AbortError, AbortablePromise, AbortablePromise as AbortablePromise$1, AnnotationAppearanceImageOptions, AnnotationAppearanceImagesResult, AnnotationAppearanceRenderOptions, AnnotationAppearancesResult, AnnotationCreateResult, AnnotationDeleteResult, AnnotationDraft, AnnotationListPageSnapshot, AnnotationListSnapshotAllPages, AnnotationMoveResult, AnnotationPatch, AnnotationRef, AnnotationUpdateResult, AttachmentContent, AttachmentsCache, DocumentAccessInfo, DocumentActionsService, DocumentAnnotationsService, DocumentAnnotationsService as DocumentAnnotationsService$1, DocumentAttachmentsService, DocumentCapabilities, DocumentEventStream, DocumentFormsService, DocumentHandle, DocumentHandle as DocumentHandle$1, DocumentIdentity, DocumentMetadata, DocumentPagesService, DocumentPagesService as DocumentPagesService$1, DocumentRedactionService, DocumentRenderService, DocumentSearchService, DocumentSecurityService, DocumentSecurityService as DocumentSecurityService$1, DocumentSecurityState, DocumentUnlockInput, DocumentUnlockResult, Engine, Engine as Engine$1, EngineError, EngineError as EngineError$1, EngineErrorCode, EngineFactory, MetadataCache, MetadataPatch, MetadataService, MetadataService as MetadataService$1, MetadataUpdateResult, MutationMeta, OpenInput, OpenInput as OpenInput$1, OpenInputShare, OpenOptions, OpenOptions as OpenOptions$1, PageAnnotationsService, PageAnnotationsService as PageAnnotationsService$1, PageDeleteResult, PageFlattenResult, PageFlattenUsage, PageGeometryService, PageGeometryService as PageGeometryService$1, PageGeometrySnapshot, PageHandle, PageHandle as PageHandle$1, PageImageHandle, PageImageOptions, PageInsertBlankSpec, PageInsertResult, PageListSnapshot, PageMoveResult, PageObjectNumber, PageRaster, PageRenderOptions, PageRenderService, PageRenderService as PageRenderService$1, PageRotateResult, PageRotation, PageStructureCache, PageTextService, PageTextService as PageTextService$1, PageTextSnapshot, PdfSaveMode, PdfSaveMode as PdfSaveMode$1, SearchRequest, SearchSlice, TokenSource, WeakAnnotationEditSession } from "@embedpdf/engine-core/runtime";
|
|
2
2
|
import { CdnAccessInfoForApply, DocumentHead, DocumentManifest, LayerScopePlane } from "@embedpdf/engine-core/wire";
|
|
3
3
|
import { SessionEventPublisher } from "@embedpdf/engine-services";
|
|
4
4
|
//#region src/transport/HttpClient.d.ts
|
|
@@ -21,6 +21,30 @@ interface HttpClientOptions {
|
|
|
21
21
|
sessionId?: string;
|
|
22
22
|
/** Replace the global fetch (e.g. in Node tests with undici). */
|
|
23
23
|
fetch?: typeof globalThis.fetch;
|
|
24
|
+
/**
|
|
25
|
+
* Document affinity key: `X-CloudPDF-Doc` is sent BY DEFAULT on
|
|
26
|
+
* origin-bound doc requests so consistent-hash load balancers can pin
|
|
27
|
+
* a document's traffic to one warm replica. Routing hints are
|
|
28
|
+
* unconditional client behavior — whether they are USED is the
|
|
29
|
+
* operator's choice at the load balancer, never the app's. Derived
|
|
30
|
+
* from the request path (`/v1/docs/:docId/…` — the same extraction
|
|
31
|
+
* the Helm chart's uri-mode fallback uses); never sent on
|
|
32
|
+
* CDN-rewritten requests.
|
|
33
|
+
*
|
|
34
|
+
* `false` is an ESCAPE HATCH, not a feature toggle: use it only
|
|
35
|
+
* against a stale server whose CORS allowlist predates the header
|
|
36
|
+
* (browser preflights would fail) or behind a proxy that rejects
|
|
37
|
+
* unknown request headers.
|
|
38
|
+
*/
|
|
39
|
+
docAffinityHeader?: boolean;
|
|
40
|
+
/** Observability: fired once per transport-level retry (engine busy /
|
|
41
|
+
* restarting). Lets apps count sheds without wrapping every call. */
|
|
42
|
+
onRetry?: (info: {
|
|
43
|
+
path: string;
|
|
44
|
+
code: string;
|
|
45
|
+
attempt: number;
|
|
46
|
+
waitMs: number;
|
|
47
|
+
}) => void;
|
|
24
48
|
}
|
|
25
49
|
/**
|
|
26
50
|
* Per-document binding for CDN URL rewriting. `CloudDocumentHandle`
|
|
@@ -51,6 +75,11 @@ declare class HttpClient {
|
|
|
51
75
|
private readonly base;
|
|
52
76
|
private readonly tokenFn;
|
|
53
77
|
private readonly fetchFn;
|
|
78
|
+
private readonly docAffinityHeader;
|
|
79
|
+
private readonly onRetry;
|
|
80
|
+
private static readonly MAX_RETRIES;
|
|
81
|
+
private static readonly RETRY_AFTER_CAP_MS;
|
|
82
|
+
private static readonly RETRY_AFTER_DEFAULT_MS;
|
|
54
83
|
private readonly sessionId;
|
|
55
84
|
private cdnBinding;
|
|
56
85
|
constructor(opts: HttpClientOptions);
|
|
@@ -150,6 +179,8 @@ declare class HttpClient {
|
|
|
150
179
|
/** PATCH counterpart of {@link postMultipartJson} (binary-carrying mutations). */
|
|
151
180
|
patchMultipartJson<T>(path: string, body: FormData, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
152
181
|
postJson<T>(path: string, body: unknown, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
182
|
+
/** POST a JSON body and return the raw binary response (pages.extract). */
|
|
183
|
+
postJsonBytes(path: string, body: unknown, signal: AbortSignal): Promise<Uint8Array>;
|
|
153
184
|
/** POST a raw binary body (FDF/XFDF import) and parse the JSON response. */
|
|
154
185
|
postBytesJson<T>(path: string, bytes: Uint8Array, contentType: string, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
155
186
|
patchJson<T>(path: string, body: unknown, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
@@ -158,6 +189,7 @@ declare class HttpClient {
|
|
|
158
189
|
postEmpty(path: string, body: unknown, signal: AbortSignal): Promise<void>;
|
|
159
190
|
private requestJson;
|
|
160
191
|
private request;
|
|
192
|
+
private execute;
|
|
161
193
|
private parseJsonResponse;
|
|
162
194
|
/**
|
|
163
195
|
* Decide where this request actually goes. With no CDN binding (or
|
|
@@ -264,6 +296,10 @@ interface ManifestAccessor {
|
|
|
264
296
|
/** Same advance for a page delete, additionally dropping the deleted
|
|
265
297
|
* pages' manifest rows so per-page leaf URLs stop resolving locally. */
|
|
266
298
|
applyPageDelete(cache: PageStructureCache, deletedPages: PageObjectNumber[]): void;
|
|
299
|
+
/** Page insert: the cached manifest has no rows for the fresh PONs (the
|
|
300
|
+
* result carries only their object numbers), so the absorb drops the
|
|
301
|
+
* cache for a lazy refetch instead of patching. */
|
|
302
|
+
applyPageInsert(cache: PageStructureCache): void;
|
|
267
303
|
/** Advance the cached manifest's docVersion + metadataVersion after a metadata write. */
|
|
268
304
|
applyMetadata(cache: MetadataCache): void;
|
|
269
305
|
/** Advance the cached manifest's docVersion + attachmentsVersion after an
|
|
@@ -365,6 +401,18 @@ declare class CloudDocumentHandle implements DocumentHandle$1 {
|
|
|
365
401
|
* 404 anyway — this keeps the failure local and instant).
|
|
366
402
|
*/
|
|
367
403
|
private absorbPageDelete;
|
|
404
|
+
/**
|
|
405
|
+
* Patch bookkeeping after a page insert — the mirror of
|
|
406
|
+
* {@link absorbPageDelete}, with one asymmetry: delete can patch the
|
|
407
|
+
* cached manifest losslessly (it only REMOVES rows), but an insert needs
|
|
408
|
+
* manifest rows for the fresh PONs and the result doesn't carry them. So
|
|
409
|
+
* this absorb flips the planes (insert changes the page SET, so like
|
|
410
|
+
* delete it owns content + annotations alongside layout), raises the
|
|
411
|
+
* version floor, and DROPS the cache — the next read refetches a manifest
|
|
412
|
+
* that includes the new pages' rows. The UI never waits on that refetch:
|
|
413
|
+
* the mutation result / event already carries the full new layout.
|
|
414
|
+
*/
|
|
415
|
+
private absorbPageInsert;
|
|
368
416
|
/**
|
|
369
417
|
* Patch the cached manifest after a metadata write. Symmetric with
|
|
370
418
|
* {@link absorbPageMove}: a metadata edit advances `docVersion` (so leaf
|
|
@@ -448,6 +496,9 @@ declare class CloudDocumentPagesService implements DocumentPagesService$1 {
|
|
|
448
496
|
move(pageObjectNumbers: PageObjectNumber[], destIndex: number): AbortablePromise$1<PageMoveResult>;
|
|
449
497
|
rotate(pageObjectNumbers: PageObjectNumber[], rotation: PageRotation): AbortablePromise$1<PageRotateResult>;
|
|
450
498
|
delete(pageObjectNumbers: PageObjectNumber[]): AbortablePromise$1<PageDeleteResult>;
|
|
499
|
+
insert(bytes: Uint8Array | ArrayBuffer, destIndex?: number): AbortablePromise$1<PageInsertResult>;
|
|
500
|
+
insertBlank(spec: PageInsertBlankSpec, destIndex?: number): AbortablePromise$1<PageInsertResult>;
|
|
501
|
+
extract(pageObjectNumbers: PageObjectNumber[]): AbortablePromise$1<Uint8Array>;
|
|
451
502
|
flatten(pageObjectNumbers: PageObjectNumber[], usage?: PageFlattenUsage): AbortablePromise$1<PageFlattenResult>;
|
|
452
503
|
}
|
|
453
504
|
//#endregion
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortError, AbortablePromise, AbortablePromise as AbortablePromise$1, AnnotationAppearanceImageOptions, AnnotationAppearanceImagesResult, AnnotationAppearanceRenderOptions, AnnotationAppearancesResult, AnnotationCreateResult, AnnotationDeleteResult, AnnotationDraft, AnnotationListPageSnapshot, AnnotationListSnapshotAllPages, AnnotationMoveResult, AnnotationPatch, AnnotationRef, AnnotationUpdateResult, AttachmentContent, AttachmentsCache, DocumentAccessInfo, DocumentActionsService, DocumentAnnotationsService, DocumentAnnotationsService as DocumentAnnotationsService$1, DocumentAttachmentsService, DocumentCapabilities, DocumentEventStream, DocumentFormsService, DocumentHandle, DocumentHandle as DocumentHandle$1, DocumentIdentity, DocumentMetadata, DocumentPagesService, DocumentPagesService as DocumentPagesService$1, DocumentRedactionService, DocumentRenderService, DocumentSearchService, DocumentSecurityService, DocumentSecurityService as DocumentSecurityService$1, DocumentSecurityState, DocumentUnlockInput, DocumentUnlockResult, Engine, Engine as Engine$1, EngineError, EngineError as EngineError$1, EngineErrorCode, EngineFactory, MetadataCache, MetadataPatch, MetadataService, MetadataService as MetadataService$1, MetadataUpdateResult, MutationMeta, OpenInput, OpenInput as OpenInput$1, OpenInputShare, OpenOptions, OpenOptions as OpenOptions$1, PageAnnotationsService, PageAnnotationsService as PageAnnotationsService$1, PageDeleteResult, PageFlattenResult, PageFlattenUsage, PageGeometryService, PageGeometryService as PageGeometryService$1, PageGeometrySnapshot, PageHandle, PageHandle as PageHandle$1, PageImageHandle, PageImageOptions, PageListSnapshot, PageMoveResult, PageObjectNumber, PageRaster, PageRenderOptions, PageRenderService, PageRenderService as PageRenderService$1, PageRotateResult, PageRotation, PageStructureCache, PageTextService, PageTextService as PageTextService$1, PageTextSnapshot, PdfSaveMode, PdfSaveMode as PdfSaveMode$1, SearchRequest, SearchSlice, TokenSource, WeakAnnotationEditSession } from "@embedpdf/engine-core/runtime";
|
|
1
|
+
import { AbortError, AbortablePromise, AbortablePromise as AbortablePromise$1, AnnotationAppearanceImageOptions, AnnotationAppearanceImagesResult, AnnotationAppearanceRenderOptions, AnnotationAppearancesResult, AnnotationCreateResult, AnnotationDeleteResult, AnnotationDraft, AnnotationListPageSnapshot, AnnotationListSnapshotAllPages, AnnotationMoveResult, AnnotationPatch, AnnotationRef, AnnotationUpdateResult, AttachmentContent, AttachmentsCache, DocumentAccessInfo, DocumentActionsService, DocumentAnnotationsService, DocumentAnnotationsService as DocumentAnnotationsService$1, DocumentAttachmentsService, DocumentCapabilities, DocumentEventStream, DocumentFormsService, DocumentHandle, DocumentHandle as DocumentHandle$1, DocumentIdentity, DocumentMetadata, DocumentPagesService, DocumentPagesService as DocumentPagesService$1, DocumentRedactionService, DocumentRenderService, DocumentSearchService, DocumentSecurityService, DocumentSecurityService as DocumentSecurityService$1, DocumentSecurityState, DocumentUnlockInput, DocumentUnlockResult, Engine, Engine as Engine$1, EngineError, EngineError as EngineError$1, EngineErrorCode, EngineFactory, MetadataCache, MetadataPatch, MetadataService, MetadataService as MetadataService$1, MetadataUpdateResult, MutationMeta, OpenInput, OpenInput as OpenInput$1, OpenInputShare, OpenOptions, OpenOptions as OpenOptions$1, PageAnnotationsService, PageAnnotationsService as PageAnnotationsService$1, PageDeleteResult, PageFlattenResult, PageFlattenUsage, PageGeometryService, PageGeometryService as PageGeometryService$1, PageGeometrySnapshot, PageHandle, PageHandle as PageHandle$1, PageImageHandle, PageImageOptions, PageInsertBlankSpec, PageInsertResult, PageListSnapshot, PageMoveResult, PageObjectNumber, PageRaster, PageRenderOptions, PageRenderService, PageRenderService as PageRenderService$1, PageRotateResult, PageRotation, PageStructureCache, PageTextService, PageTextService as PageTextService$1, PageTextSnapshot, PdfSaveMode, PdfSaveMode as PdfSaveMode$1, SearchRequest, SearchSlice, TokenSource, WeakAnnotationEditSession } from "@embedpdf/engine-core/runtime";
|
|
2
2
|
import { CdnAccessInfoForApply, DocumentHead, DocumentManifest, LayerScopePlane } from "@embedpdf/engine-core/wire";
|
|
3
3
|
import { SessionEventPublisher } from "@embedpdf/engine-services";
|
|
4
4
|
//#region src/transport/HttpClient.d.ts
|
|
@@ -21,6 +21,30 @@ interface HttpClientOptions {
|
|
|
21
21
|
sessionId?: string;
|
|
22
22
|
/** Replace the global fetch (e.g. in Node tests with undici). */
|
|
23
23
|
fetch?: typeof globalThis.fetch;
|
|
24
|
+
/**
|
|
25
|
+
* Document affinity key: `X-CloudPDF-Doc` is sent BY DEFAULT on
|
|
26
|
+
* origin-bound doc requests so consistent-hash load balancers can pin
|
|
27
|
+
* a document's traffic to one warm replica. Routing hints are
|
|
28
|
+
* unconditional client behavior — whether they are USED is the
|
|
29
|
+
* operator's choice at the load balancer, never the app's. Derived
|
|
30
|
+
* from the request path (`/v1/docs/:docId/…` — the same extraction
|
|
31
|
+
* the Helm chart's uri-mode fallback uses); never sent on
|
|
32
|
+
* CDN-rewritten requests.
|
|
33
|
+
*
|
|
34
|
+
* `false` is an ESCAPE HATCH, not a feature toggle: use it only
|
|
35
|
+
* against a stale server whose CORS allowlist predates the header
|
|
36
|
+
* (browser preflights would fail) or behind a proxy that rejects
|
|
37
|
+
* unknown request headers.
|
|
38
|
+
*/
|
|
39
|
+
docAffinityHeader?: boolean;
|
|
40
|
+
/** Observability: fired once per transport-level retry (engine busy /
|
|
41
|
+
* restarting). Lets apps count sheds without wrapping every call. */
|
|
42
|
+
onRetry?: (info: {
|
|
43
|
+
path: string;
|
|
44
|
+
code: string;
|
|
45
|
+
attempt: number;
|
|
46
|
+
waitMs: number;
|
|
47
|
+
}) => void;
|
|
24
48
|
}
|
|
25
49
|
/**
|
|
26
50
|
* Per-document binding for CDN URL rewriting. `CloudDocumentHandle`
|
|
@@ -51,6 +75,11 @@ declare class HttpClient {
|
|
|
51
75
|
private readonly base;
|
|
52
76
|
private readonly tokenFn;
|
|
53
77
|
private readonly fetchFn;
|
|
78
|
+
private readonly docAffinityHeader;
|
|
79
|
+
private readonly onRetry;
|
|
80
|
+
private static readonly MAX_RETRIES;
|
|
81
|
+
private static readonly RETRY_AFTER_CAP_MS;
|
|
82
|
+
private static readonly RETRY_AFTER_DEFAULT_MS;
|
|
54
83
|
private readonly sessionId;
|
|
55
84
|
private cdnBinding;
|
|
56
85
|
constructor(opts: HttpClientOptions);
|
|
@@ -150,6 +179,8 @@ declare class HttpClient {
|
|
|
150
179
|
/** PATCH counterpart of {@link postMultipartJson} (binary-carrying mutations). */
|
|
151
180
|
patchMultipartJson<T>(path: string, body: FormData, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
152
181
|
postJson<T>(path: string, body: unknown, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
182
|
+
/** POST a JSON body and return the raw binary response (pages.extract). */
|
|
183
|
+
postJsonBytes(path: string, body: unknown, signal: AbortSignal): Promise<Uint8Array>;
|
|
153
184
|
/** POST a raw binary body (FDF/XFDF import) and parse the JSON response. */
|
|
154
185
|
postBytesJson<T>(path: string, bytes: Uint8Array, contentType: string, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
155
186
|
patchJson<T>(path: string, body: unknown, parser: (raw: unknown) => T, signal: AbortSignal): Promise<T>;
|
|
@@ -158,6 +189,7 @@ declare class HttpClient {
|
|
|
158
189
|
postEmpty(path: string, body: unknown, signal: AbortSignal): Promise<void>;
|
|
159
190
|
private requestJson;
|
|
160
191
|
private request;
|
|
192
|
+
private execute;
|
|
161
193
|
private parseJsonResponse;
|
|
162
194
|
/**
|
|
163
195
|
* Decide where this request actually goes. With no CDN binding (or
|
|
@@ -264,6 +296,10 @@ interface ManifestAccessor {
|
|
|
264
296
|
/** Same advance for a page delete, additionally dropping the deleted
|
|
265
297
|
* pages' manifest rows so per-page leaf URLs stop resolving locally. */
|
|
266
298
|
applyPageDelete(cache: PageStructureCache, deletedPages: PageObjectNumber[]): void;
|
|
299
|
+
/** Page insert: the cached manifest has no rows for the fresh PONs (the
|
|
300
|
+
* result carries only their object numbers), so the absorb drops the
|
|
301
|
+
* cache for a lazy refetch instead of patching. */
|
|
302
|
+
applyPageInsert(cache: PageStructureCache): void;
|
|
267
303
|
/** Advance the cached manifest's docVersion + metadataVersion after a metadata write. */
|
|
268
304
|
applyMetadata(cache: MetadataCache): void;
|
|
269
305
|
/** Advance the cached manifest's docVersion + attachmentsVersion after an
|
|
@@ -365,6 +401,18 @@ declare class CloudDocumentHandle implements DocumentHandle$1 {
|
|
|
365
401
|
* 404 anyway — this keeps the failure local and instant).
|
|
366
402
|
*/
|
|
367
403
|
private absorbPageDelete;
|
|
404
|
+
/**
|
|
405
|
+
* Patch bookkeeping after a page insert — the mirror of
|
|
406
|
+
* {@link absorbPageDelete}, with one asymmetry: delete can patch the
|
|
407
|
+
* cached manifest losslessly (it only REMOVES rows), but an insert needs
|
|
408
|
+
* manifest rows for the fresh PONs and the result doesn't carry them. So
|
|
409
|
+
* this absorb flips the planes (insert changes the page SET, so like
|
|
410
|
+
* delete it owns content + annotations alongside layout), raises the
|
|
411
|
+
* version floor, and DROPS the cache — the next read refetches a manifest
|
|
412
|
+
* that includes the new pages' rows. The UI never waits on that refetch:
|
|
413
|
+
* the mutation result / event already carries the full new layout.
|
|
414
|
+
*/
|
|
415
|
+
private absorbPageInsert;
|
|
368
416
|
/**
|
|
369
417
|
* Patch the cached manifest after a metadata write. Symmetric with
|
|
370
418
|
* {@link absorbPageMove}: a metadata edit advances `docVersion` (so leaf
|
|
@@ -448,6 +496,9 @@ declare class CloudDocumentPagesService implements DocumentPagesService$1 {
|
|
|
448
496
|
move(pageObjectNumbers: PageObjectNumber[], destIndex: number): AbortablePromise$1<PageMoveResult>;
|
|
449
497
|
rotate(pageObjectNumbers: PageObjectNumber[], rotation: PageRotation): AbortablePromise$1<PageRotateResult>;
|
|
450
498
|
delete(pageObjectNumbers: PageObjectNumber[]): AbortablePromise$1<PageDeleteResult>;
|
|
499
|
+
insert(bytes: Uint8Array | ArrayBuffer, destIndex?: number): AbortablePromise$1<PageInsertResult>;
|
|
500
|
+
insertBlank(spec: PageInsertBlankSpec, destIndex?: number): AbortablePromise$1<PageInsertResult>;
|
|
501
|
+
extract(pageObjectNumbers: PageObjectNumber[]): AbortablePromise$1<Uint8Array>;
|
|
451
502
|
flatten(pageObjectNumbers: PageObjectNumber[], usage?: PageFlattenUsage): AbortablePromise$1<PageFlattenResult>;
|
|
452
503
|
}
|
|
453
504
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbortError, AbortError as AbortError$1, AbortablePromise, AbortablePromise as AbortablePromise$1, CONTINUOUS_RENDER_POLICY, DEFAULT_PDF_SAVE_MODE, EngineError, EngineError as EngineError$1, EngineErrorCode, EngineErrorCode as EngineErrorCode$1, canonicalSearchQuery, checkCapability, createPageImageHandle, decodePdfBits, encodeFieldRefKey, encodeStableIdKey, expandRawScope, normalizeAnnotationDraft, normalizeAnnotationPatch, normalizeAttachmentFileSource, passwordPromptFromState, searchContentEpoch, securityStateFromHead } from "@embedpdf/engine-core/runtime";
|
|
2
|
-
import { AccessResponseSchema, AnnotationAppearanceManifestSchema, AnnotationCreateResultSchema, AnnotationDeleteResultSchema, AnnotationListPageSnapshotSchema, AnnotationMoveResultSchema, AnnotationUpdateResultSchema, AttachmentCreateResultSchema, AttachmentDeleteResultSchema, DEFAULT_LAYER_NAME, DocumentActionsSnapshotSchema, DocumentHeadSchema, DocumentManifestSchema, DocumentMetadataSchema, EmbeddedFileItemSchema, EngineErrorPayloadSchema, FormEffectsResultSchema, FormFieldCreateResultSchema, FormFieldDTOSchema, FormFieldDeleteResultSchema, FormFieldUpdateResultSchema, FormImportResultSchema, FormRepairResultSchema, FormSetValueResultSchema, FormSnapshotSchema, FormWidgetLinkResultSchema, MetadataUpdateResultSchema, PageDeleteResultSchema, PageFlattenResultSchema, PageGeometrySnapshotSchema, PageListSnapshotSchema, PageMoveResultSchema, PageRotateResultSchema, PageTextSnapshotSchema, RedactionApplyResultSchema, SearchSliceSchema, WeakAnnotationSessionResponseSchema, annotationAppearancesImageOptionsToWire, applyCdnAccess, decodeSearchToken, decodeTokenText, encodeSearchToken, renderImageOptionsToWire, wirePaths } from "@embedpdf/engine-core/wire";
|
|
2
|
+
import { AccessResponseSchema, AnnotationAppearanceManifestSchema, AnnotationCreateResultSchema, AnnotationDeleteResultSchema, AnnotationListPageSnapshotSchema, AnnotationMoveResultSchema, AnnotationUpdateResultSchema, AttachmentCreateResultSchema, AttachmentDeleteResultSchema, DEFAULT_LAYER_NAME, DocumentActionsSnapshotSchema, DocumentHeadSchema, DocumentManifestSchema, DocumentMetadataSchema, EmbeddedFileItemSchema, EngineErrorPayloadSchema, FormEffectsResultSchema, FormFieldCreateResultSchema, FormFieldDTOSchema, FormFieldDeleteResultSchema, FormFieldUpdateResultSchema, FormImportResultSchema, FormRepairResultSchema, FormSetValueResultSchema, FormSnapshotSchema, FormWidgetLinkResultSchema, MetadataUpdateResultSchema, PageDeleteResultSchema, PageFlattenResultSchema, PageGeometrySnapshotSchema, PageInsertResultSchema, PageListSnapshotSchema, PageMoveResultSchema, PageRotateResultSchema, PageTextSnapshotSchema, RedactionApplyResultSchema, SearchSliceSchema, WeakAnnotationSessionResponseSchema, annotationAppearancesImageOptionsToWire, applyCdnAccess, decodeSearchToken, decodeTokenText, encodeSearchToken, renderImageOptionsToWire, wirePaths } from "@embedpdf/engine-core/wire";
|
|
3
3
|
import { EventHub, SessionEventPublisher, generateUuid } from "@embedpdf/engine-services";
|
|
4
4
|
//#region src/document/planes.ts
|
|
5
5
|
/**
|
|
@@ -449,6 +449,13 @@ var CloudDocumentFormsService = class {
|
|
|
449
449
|
};
|
|
450
450
|
//#endregion
|
|
451
451
|
//#region src/document/CloudDocumentPagesService.ts
|
|
452
|
+
/** Detach a Uint8Array view into a standalone ArrayBuffer (the resource-map
|
|
453
|
+
* shape) without disturbing a larger buffer the caller still owns. */
|
|
454
|
+
function copyToExactBuffer(view) {
|
|
455
|
+
const copy = new ArrayBuffer(view.byteLength);
|
|
456
|
+
new Uint8Array(copy).set(view);
|
|
457
|
+
return copy;
|
|
458
|
+
}
|
|
452
459
|
/**
|
|
453
460
|
* Cloud-side document pages service. Mirrors `LocalDocumentPagesService`
|
|
454
461
|
* over HTTP: GET /pages for `list`, POST /pages/move for the reorder.
|
|
@@ -541,6 +548,46 @@ var CloudDocumentPagesService = class {
|
|
|
541
548
|
return result;
|
|
542
549
|
});
|
|
543
550
|
}
|
|
551
|
+
insert(bytes, destIndex) {
|
|
552
|
+
if (this.isClosed()) return AbortablePromise$1.rejectReason(new EngineError$1(EngineErrorCode$1.DocNotOpen, `document ${this.docId} is closed`));
|
|
553
|
+
return AbortablePromise$1.run(async (signal) => {
|
|
554
|
+
const buffer = bytes instanceof ArrayBuffer ? bytes : copyToExactBuffer(bytes);
|
|
555
|
+
const form = buildMutationForm(destIndex !== void 0 ? { destIndex } : {}, { source: {
|
|
556
|
+
bytes: buffer,
|
|
557
|
+
mimeType: "application/pdf",
|
|
558
|
+
name: "source.pdf"
|
|
559
|
+
} });
|
|
560
|
+
const result = await this.http.postMultipartJson(wirePaths.layerPagesInsert(this.docId, this.layerName), form, (raw) => PageInsertResultSchema.parse(raw), signal);
|
|
561
|
+
if (result.cache) this.manifest.applyPageInsert(result.cache);
|
|
562
|
+
this.publisher.publishLocal({
|
|
563
|
+
type: "pages.inserted",
|
|
564
|
+
destIndex,
|
|
565
|
+
...result
|
|
566
|
+
});
|
|
567
|
+
return result;
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
insertBlank(spec, destIndex) {
|
|
571
|
+
if (this.isClosed()) return AbortablePromise$1.rejectReason(new EngineError$1(EngineErrorCode$1.DocNotOpen, `document ${this.docId} is closed`));
|
|
572
|
+
return AbortablePromise$1.run(async (signal) => {
|
|
573
|
+
const result = await this.http.postJson(wirePaths.layerPagesInsertBlank(this.docId, this.layerName), {
|
|
574
|
+
size: spec.size,
|
|
575
|
+
...spec.count !== void 0 ? { count: spec.count } : {},
|
|
576
|
+
...destIndex !== void 0 ? { destIndex } : {}
|
|
577
|
+
}, (raw) => PageInsertResultSchema.parse(raw), signal);
|
|
578
|
+
if (result.cache) this.manifest.applyPageInsert(result.cache);
|
|
579
|
+
this.publisher.publishLocal({
|
|
580
|
+
type: "pages.inserted",
|
|
581
|
+
destIndex,
|
|
582
|
+
...result
|
|
583
|
+
});
|
|
584
|
+
return result;
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
extract(pageObjectNumbers) {
|
|
588
|
+
if (this.isClosed()) return AbortablePromise$1.rejectReason(new EngineError$1(EngineErrorCode$1.DocNotOpen, `document ${this.docId} is closed`));
|
|
589
|
+
return AbortablePromise$1.run(async (signal) => this.http.postJsonBytes(wirePaths.layerPagesExtract(this.docId, this.layerName), { pageObjectNumbers }, signal));
|
|
590
|
+
}
|
|
544
591
|
flatten(pageObjectNumbers, usage = "display") {
|
|
545
592
|
if (this.isClosed()) return AbortablePromise$1.rejectReason(new EngineError$1(EngineErrorCode$1.DocNotOpen, `document ${this.docId} is closed`));
|
|
546
593
|
return AbortablePromise$1.run(async (signal) => {
|
|
@@ -809,9 +856,7 @@ var CloudDocumentSecurityService = class {
|
|
|
809
856
|
* and `applyCdnAccess` short-circuits to origin; safe to call always.
|
|
810
857
|
*/
|
|
811
858
|
async postAccess(signal, body) {
|
|
812
|
-
const response = await this.http.postJson(wirePaths.access, {
|
|
813
|
-
docId: this.docId,
|
|
814
|
-
layerName: this.layerName,
|
|
859
|
+
const response = await this.http.postJson(wirePaths.access(this.docId, this.layerName), {
|
|
815
860
|
...body.password ? { password: body.password } : {},
|
|
816
861
|
mode: body.mode
|
|
817
862
|
}, (raw) => AccessResponseSchema.parse(raw), signal);
|
|
@@ -1341,6 +1386,12 @@ function auditRowToEvent(row, mySessionId) {
|
|
|
1341
1386
|
origin,
|
|
1342
1387
|
...row.payload
|
|
1343
1388
|
};
|
|
1389
|
+
case "pages.insert":
|
|
1390
|
+
case "pages.insertBlank": return {
|
|
1391
|
+
type: "pages.inserted",
|
|
1392
|
+
origin,
|
|
1393
|
+
...row.payload
|
|
1394
|
+
};
|
|
1344
1395
|
case "pages.flatten": return {
|
|
1345
1396
|
type: "pages.flattened",
|
|
1346
1397
|
origin,
|
|
@@ -1583,6 +1634,7 @@ var CloudDocumentHandle = class {
|
|
|
1583
1634
|
apply: (meta, owns) => this.absorbMutation(meta, owns),
|
|
1584
1635
|
applyPageStructure: (cache) => this.absorbPageStructure(cache),
|
|
1585
1636
|
applyPageDelete: (cache, deletedPages) => this.absorbPageDelete(cache, deletedPages),
|
|
1637
|
+
applyPageInsert: (cache) => this.absorbPageInsert(cache),
|
|
1586
1638
|
applyMetadata: (cache) => this.absorbMetadata(cache),
|
|
1587
1639
|
applyAttachments: (cache) => this.absorbAttachments(cache)
|
|
1588
1640
|
};
|
|
@@ -1748,6 +1800,27 @@ var CloudDocumentHandle = class {
|
|
|
1748
1800
|
};
|
|
1749
1801
|
}
|
|
1750
1802
|
/**
|
|
1803
|
+
* Patch bookkeeping after a page insert — the mirror of
|
|
1804
|
+
* {@link absorbPageDelete}, with one asymmetry: delete can patch the
|
|
1805
|
+
* cached manifest losslessly (it only REMOVES rows), but an insert needs
|
|
1806
|
+
* manifest rows for the fresh PONs and the result doesn't carry them. So
|
|
1807
|
+
* this absorb flips the planes (insert changes the page SET, so like
|
|
1808
|
+
* delete it owns content + annotations alongside layout), raises the
|
|
1809
|
+
* version floor, and DROPS the cache — the next read refetches a manifest
|
|
1810
|
+
* that includes the new pages' rows. The UI never waits on that refetch:
|
|
1811
|
+
* the mutation result / event already carries the full new layout.
|
|
1812
|
+
*/
|
|
1813
|
+
absorbPageInsert(cache) {
|
|
1814
|
+
this.flipScopes([
|
|
1815
|
+
"layout",
|
|
1816
|
+
"content",
|
|
1817
|
+
"annotations"
|
|
1818
|
+
]);
|
|
1819
|
+
this.manifestFloorVersion = Math.max(this.manifestFloorVersion, cache.docVersion);
|
|
1820
|
+
this.inflightManifest = null;
|
|
1821
|
+
this.manifestCache = null;
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1751
1824
|
* Patch the cached manifest after a metadata write. Symmetric with
|
|
1752
1825
|
* {@link absorbPageMove}: a metadata edit advances `docVersion` (so leaf
|
|
1753
1826
|
* URLs re-resolve) and `metadataVersion` (so the /metadata leaf re-fetches),
|
|
@@ -1883,6 +1956,9 @@ var CloudDocumentHandle = class {
|
|
|
1883
1956
|
case "pages.deleted":
|
|
1884
1957
|
if (event.cache) this.absorbPageDelete(event.cache, event.pageObjectNumbers);
|
|
1885
1958
|
return;
|
|
1959
|
+
case "pages.inserted":
|
|
1960
|
+
if (event.cache) this.absorbPageInsert(event.cache);
|
|
1961
|
+
return;
|
|
1886
1962
|
case "metadata.updated":
|
|
1887
1963
|
if (event.cache) this.absorbMetadata(event.cache);
|
|
1888
1964
|
return;
|
|
@@ -1934,7 +2010,7 @@ function fallbackUnknownHead(id) {
|
|
|
1934
2010
|
access: {
|
|
1935
2011
|
required: true,
|
|
1936
2012
|
reasons: ["permissions-unknown"],
|
|
1937
|
-
endpoint: wirePaths.access
|
|
2013
|
+
endpoint: wirePaths.access(id, DEFAULT_LAYER_NAME)
|
|
1938
2014
|
}
|
|
1939
2015
|
};
|
|
1940
2016
|
}
|
|
@@ -2042,6 +2118,8 @@ var HttpClient = class HttpClient {
|
|
|
2042
2118
|
_defineProperty(this, "base", void 0);
|
|
2043
2119
|
_defineProperty(this, "tokenFn", void 0);
|
|
2044
2120
|
_defineProperty(this, "fetchFn", void 0);
|
|
2121
|
+
_defineProperty(this, "docAffinityHeader", void 0);
|
|
2122
|
+
_defineProperty(this, "onRetry", void 0);
|
|
2045
2123
|
_defineProperty(this, "sessionId", void 0);
|
|
2046
2124
|
_defineProperty(this, "cdnBinding", null);
|
|
2047
2125
|
this.base = opts.baseUrl.replace(/\/$/, "");
|
|
@@ -2049,6 +2127,8 @@ var HttpClient = class HttpClient {
|
|
|
2049
2127
|
this.tokenFn = token === void 0 ? null : typeof token === "function" ? token : () => token;
|
|
2050
2128
|
this.sessionId = opts.sessionId ?? null;
|
|
2051
2129
|
this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
2130
|
+
this.docAffinityHeader = opts.docAffinityHeader ?? true;
|
|
2131
|
+
this.onRetry = opts.onRetry;
|
|
2052
2132
|
}
|
|
2053
2133
|
/** Normalized server base URL (no trailing slash). */
|
|
2054
2134
|
get baseUrl() {
|
|
@@ -2279,6 +2359,12 @@ var HttpClient = class HttpClient {
|
|
|
2279
2359
|
const res = await this.requestJson(path, "POST", body, signal);
|
|
2280
2360
|
return await this.parseJsonResponse(res, parser);
|
|
2281
2361
|
}
|
|
2362
|
+
/** POST a JSON body and return the raw binary response (pages.extract). */
|
|
2363
|
+
async postJsonBytes(path, body, signal) {
|
|
2364
|
+
const res = await this.requestJson(path, "POST", body, signal);
|
|
2365
|
+
if (!res.ok) await this.throwFromBody(res);
|
|
2366
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
2367
|
+
}
|
|
2282
2368
|
/** POST a raw binary body (FDF/XFDF import) and parse the JSON response. */
|
|
2283
2369
|
async postBytesJson(path, bytes, contentType, parser, signal) {
|
|
2284
2370
|
const headers = new Headers({ "Content-Type": contentType });
|
|
@@ -2325,7 +2411,7 @@ var HttpClient = class HttpClient {
|
|
|
2325
2411
|
});
|
|
2326
2412
|
}
|
|
2327
2413
|
async request(path, init) {
|
|
2328
|
-
const { url, extraHeader } = this.resolveOutgoing(path);
|
|
2414
|
+
const { url, extraHeader, routedToCdn } = this.resolveOutgoing(path);
|
|
2329
2415
|
const headers = new Headers(init.headers ?? {});
|
|
2330
2416
|
if (this.tokenFn) {
|
|
2331
2417
|
const token = await this.tokenFn();
|
|
@@ -2334,11 +2420,32 @@ var HttpClient = class HttpClient {
|
|
|
2334
2420
|
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
2335
2421
|
if (this.sessionId) headers.set("X-Engine-Session-Id", this.sessionId);
|
|
2336
2422
|
if (extraHeader) headers.set(extraHeader.name, extraHeader.value);
|
|
2337
|
-
|
|
2338
|
-
|
|
2423
|
+
if (this.docAffinityHeader && !routedToCdn) {
|
|
2424
|
+
const doc = /^\/v1\/docs\/([^/]+)\//.exec(path);
|
|
2425
|
+
if (doc) headers.set("X-CloudPDF-Doc", decodeURIComponent(doc[1]));
|
|
2426
|
+
}
|
|
2427
|
+
for (let attempt = 0;; attempt++) {
|
|
2428
|
+
const res = await this.execute(url, {
|
|
2339
2429
|
...init,
|
|
2340
2430
|
headers
|
|
2341
2431
|
});
|
|
2432
|
+
if (res.status !== 503 || attempt >= HttpClient.MAX_RETRIES || isStreamBody(init.body ?? null)) return res;
|
|
2433
|
+
const code = await peekErrorCode(res);
|
|
2434
|
+
if (code === null || !RETRYABLE_503.has(code)) return res;
|
|
2435
|
+
const hinted = Number(res.headers.get("retry-after"));
|
|
2436
|
+
const waitMs = (Number.isFinite(hinted) ? Math.min(Math.max(hinted, 0) * 1e3, HttpClient.RETRY_AFTER_CAP_MS) : HttpClient.RETRY_AFTER_DEFAULT_MS) * (.9 + .2 * Math.random());
|
|
2437
|
+
this.onRetry?.({
|
|
2438
|
+
path,
|
|
2439
|
+
code,
|
|
2440
|
+
attempt,
|
|
2441
|
+
waitMs
|
|
2442
|
+
});
|
|
2443
|
+
await sleepRacingAbort(waitMs, init.signal ?? null);
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
async execute(url, init) {
|
|
2447
|
+
try {
|
|
2448
|
+
return await this.fetchFn(url, init);
|
|
2342
2449
|
} catch (err) {
|
|
2343
2450
|
if (init.signal?.aborted) throw new AbortError$1(init.signal.reason ?? err);
|
|
2344
2451
|
if (err?.name === "AbortError") throw new AbortError$1(err.message);
|
|
@@ -2364,7 +2471,8 @@ var HttpClient = class HttpClient {
|
|
|
2364
2471
|
resolveOutgoing(path) {
|
|
2365
2472
|
if (!this.cdnBinding || !this.cdnBinding.cdn) return {
|
|
2366
2473
|
url: `${this.baseUrl}${path}`,
|
|
2367
|
-
extraHeader: null
|
|
2474
|
+
extraHeader: null,
|
|
2475
|
+
routedToCdn: false
|
|
2368
2476
|
};
|
|
2369
2477
|
const applied = applyCdnAccess({
|
|
2370
2478
|
path,
|
|
@@ -2375,7 +2483,8 @@ var HttpClient = class HttpClient {
|
|
|
2375
2483
|
});
|
|
2376
2484
|
return {
|
|
2377
2485
|
url: applied.url,
|
|
2378
|
-
extraHeader: applied.routedToCdn && applied.authHeader ? applied.authHeader : null
|
|
2486
|
+
extraHeader: applied.routedToCdn && applied.authHeader ? applied.authHeader : null,
|
|
2487
|
+
routedToCdn: applied.routedToCdn
|
|
2379
2488
|
};
|
|
2380
2489
|
}
|
|
2381
2490
|
async throwFromBody(res) {
|
|
@@ -2395,6 +2504,9 @@ var HttpClient = class HttpClient {
|
|
|
2395
2504
|
throw new EngineError$1(code, `HTTP ${res.status}`, { details: { status: res.status } });
|
|
2396
2505
|
}
|
|
2397
2506
|
};
|
|
2507
|
+
_defineProperty(HttpClient, "MAX_RETRIES", 2);
|
|
2508
|
+
_defineProperty(HttpClient, "RETRY_AFTER_CAP_MS", 5e3);
|
|
2509
|
+
_defineProperty(HttpClient, "RETRY_AFTER_DEFAULT_MS", 1e3);
|
|
2398
2510
|
function mapStatusToCode(status) {
|
|
2399
2511
|
if (status === 401) return EngineErrorCode$1.Unauthenticated;
|
|
2400
2512
|
if (status === 403) return EngineErrorCode$1.Forbidden;
|
|
@@ -2409,6 +2521,36 @@ function trySerialized(value) {
|
|
|
2409
2521
|
const result = EngineErrorPayloadSchema.safeParse(value);
|
|
2410
2522
|
return result.success ? result.data : null;
|
|
2411
2523
|
}
|
|
2524
|
+
const RETRYABLE_503 = /* @__PURE__ */ new Set(["EngineBusy", "EngineRestarting"]);
|
|
2525
|
+
function isStreamBody(body) {
|
|
2526
|
+
return typeof ReadableStream !== "undefined" && body instanceof ReadableStream;
|
|
2527
|
+
}
|
|
2528
|
+
/** Read the error code from a 503 body without consuming the response. */
|
|
2529
|
+
async function peekErrorCode(res) {
|
|
2530
|
+
try {
|
|
2531
|
+
const code = (await res.clone().json())?.error?.code;
|
|
2532
|
+
return typeof code === "string" ? code : null;
|
|
2533
|
+
} catch {
|
|
2534
|
+
return null;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
function sleepRacingAbort(ms, signal) {
|
|
2538
|
+
return new Promise((resolve, reject) => {
|
|
2539
|
+
if (signal?.aborted) {
|
|
2540
|
+
reject(new AbortError$1(signal.reason ?? "aborted"));
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
const timer = setTimeout(() => {
|
|
2544
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2545
|
+
resolve();
|
|
2546
|
+
}, ms);
|
|
2547
|
+
const onAbort = () => {
|
|
2548
|
+
clearTimeout(timer);
|
|
2549
|
+
reject(new AbortError$1(signal?.reason ?? "aborted"));
|
|
2550
|
+
};
|
|
2551
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2412
2554
|
//#endregion
|
|
2413
2555
|
//#region src/CloudEngine.ts
|
|
2414
2556
|
/**
|