@alpha2-sh/editor 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +108 -2
  3. package/dist/browser/bridge.d.ts +44 -0
  4. package/dist/browser/bridge.js +2176 -0
  5. package/dist/browser/extensions-worker-entry.d.ts +1 -0
  6. package/dist/browser/extensions-worker-entry.js +3 -0
  7. package/dist/browser/extensions-worker.d.ts +46 -0
  8. package/dist/browser/extensions-worker.js +149 -0
  9. package/dist/browser/hash-worker-entry.d.ts +1 -0
  10. package/dist/browser/hash-worker-entry.js +3 -0
  11. package/dist/browser/hash-worker.d.ts +24 -0
  12. package/dist/browser/hash-worker.js +87 -0
  13. package/dist/browser/index.d.ts +11 -0
  14. package/dist/browser/index.js +9 -0
  15. package/dist/browser/jobs.d.ts +82 -0
  16. package/dist/browser/jobs.js +291 -0
  17. package/dist/browser/resources.d.ts +40 -0
  18. package/dist/browser/resources.js +83 -0
  19. package/dist/browser/runtime/assets.d.ts +233 -0
  20. package/dist/browser/runtime/assets.js +633 -0
  21. package/dist/browser/runtime/audio.d.ts +60 -0
  22. package/dist/browser/runtime/audio.js +549 -0
  23. package/dist/browser/runtime/budget.d.ts +11 -0
  24. package/dist/browser/runtime/budget.js +26 -0
  25. package/dist/browser/runtime/caption-fonts.d.ts +34 -0
  26. package/dist/browser/runtime/caption-fonts.js +69 -0
  27. package/dist/browser/runtime/color.d.ts +7 -0
  28. package/dist/browser/runtime/color.js +12 -0
  29. package/dist/browser/runtime/engine.d.ts +91 -0
  30. package/dist/browser/runtime/engine.js +352 -0
  31. package/dist/browser/runtime/export-audio.d.ts +23 -0
  32. package/dist/browser/runtime/export-audio.js +75 -0
  33. package/dist/browser/runtime/exporter.d.ts +140 -0
  34. package/dist/browser/runtime/exporter.js +751 -0
  35. package/dist/browser/runtime/extension-import.d.ts +196 -0
  36. package/dist/browser/runtime/extension-import.js +521 -0
  37. package/dist/browser/runtime/fingerprint.d.ts +57 -0
  38. package/dist/browser/runtime/fingerprint.js +157 -0
  39. package/dist/browser/runtime/generators.d.ts +188 -0
  40. package/dist/browser/runtime/generators.js +173 -0
  41. package/dist/browser/runtime/locks.d.ts +32 -0
  42. package/dist/browser/runtime/locks.js +86 -0
  43. package/dist/browser/runtime/output.d.ts +87 -0
  44. package/dist/browser/runtime/output.js +230 -0
  45. package/dist/browser/runtime/preview-utils.d.ts +3 -0
  46. package/dist/browser/runtime/preview-utils.js +49 -0
  47. package/dist/browser/runtime/proxies.d.ts +72 -0
  48. package/dist/browser/runtime/proxies.js +369 -0
  49. package/dist/browser/runtime/proxy-storage.d.ts +59 -0
  50. package/dist/browser/runtime/proxy-storage.js +119 -0
  51. package/dist/browser/runtime/sources.d.ts +82 -0
  52. package/dist/browser/runtime/sources.js +819 -0
  53. package/dist/browser/runtime/staging.d.ts +10 -0
  54. package/dist/browser/runtime/staging.js +32 -0
  55. package/dist/browser/runtime/still.d.ts +21 -0
  56. package/dist/browser/runtime/still.js +52 -0
  57. package/dist/browser/runtime/wasm.d.ts +9 -0
  58. package/dist/browser/runtime/wasm.js +20 -0
  59. package/dist/browser/types.d.ts +549 -0
  60. package/dist/browser/types.js +1 -0
  61. package/dist/browser/worker-entry.d.ts +1 -0
  62. package/dist/browser/worker-entry.js +2 -0
  63. package/dist/browser/worker.d.ts +24 -0
  64. package/dist/browser/worker.js +239 -0
  65. package/dist/core/captions.d.ts +34 -0
  66. package/dist/core/captions.js +108 -0
  67. package/dist/core/clip-time.d.ts +31 -0
  68. package/dist/core/clip-time.js +125 -0
  69. package/dist/core/commands.d.ts +413 -0
  70. package/dist/core/commands.js +1118 -0
  71. package/dist/core/compositions.d.ts +234 -0
  72. package/dist/core/compositions.js +851 -0
  73. package/dist/core/document.d.ts +245 -0
  74. package/dist/core/document.js +683 -0
  75. package/dist/core/keyframes.d.ts +82 -0
  76. package/dist/core/keyframes.js +153 -0
  77. package/dist/core/presets.d.ts +64 -0
  78. package/dist/core/presets.js +241 -0
  79. package/dist/core/session.d.ts +150 -0
  80. package/dist/core/session.js +541 -0
  81. package/dist/core/text.d.ts +17 -0
  82. package/dist/core/text.js +17 -0
  83. package/dist/core/time.d.ts +19 -0
  84. package/dist/core/time.js +39 -0
  85. package/dist/core/timeline-index.d.ts +44 -0
  86. package/dist/core/timeline-index.js +59 -0
  87. package/dist/core/transitions.d.ts +20 -0
  88. package/dist/core/transitions.js +118 -0
  89. package/dist/core/validated.d.ts +23 -0
  90. package/dist/core/validated.js +52 -0
  91. package/dist/index.d.ts +12 -0
  92. package/dist/index.js +11 -0
  93. package/dist/native-client/index.d.ts +131 -0
  94. package/dist/native-client/index.js +354 -0
  95. package/dist/ports/index.d.ts +146 -0
  96. package/dist/ports/index.js +1 -0
  97. package/dist/react/index.d.ts +37 -0
  98. package/dist/react/index.js +94 -0
  99. package/package.json +79 -4
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ // The `@alpha2-sh/editor/extensions-worker` entry (see `runtimeAssets.extensionsWorkerFactory`).
2
+ import { installExtensionsWorker } from "./extensions-worker.js";
3
+ installExtensionsWorker(self);
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Answers the call posted to `scope` (a dedicated worker's global): `{ type: "call", call, file,
3
+ * wasmUrl, ... }` is answered with `{ type: "answered", result }`, or `{ type: "failed", name,
4
+ * message }` when the file cannot be read or the provider refuses it. `{ type: "started" }` is
5
+ * posted first, once the worker runs.
6
+ * @param {{ onmessage: ((event: MessageEvent) => void) | null, postMessage(message: unknown): void }} scope
7
+ */
8
+ export function installExtensionsWorker(scope: {
9
+ onmessage: ((event: MessageEvent) => void) | null;
10
+ postMessage(message: unknown): void;
11
+ }): void;
12
+ /**
13
+ * Runs an extensions call: `request` names the call (`read`, `svg-diagnostics` or
14
+ * `exr-first-frame`), the `file` it reads and what else the call takes. With `factory`, the call
15
+ * runs in a worker it makes (one running {@link installExtensionsWorker}), which is terminated once
16
+ * it answers, when `signal` aborts (rejecting with its reason), or after `timeoutMs` (rejecting as
17
+ * `provider_stalled`). A worker that cannot start (its script does not load) leaves the call to
18
+ * this thread, as it does without a factory; here the module call cannot be stopped, and `signal`
19
+ * is checked between steps.
20
+ *
21
+ * @param {{ factory?: () => Worker, wasmUrl?: string | URL }} module
22
+ * @param {{ call: string, file: Blob & { name?: string }, [key: string]: unknown }} request
23
+ * @param {{ signal?: AbortSignal, timeoutMs?: number, operation?: string }} [options]
24
+ */
25
+ export function callExtensions({ factory, wasmUrl }: {
26
+ factory?: () => Worker;
27
+ wasmUrl?: string | URL;
28
+ }, request: {
29
+ call: string;
30
+ file: Blob & {
31
+ name?: string;
32
+ };
33
+ [key: string]: unknown;
34
+ }, { signal, timeoutMs, operation }?: {
35
+ signal?: AbortSignal;
36
+ timeoutMs?: number;
37
+ operation?: string;
38
+ }): Promise<any>;
39
+ /**
40
+ * How long one call in an extensions worker may run before the worker is terminated and the call
41
+ * fails as `provider_stalled`: the 40 s a native host's decode shard may spend inside one provider
42
+ * or decoder call (10 s plus the default `ALPHA2_GPU_TIMEOUT_MS`) before the export fails as
43
+ * stalled. The provider's budgets refuse a file whose drawing they can count long before this; the
44
+ * limit is for work they cannot.
45
+ */
46
+ export const EXTENSIONS_CALL_TIMEOUT_MS: 40000;
@@ -0,0 +1,149 @@
1
+ // Runs extension provider calls (probe, open, thumbnail) off the page's thread: each is one
2
+ // synchronous call that can decode every frame or parse a file whole. A host bundling the
3
+ // `@alpha2-sh/editor/extensions-worker` entry passes `runtimeAssets.extensionsWorkerFactory`; each
4
+ // call gets its own worker, terminated on cancel or past `EXTENSIONS_CALL_TIMEOUT_MS`.
5
+ import { EditorError } from "../core/document.js";
6
+ import { exrFirstFrame, readProviderFile, svgDiagnostics, } from "./runtime/extension-import.js";
7
+ /**
8
+ * How long one call in an extensions worker may run before the worker is terminated and the call
9
+ * fails as `provider_stalled`: the 40 s a native host's decode shard may spend inside one provider
10
+ * or decoder call (10 s plus the default `ALPHA2_GPU_TIMEOUT_MS`) before the export fails as
11
+ * stalled. The provider's budgets refuse a file whose drawing they can count long before this; the
12
+ * limit is for work they cannot.
13
+ */
14
+ export const EXTENSIONS_CALL_TIMEOUT_MS = 40_000;
15
+ /** Loads the provider module (`alpha2/extensions`) from `wasmUrl`, once per realm. */
16
+ async function loadModule(wasmUrl) {
17
+ const { loadExtensions } = await import("alpha2/extensions");
18
+ return loadExtensions(wasmUrl);
19
+ }
20
+ /** What each call reads of its file's bytes with the provider module. */
21
+ const CALLS = {
22
+ /** What import reads of a file: `null`, or its provider, report and thumbnail. */
23
+ read: (extensions, bytes) => readProviderFile(extensions, bytes),
24
+ /** An SVG's diagnostics with the font stack `fonts` (bytes, in fallback order). */
25
+ "svg-diagnostics": (extensions, bytes, { provider, fonts }) => svgDiagnostics(extensions, provider, bytes, fonts),
26
+ /** An OpenEXR sequence's first frame: its thumbnail and report. */
27
+ "exr-first-frame": (extensions, bytes) => exrFirstFrame(extensions, bytes),
28
+ };
29
+ /** Runs `request` in this realm, checking `signal` between the steps it waits for. */
30
+ async function run(request, wasmUrl, load, signal) {
31
+ const call = CALLS[request.call];
32
+ if (!call)
33
+ throw new Error(`unknown extensions call ${request.call}`);
34
+ const extensions = await load(wasmUrl);
35
+ signal?.throwIfAborted();
36
+ const bytes = new Uint8Array(await request.file.arrayBuffer());
37
+ signal?.throwIfAborted();
38
+ return call(extensions, bytes, request);
39
+ }
40
+ /**
41
+ * Answers the call posted to `scope` (a dedicated worker's global): `{ type: "call", call, file,
42
+ * wasmUrl, ... }` is answered with `{ type: "answered", result }`, or `{ type: "failed", name,
43
+ * message }` when the file cannot be read or the provider refuses it. `{ type: "started" }` is
44
+ * posted first, once the worker runs.
45
+ * @param {{ onmessage: ((event: MessageEvent) => void) | null, postMessage(message: unknown): void }} scope
46
+ */
47
+ export function installExtensionsWorker(scope) {
48
+ scope.onmessage = async ({ data }) => {
49
+ if (data?.type !== "call")
50
+ return;
51
+ try {
52
+ const result = await run(data, data.wasmUrl, loadModule);
53
+ scope.postMessage({ type: "answered", result });
54
+ }
55
+ catch (error) {
56
+ scope.postMessage({
57
+ type: "failed",
58
+ name: error?.name ?? "Error",
59
+ // A provider's refusal is thrown as its message alone.
60
+ message: String(error?.message ?? error),
61
+ });
62
+ }
63
+ };
64
+ scope.postMessage({ type: "started" });
65
+ }
66
+ /** `url` as an absolute URL, which a worker resolves the same as the page. */
67
+ function absolute(url) {
68
+ if (url === undefined)
69
+ return undefined;
70
+ const base = globalThis.location?.href;
71
+ return base ? new URL(String(url), base).href : String(url);
72
+ }
73
+ /**
74
+ * Runs an extensions call: `request` names the call (`read`, `svg-diagnostics` or
75
+ * `exr-first-frame`), the `file` it reads and what else the call takes. With `factory`, the call
76
+ * runs in a worker it makes (one running {@link installExtensionsWorker}), which is terminated once
77
+ * it answers, when `signal` aborts (rejecting with its reason), or after `timeoutMs` (rejecting as
78
+ * `provider_stalled`). A worker that cannot start (its script does not load) leaves the call to
79
+ * this thread, as it does without a factory; here the module call cannot be stopped, and `signal`
80
+ * is checked between steps.
81
+ *
82
+ * @param {{ factory?: () => Worker, wasmUrl?: string | URL }} module
83
+ * @param {{ call: string, file: Blob & { name?: string }, [key: string]: unknown }} request
84
+ * @param {{ signal?: AbortSignal, timeoutMs?: number, operation?: string }} [options]
85
+ */
86
+ export function callExtensions({ factory, wasmUrl }, request, { signal, timeoutMs = EXTENSIONS_CALL_TIMEOUT_MS, operation = "import" } = {}) {
87
+ signal?.throwIfAborted();
88
+ const here = () => run(request, wasmUrl, loadModule, signal);
89
+ if (!factory)
90
+ return here();
91
+ let worker;
92
+ try {
93
+ worker = factory();
94
+ }
95
+ catch {
96
+ return here();
97
+ }
98
+ return new Promise((resolve, reject) => {
99
+ let started = false;
100
+ let ended = false;
101
+ const end = () => {
102
+ ended = true;
103
+ clearTimeout(timer);
104
+ signal?.removeEventListener("abort", abort);
105
+ worker.terminate();
106
+ };
107
+ const abort = () => {
108
+ end();
109
+ reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
110
+ };
111
+ const timer = setTimeout(() => {
112
+ end();
113
+ reject(new EditorError("provider_stalled", `${request.file.name ?? "The file"} took the extension provider more than ${timeoutMs / 1000} s to read; it was stopped`, operation));
114
+ }, timeoutMs);
115
+ signal?.addEventListener("abort", abort, { once: true });
116
+ worker.onmessage = ({ data }) => {
117
+ if (ended)
118
+ return;
119
+ if (data?.type === "started")
120
+ started = true;
121
+ else if (data?.type === "answered") {
122
+ end();
123
+ resolve(data.result);
124
+ }
125
+ else if (data?.type === "failed") {
126
+ end();
127
+ reject(Object.assign(new Error(data.message), {
128
+ name: data.name ?? "Error",
129
+ }));
130
+ }
131
+ };
132
+ worker.onerror = (event) => {
133
+ if (ended)
134
+ return;
135
+ event?.preventDefault?.();
136
+ end();
137
+ if (started)
138
+ reject(new Error(`the extensions worker stopped: ${event?.message ?? "an uncaught error"}`));
139
+ // The worker never ran: the call is made here instead.
140
+ else
141
+ here().then(resolve, reject);
142
+ };
143
+ worker.postMessage({
144
+ type: "call",
145
+ ...request,
146
+ wasmUrl: absolute(wasmUrl),
147
+ });
148
+ });
149
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ // The `@alpha2-sh/editor/hash-worker` entry (see `runtimeAssets.hashWorkerFactory`).
2
+ import { installHashWorker } from "./hash-worker.js";
3
+ installHashWorker(self);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Answers hash requests posted to `scope` (a dedicated worker's global): `{ type: "hash", file }`
3
+ * is answered with `{ type: "progress", bytes }` as it is read and `{ type: "hashed", sha256 }`, or
4
+ * `{ type: "failed", message, name }` when the file cannot be read.
5
+ * @param {{ onmessage: ((event: MessageEvent) => void) | null, postMessage(message: unknown): void }} scope
6
+ */
7
+ export function installHashWorker(scope: {
8
+ onmessage: ((event: MessageEvent) => void) | null;
9
+ postMessage(message: unknown): void;
10
+ }): void;
11
+ /**
12
+ * The SHA-256 of `file`, as `sha256Blob` gives it, hashed in a worker `factory` makes (one running
13
+ * {@link installHashWorker}), which is terminated once it answers or `signal` aborts. A worker that
14
+ * cannot start (its script does not load) leaves the hash to this thread's `sha256Blob`, which lets
15
+ * other tasks run every few milliseconds.
16
+ * @param {() => Worker} factory
17
+ * @param {Blob} file
18
+ * @param {{ signal?: AbortSignal, onProgress?: (bytes: number) => void }} [options]
19
+ * @returns {Promise<string>}
20
+ */
21
+ export function hashInWorker(factory: () => Worker, file: Blob, { signal, onProgress }?: {
22
+ signal?: AbortSignal;
23
+ onProgress?: (bytes: number) => void;
24
+ }): Promise<string>;
@@ -0,0 +1,87 @@
1
+ // Hashes an imported file off the page's thread (about 5 ms of CPU per megabyte in JavaScript).
2
+ // A host bundling the `@alpha2-sh/editor/hash-worker` entry passes
3
+ // `runtimeAssets.hashWorkerFactory`; each file gets its own worker, terminated on cancel.
4
+ import { sha256Blob } from "alpha2/assets";
5
+ /**
6
+ * Answers hash requests posted to `scope` (a dedicated worker's global): `{ type: "hash", file }`
7
+ * is answered with `{ type: "progress", bytes }` as it is read and `{ type: "hashed", sha256 }`, or
8
+ * `{ type: "failed", message, name }` when the file cannot be read.
9
+ * @param {{ onmessage: ((event: MessageEvent) => void) | null, postMessage(message: unknown): void }} scope
10
+ */
11
+ export function installHashWorker(scope) {
12
+ scope.onmessage = async ({ data }) => {
13
+ if (data?.type !== "hash")
14
+ return;
15
+ try {
16
+ const sha256 = await sha256Blob(data.file, {
17
+ onProgress: (bytes) => scope.postMessage({ type: "progress", bytes }),
18
+ });
19
+ scope.postMessage({ type: "hashed", sha256 });
20
+ }
21
+ catch (error) {
22
+ scope.postMessage({
23
+ type: "failed",
24
+ name: error?.name ?? "Error",
25
+ message: String(error?.message ?? error),
26
+ });
27
+ }
28
+ };
29
+ }
30
+ /**
31
+ * The SHA-256 of `file`, as `sha256Blob` gives it, hashed in a worker `factory` makes (one running
32
+ * {@link installHashWorker}), which is terminated once it answers or `signal` aborts. A worker that
33
+ * cannot start (its script does not load) leaves the hash to this thread's `sha256Blob`, which lets
34
+ * other tasks run every few milliseconds.
35
+ * @param {() => Worker} factory
36
+ * @param {Blob} file
37
+ * @param {{ signal?: AbortSignal, onProgress?: (bytes: number) => void }} [options]
38
+ * @returns {Promise<string>}
39
+ */
40
+ export function hashInWorker(factory, file, { signal, onProgress } = {}) {
41
+ signal?.throwIfAborted();
42
+ let worker;
43
+ try {
44
+ worker = factory();
45
+ }
46
+ catch {
47
+ return sha256Blob(file, { signal, onProgress });
48
+ }
49
+ return new Promise((resolve, reject) => {
50
+ let answered = false;
51
+ const end = () => {
52
+ answered = true;
53
+ signal?.removeEventListener("abort", abort);
54
+ worker.terminate();
55
+ };
56
+ const abort = () => {
57
+ end();
58
+ reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
59
+ };
60
+ signal?.addEventListener("abort", abort, { once: true });
61
+ worker.onmessage = ({ data }) => {
62
+ if (answered)
63
+ return;
64
+ if (data?.type === "progress")
65
+ onProgress?.(data.bytes);
66
+ else if (data?.type === "hashed") {
67
+ end();
68
+ resolve(data.sha256);
69
+ }
70
+ else if (data?.type === "failed") {
71
+ end();
72
+ reject(Object.assign(new Error(data.message), {
73
+ name: data.name ?? "Error",
74
+ }));
75
+ }
76
+ };
77
+ worker.onerror = (event) => {
78
+ if (answered)
79
+ return;
80
+ event?.preventDefault?.();
81
+ end();
82
+ // The worker never ran: the hash is made here instead.
83
+ sha256Blob(file, { signal, onProgress }).then(resolve, reject);
84
+ };
85
+ worker.postMessage({ type: "hash", file });
86
+ });
87
+ }
@@ -0,0 +1,11 @@
1
+ export { ConformError, createBrowserBridge, createMediaAdapter, } from "./bridge.js";
2
+ export { ResourceRegistry } from "./resources.js";
3
+ export { ExportJob } from "./jobs.js";
4
+ export type { ExportAttemptRecord, ExportJobOptions } from "./jobs.js";
5
+ export { keptProxies, removeKeptProxies } from "./runtime/proxy-storage.js";
6
+ export { estimatedStagingBytes, minimumExportWorkingSet, } from "./runtime/staging.js";
7
+ export { sequenceNumbering } from "./runtime/extension-import.js";
8
+ export { createWorkerPreview, installEditorWorker } from "./worker.js";
9
+ export { hashInWorker, installHashWorker } from "./hash-worker.js";
10
+ export { EXTENSIONS_CALL_TIMEOUT_MS, installExtensionsWorker, } from "./extensions-worker.js";
11
+ export type * from "./types.js";
@@ -0,0 +1,9 @@
1
+ export { ConformError, createBrowserBridge, createMediaAdapter, } from "./bridge.js";
2
+ export { ResourceRegistry } from "./resources.js";
3
+ export { ExportJob } from "./jobs.js";
4
+ export { keptProxies, removeKeptProxies } from "./runtime/proxy-storage.js";
5
+ export { estimatedStagingBytes, minimumExportWorkingSet, } from "./runtime/staging.js";
6
+ export { sequenceNumbering } from "./runtime/extension-import.js";
7
+ export { createWorkerPreview, installEditorWorker } from "./worker.js";
8
+ export { hashInWorker, installHashWorker } from "./hash-worker.js";
9
+ export { EXTENSIONS_CALL_TIMEOUT_MS, installExtensionsWorker, } from "./extensions-worker.js";
@@ -0,0 +1,82 @@
1
+ import { type ExportFailure } from "alpha2/export-failure";
2
+ import { type RetryPolicyOptions, type RetryProfile } from "alpha2/export-retry";
3
+ import type { EditorDocument } from "../core/document.js";
4
+ import type { ExportBackend, ExportProgress, ExportArtifact } from "../ports/index.js";
5
+ /**
6
+ * Refuses an export `frameRate` the engine cannot read, before an export starts, for every
7
+ * backend: one rate, the same everywhere.
8
+ */
9
+ export declare function requireExportFrameRate(frameRate: unknown): void;
10
+ /** What happened to one attempt of a job. */
11
+ export interface ExportAttemptRecord {
12
+ readonly id: string;
13
+ readonly number: number;
14
+ readonly profile: RetryProfile;
15
+ readonly startedAt: number;
16
+ readonly endedAt?: number;
17
+ readonly outcome: "running" | "succeeded" | "failed" | "cancelled";
18
+ readonly failure?: ExportFailure;
19
+ /** What the retry policy decided after this attempt failed, and why. */
20
+ readonly decision?: {
21
+ readonly retry: boolean;
22
+ readonly reason: string;
23
+ };
24
+ }
25
+ /** Options of an {@link ExportJob}. */
26
+ export interface ExportJobOptions {
27
+ /** The job's id, when its backend already named it (a service's job); a fresh UUID otherwise. */
28
+ readonly id?: string;
29
+ /**
30
+ * The retry policy (see `alpha2/export-retry`), or `false` for one attempt. Default: three
31
+ * attempts, the reference backoff.
32
+ */
33
+ readonly retry?: RetryPolicyOptions | false;
34
+ /** Waits `ms` or rejects when `signal` aborts; replaceable for tests. */
35
+ readonly sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
36
+ }
37
+ /**
38
+ * One export of a pinned document, run in bounded attempts.
39
+ *
40
+ * Every attempt renders the same document from the start, on a fresh device or host, with the same
41
+ * output settings; a failed one is retried only as the retry policy allows, with less in flight
42
+ * when the failure is evidence of memory exhaustion. Progress from an attempt that has ended is
43
+ * ignored, so a late answer cannot move a newer attempt's progress or change its result. Cancelling
44
+ * stops the running attempt and any pending retry; no new attempt starts after it.
45
+ */
46
+ export declare class ExportJob {
47
+ #private;
48
+ readonly revision: number;
49
+ readonly id: string;
50
+ /**
51
+ * Resolves to the artifact. Rejects with `EditorError` `cancelled` after {@link cancel}, or
52
+ * with the last attempt's error, whose `failure` property (and {@link failure}) holds the
53
+ * classified `ExportFailure`.
54
+ */
55
+ readonly result: Promise<ExportArtifact>;
56
+ /**
57
+ * Starts the job at once, on the next microtask.
58
+ *
59
+ * @param revision The editor revision `document` was pinned at.
60
+ * @param backend Runs each attempt; its `dispose` runs once when the job settles.
61
+ * @param release Called once when the job settles, whatever the outcome.
62
+ */
63
+ constructor(revision: number, document: EditorDocument, backend: ExportBackend, release?: () => void, options?: ExportJobOptions);
64
+ /** The latest progress, frozen. Reports from an attempt that has ended are not reflected. */
65
+ get progress(): ExportProgress;
66
+ /** Why the job failed, classified; undefined unless it failed. */
67
+ get failure(): ExportFailure | undefined;
68
+ /** Every attempt so far, oldest first. */
69
+ get attempts(): readonly ExportAttemptRecord[];
70
+ /**
71
+ * Calls `listener` with the current progress at once and with every later change. Returns
72
+ * the unsubscribe function.
73
+ */
74
+ subscribe(listener: (value: ExportProgress) => void): () => void;
75
+ /**
76
+ * Stops the running attempt and any pending retry, and resolves once the job has settled. An
77
+ * attempt that finished before the job settled has what it made released (a temporary file
78
+ * deleted). Resolves without effect on a settled job. Rejects only when cleanup failed
79
+ * (`EditorError` `cancel_failed`).
80
+ */
81
+ cancel(): Promise<void>;
82
+ }