@ttsc/wasm 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/.ttsc-wasm-build.json +2 -2
- package/dist/ttsc.wasm +0 -0
- package/lib/src/BootTtscWorkerTerminationError.d.ts +15 -0
- package/lib/src/BootTtscWorkerTerminationError.js +26 -0
- package/lib/src/BootTtscWorkerTerminationError.js.map +1 -0
- package/lib/src/bootTtsc.d.ts +6 -2
- package/lib/src/bootTtsc.js +162 -33
- package/lib/src/bootTtsc.js.map +1 -1
- package/lib/src/index.d.ts +1 -0
- package/lib/src/index.js +1 -0
- package/lib/src/index.js.map +1 -1
- package/lib/src/structures/IBootTtscOptions.d.ts +11 -0
- package/package.json +1 -1
- package/src/BootTtscWorkerTerminationError.ts +26 -0
- package/src/bootTtsc.ts +240 -26
- package/src/index.ts +1 -0
- package/src/structures/IBootTtscOptions.ts +11 -0
package/README.md
CHANGED
|
@@ -100,9 +100,12 @@ The `version`, `commit`, and `date` variables in `host/host.go` are all overrida
|
|
|
100
100
|
```ts
|
|
101
101
|
import { bootTtsc } from "@ttsc/wasm";
|
|
102
102
|
|
|
103
|
+
const controller = new AbortController();
|
|
103
104
|
const { api, host } = await bootTtsc({
|
|
104
105
|
wasmUrl: "/your.wasm",
|
|
105
106
|
apiName: "yourApi",
|
|
107
|
+
signal: controller.signal,
|
|
108
|
+
timeoutMs: 60_000,
|
|
106
109
|
});
|
|
107
110
|
|
|
108
111
|
host.writeFile("/work/tsconfig.json", '{"compilerOptions":{"strict":true}}');
|
|
@@ -112,6 +115,10 @@ const result = await api.build({ cwd: "/work" });
|
|
|
112
115
|
console.log(result.result); // JSON: { diagnostics, output }
|
|
113
116
|
```
|
|
114
117
|
|
|
118
|
+
`timeoutMs` defaults to 60 seconds and covers serialization behind an earlier boot, fetch, instantiation, and the Go readiness signal. A caller abort or deadline cancels the shared in-flight boot. Failures before `go.run` clear cache and callback state so a later call can retry. `importScripts` is synchronous, so cancellation is observed immediately before and after that call but cannot interrupt the script while it evaluates.
|
|
119
|
+
|
|
120
|
+
Once `go.run` starts, JavaScript cannot terminate the Go wasm runtime. A boot failure from that point throws `BootTtscWorkerTerminationError` with code `TTSC_WASM_WORKER_TERMINATION_REQUIRED`, and later boots for the same `apiName` reject without installing another readiness bridge. Terminate and replace that Worker before retrying.
|
|
121
|
+
|
|
115
122
|
Booting two wasms with the same `apiName` overwrites the previous global binding; pick a unique `apiName` per binary. `bootTtsc` also installs shared `fs` and `process` globals in its Worker, so use separate Workers when binaries need independent filesystems.
|
|
116
123
|
|
|
117
124
|
## Fountain API (snapshot, AST, type checker)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
|
-
"identity": "
|
|
3
|
+
"identity": "15c3bdce6daf77fd832027479abe7265e38699a6764b718d30a441bed7cb9266",
|
|
4
4
|
"artifacts": [
|
|
5
5
|
{
|
|
6
6
|
"path": "/home/runner/work/ttsc/ttsc/packages/wasm/dist/ttsc.wasm",
|
|
7
7
|
"type": "file",
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "fe7a63d35504171ee8e7183bdf1581a9fe13ca619639c48121a239275c91abb7"
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "/home/runner/work/ttsc/ttsc/packages/wasm/dist/wasm_exec.js",
|
package/dist/ttsc.wasm
CHANGED
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A boot failure that requires replacing the current Web Worker.
|
|
3
|
+
*
|
|
4
|
+
* Once `go.run` starts, JavaScript cannot stop that Go runtime. Retrying the
|
|
5
|
+
* same API name in the same Worker would install a new readiness bridge that
|
|
6
|
+
* the stale runtime could invoke, so `bootTtsc` terminally rejects later boots
|
|
7
|
+
* until the caller terminates the Worker.
|
|
8
|
+
*/
|
|
9
|
+
export declare class BootTtscWorkerTerminationError extends Error {
|
|
10
|
+
static readonly CODE = "TTSC_WASM_WORKER_TERMINATION_REQUIRED";
|
|
11
|
+
readonly apiName: string;
|
|
12
|
+
readonly code = "TTSC_WASM_WORKER_TERMINATION_REQUIRED";
|
|
13
|
+
readonly cause: unknown;
|
|
14
|
+
constructor(apiName: string, cause: unknown);
|
|
15
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BootTtscWorkerTerminationError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A boot failure that requires replacing the current Web Worker.
|
|
6
|
+
*
|
|
7
|
+
* Once `go.run` starts, JavaScript cannot stop that Go runtime. Retrying the
|
|
8
|
+
* same API name in the same Worker would install a new readiness bridge that
|
|
9
|
+
* the stale runtime could invoke, so `bootTtsc` terminally rejects later boots
|
|
10
|
+
* until the caller terminates the Worker.
|
|
11
|
+
*/
|
|
12
|
+
class BootTtscWorkerTerminationError extends Error {
|
|
13
|
+
static CODE = "TTSC_WASM_WORKER_TERMINATION_REQUIRED";
|
|
14
|
+
apiName;
|
|
15
|
+
code = BootTtscWorkerTerminationError.CODE;
|
|
16
|
+
cause;
|
|
17
|
+
constructor(apiName, cause) {
|
|
18
|
+
const causeMessage = cause instanceof Error ? cause.message : String(cause ?? "boot failed");
|
|
19
|
+
super(`[${BootTtscWorkerTerminationError.CODE}] ${causeMessage} bootTtsc: the ${apiName} Go runtime already started; terminate and replace this Worker before retrying.`);
|
|
20
|
+
this.name = "BootTtscWorkerTerminationError";
|
|
21
|
+
this.apiName = apiName;
|
|
22
|
+
this.cause = cause;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.BootTtscWorkerTerminationError = BootTtscWorkerTerminationError;
|
|
26
|
+
//# sourceMappingURL=BootTtscWorkerTerminationError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BootTtscWorkerTerminationError.js","sourceRoot":"","sources":["../../src/BootTtscWorkerTerminationError.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,oCAA4C,SAAQ,KAAK;IAChD,MAAM,CAAU,IAAI,GAAG,uCAAuC,CAAC;IAEtD,OAAO,CAAS;IAChB,IAAI,GAAG,8BAA8B,CAAC,IAAI,CAAC;IAClC,KAAK,CAAU;IAExC,YAAmB,OAAe,EAAE,KAAc;QAChD,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,aAAa,CAAC,CAAC;QAC1E,KAAK,CACH,IAAI,8BAA8B,CAAC,IAAI,KAAK,YAAY,kBAAkB,OAAO,iFAAiF,CACnK,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,gCAAgC,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}
|
package/lib/src/bootTtsc.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { IBootResult } from "./structures/IBootResult";
|
|
2
2
|
import type { IBootTtscOptions } from "./structures/IBootTtscOptions";
|
|
3
|
+
/** Finite default for queueing, fetch, instantiation, and Go readiness. */
|
|
4
|
+
export declare const DEFAULT_BOOT_TIMEOUT_MS = 60000;
|
|
3
5
|
/**
|
|
4
6
|
* Boot a host-built wasm. Re-entrant only if you reuse the same `host`.
|
|
5
7
|
*
|
|
6
8
|
* Concurrent calls with the same `(apiName, wasmUrl)` pair share the same
|
|
7
9
|
* in-flight boot. Calls with the same `apiName` but different `wasmUrl` are
|
|
8
10
|
* serialized via `bootChainByApiName` so they don't race on the shared
|
|
9
|
-
* `globalThis[apiName+"Ready"]` resolver slot.
|
|
10
|
-
*
|
|
11
|
+
* `globalThis[apiName+"Ready"]` resolver slot. A rejection before `go.run`
|
|
12
|
+
* clears the cache entries so the next call can retry from scratch. A rejection
|
|
13
|
+
* after `go.run` terminally poisons that API name because JavaScript cannot
|
|
14
|
+
* stop the old runtime; replace the Worker before retrying.
|
|
11
15
|
*
|
|
12
16
|
* **Single-Worker caveat.** Even with the chain, a second boot loaded into the
|
|
13
17
|
* SAME Worker after a first boot completes will overlay its Go runtime on top
|
package/lib/src/bootTtsc.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_BOOT_TIMEOUT_MS = void 0;
|
|
3
4
|
exports.bootTtsc = bootTtsc;
|
|
4
5
|
// Boots a host-built wasm and returns the JS-side handle.
|
|
5
6
|
//
|
|
@@ -10,16 +11,15 @@ exports.bootTtsc = bootTtsc;
|
|
|
10
11
|
// The boot helper is parameterized by `apiName` so any wasm built with
|
|
11
12
|
// `host.Expose(...)` can be loaded the same way. The base wasm uses "ttsc";
|
|
12
13
|
// downstream consumers pick their own (e.g. "ttscPlayground", "ttscTypia").
|
|
14
|
+
const BootTtscWorkerTerminationError_1 = require("./BootTtscWorkerTerminationError");
|
|
13
15
|
const createMemFS_1 = require("./createMemFS");
|
|
16
|
+
const bootsInFlight = new Map();
|
|
14
17
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* think they booted a fresh binary while the cached one stayed in place. The
|
|
19
|
-
* composite key lets HMR / cache-busting query strings get a fresh boot while
|
|
20
|
-
* still single-flighting genuine concurrent duplicate calls.
|
|
18
|
+
* Terminal failures keyed by the global API bridge they own. A failed runtime
|
|
19
|
+
* cannot be stopped after `go.run` starts, so no later boot may install another
|
|
20
|
+
* readiness bridge for that API name inside the same Worker.
|
|
21
21
|
*/
|
|
22
|
-
const
|
|
22
|
+
const terminalBootFailuresByApiName = new Map();
|
|
23
23
|
/**
|
|
24
24
|
* Per-apiName serialization chain. Two concurrent boots with the same apiName
|
|
25
25
|
* but different wasmUrls each install their own `globalThis[apiName
|
|
@@ -29,6 +29,8 @@ const bootsInFlight = new Map();
|
|
|
29
29
|
* Go-side`Ready.Invoke()` always lands on the resolver that boot installed.
|
|
30
30
|
*/
|
|
31
31
|
const bootChainByApiName = new Map();
|
|
32
|
+
/** Finite default for queueing, fetch, instantiation, and Go readiness. */
|
|
33
|
+
exports.DEFAULT_BOOT_TIMEOUT_MS = 60_000;
|
|
32
34
|
function bootKey(apiName, wasmUrl) {
|
|
33
35
|
return `${apiName}|${resolveWasmUrl(wasmUrl)}`;
|
|
34
36
|
}
|
|
@@ -54,8 +56,10 @@ function resolveWasmUrl(wasmUrl) {
|
|
|
54
56
|
* Concurrent calls with the same `(apiName, wasmUrl)` pair share the same
|
|
55
57
|
* in-flight boot. Calls with the same `apiName` but different `wasmUrl` are
|
|
56
58
|
* serialized via `bootChainByApiName` so they don't race on the shared
|
|
57
|
-
* `globalThis[apiName+"Ready"]` resolver slot.
|
|
58
|
-
*
|
|
59
|
+
* `globalThis[apiName+"Ready"]` resolver slot. A rejection before `go.run`
|
|
60
|
+
* clears the cache entries so the next call can retry from scratch. A rejection
|
|
61
|
+
* after `go.run` terminally poisons that API name because JavaScript cannot
|
|
62
|
+
* stop the old runtime; replace the Worker before retrying.
|
|
59
63
|
*
|
|
60
64
|
* **Single-Worker caveat.** Even with the chain, a second boot loaded into the
|
|
61
65
|
* SAME Worker after a first boot completes will overlay its Go runtime on top
|
|
@@ -69,30 +73,54 @@ function resolveWasmUrl(wasmUrl) {
|
|
|
69
73
|
function bootTtsc(options) {
|
|
70
74
|
const apiName = options.apiName ?? "ttsc";
|
|
71
75
|
const key = bootKey(apiName, options.wasmUrl);
|
|
76
|
+
const timeoutMs = resolveBootTimeout(options.timeoutMs);
|
|
77
|
+
const terminalFailure = terminalBootFailuresByApiName.get(apiName);
|
|
78
|
+
if (terminalFailure)
|
|
79
|
+
return Promise.reject(terminalFailure);
|
|
72
80
|
const inflight = bootsInFlight.get(key);
|
|
73
|
-
if (inflight)
|
|
74
|
-
|
|
81
|
+
if (inflight) {
|
|
82
|
+
attachBootCancellation(inflight, options.signal, timeoutMs);
|
|
83
|
+
return inflight.promise;
|
|
84
|
+
}
|
|
85
|
+
const controller = new AbortController();
|
|
75
86
|
const prior = bootChainByApiName.get(apiName) ?? Promise.resolve();
|
|
76
|
-
const
|
|
87
|
+
const queuedCancellation = createBootCancellationPromise(controller.signal, apiName, () => "waiting for an earlier boot");
|
|
88
|
+
const queued = prior
|
|
77
89
|
.catch(() => {
|
|
78
90
|
/* don't propagate a prior boot's rejection — let this one run */
|
|
79
91
|
})
|
|
80
|
-
.then(() =>
|
|
92
|
+
.then(() => {
|
|
93
|
+
throwIfBootWorkerTerminated(apiName);
|
|
94
|
+
throwIfBootCanceled(controller.signal, apiName, "waiting for an earlier boot");
|
|
95
|
+
queuedCancellation.dispose();
|
|
96
|
+
return bootTtscOnce(options, apiName, controller.signal);
|
|
97
|
+
});
|
|
98
|
+
let entry;
|
|
99
|
+
const promise = Promise.race([queued, queuedCancellation.promise])
|
|
81
100
|
.catch((err) => {
|
|
82
|
-
bootsInFlight.
|
|
101
|
+
if (bootsInFlight.get(key) === entry)
|
|
102
|
+
bootsInFlight.delete(key);
|
|
83
103
|
throw err;
|
|
84
|
-
})
|
|
85
|
-
|
|
104
|
+
})
|
|
105
|
+
.finally(queuedCancellation.dispose);
|
|
106
|
+
entry = { controller, promise };
|
|
107
|
+
bootsInFlight.set(key, entry);
|
|
108
|
+
attachBootCancellation(entry, options.signal, timeoutMs);
|
|
86
109
|
// Track the chain head for this apiName so the next boot waits on it.
|
|
87
110
|
// Swallow on the chain head specifically: we already throw to the
|
|
88
111
|
// immediate caller; surfacing it through the chain would reject every
|
|
89
112
|
// subsequent boot just because this one failed.
|
|
90
|
-
bootChainByApiName.set(apiName,
|
|
113
|
+
bootChainByApiName.set(apiName, queued.catch(() => { }));
|
|
91
114
|
return promise;
|
|
92
115
|
}
|
|
93
|
-
async function bootTtscOnce(options, apiName) {
|
|
116
|
+
async function bootTtscOnce(options, apiName, signal) {
|
|
94
117
|
const wasmUrl = options.wasmUrl;
|
|
95
118
|
const wasmExecUrl = options.wasmExecUrl ?? defaultWasmExecUrl(wasmUrl);
|
|
119
|
+
let phase = "preparing the wasm host";
|
|
120
|
+
const cancellation = createBootCancellationPromise(signal, apiName, () => phase);
|
|
121
|
+
let readyCb;
|
|
122
|
+
let failedCb;
|
|
123
|
+
let runtimeStarted = false;
|
|
96
124
|
const host = options.host ?? (0, createMemFS_1.createMemFS)();
|
|
97
125
|
const globalAny = globalThis;
|
|
98
126
|
// Install fs / process only if they aren't already in place, and remember
|
|
@@ -123,14 +151,18 @@ async function bootTtscOnce(options, apiName) {
|
|
|
123
151
|
delete globalAny.process;
|
|
124
152
|
};
|
|
125
153
|
try {
|
|
154
|
+
throwIfBootCanceled(signal, apiName, phase);
|
|
126
155
|
// wasm_exec.js installs `globalThis.Go`. It also reads globalThis.fs at
|
|
127
156
|
// module-eval time, so this import must follow the assignment above.
|
|
157
|
+
phase = `loading ${wasmExecUrl}`;
|
|
128
158
|
importScripts(wasmExecUrl);
|
|
159
|
+
// importScripts is synchronous and therefore cannot be interrupted while
|
|
160
|
+
// the script evaluates. Observe a cancellation that arrived around that
|
|
161
|
+
// boundary before starting any asynchronous work.
|
|
162
|
+
throwIfBootCanceled(signal, apiName, phase);
|
|
129
163
|
// Race the Ready resolver against a Failed signal so a wasm-side fault
|
|
130
164
|
// (e.g. `host.Expose` refusing a duplicate call) surfaces here instead of
|
|
131
165
|
// hanging on `await ready` forever.
|
|
132
|
-
let readyCb;
|
|
133
|
-
let failedCb;
|
|
134
166
|
const ready = new Promise((resolve, reject) => {
|
|
135
167
|
readyCb = () => {
|
|
136
168
|
delete globalAny[apiName + "Failed"];
|
|
@@ -148,11 +180,13 @@ async function bootTtscOnce(options, apiName) {
|
|
|
148
180
|
throw new Error(`bootTtsc: globalThis.Go was not installed by ${wasmExecUrl} — the file may not have loaded (CSP block, wrong content type, 404), or it is not the wasm_exec.js shipped with the Go toolchain.`);
|
|
149
181
|
}
|
|
150
182
|
const go = new goCtor();
|
|
151
|
-
|
|
183
|
+
phase = `fetching ${wasmUrl}`;
|
|
184
|
+
const response = await raceBootCancellation(fetch(wasmUrl, { signal }), cancellation.promise, signal, apiName, () => phase);
|
|
152
185
|
if (!response.ok) {
|
|
153
186
|
throw new Error(`bootTtsc: failed to fetch ${wasmUrl}: ${response.status}`);
|
|
154
187
|
}
|
|
155
|
-
|
|
188
|
+
phase = `instantiating ${wasmUrl}`;
|
|
189
|
+
const wasm = await raceBootCancellation(WebAssembly.instantiateStreaming(response, go.importObject), cancellation.promise, signal, apiName, () => phase);
|
|
156
190
|
// A normal host keeps `go.run` pending forever after signaling Ready, so a
|
|
157
191
|
// settlement (fulfil OR reject) BEFORE Ready means the Go runtime exited or
|
|
158
192
|
// panicked before it could register — e.g. an early `host.Expose` panic
|
|
@@ -161,6 +195,7 @@ async function bootTtscOnce(options, apiName) {
|
|
|
161
195
|
// The standard Go runner discards the exit code, so an unknown early exit
|
|
162
196
|
// can only synthesize a generic message; known host validation failures
|
|
163
197
|
// reject through Failed above and keep their original cause.
|
|
198
|
+
runtimeStarted = true;
|
|
164
199
|
const runPromise = Promise.resolve(go.run(wasm.instance));
|
|
165
200
|
const earlyExit = runPromise.then(() => {
|
|
166
201
|
throw new Error(`bootTtsc: the ${apiName} wasm runtime exited before signaling readiness (the host may have panicked; check the wasm stderr).`);
|
|
@@ -171,17 +206,8 @@ async function bootTtscOnce(options, apiName) {
|
|
|
171
206
|
// must not surface as an unhandled rejection. Attach a terminal handler to
|
|
172
207
|
// the losing branch.
|
|
173
208
|
earlyExit.catch(() => { });
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
finally {
|
|
178
|
-
// Drop this attempt's readiness bridge so a later boot for the same
|
|
179
|
-
// apiName installs a clean pair and no stale resolver survives.
|
|
180
|
-
if (globalAny[apiName + "Ready"] === readyCb)
|
|
181
|
-
delete globalAny[apiName + "Ready"];
|
|
182
|
-
if (globalAny[apiName + "Failed"] === failedCb)
|
|
183
|
-
delete globalAny[apiName + "Failed"];
|
|
184
|
-
}
|
|
209
|
+
phase = `waiting for ${apiName} readiness`;
|
|
210
|
+
await raceBootCancellation(Promise.race([ready, earlyExit]), cancellation.promise, signal, apiName, () => phase);
|
|
185
211
|
const api = globalAny[apiName];
|
|
186
212
|
if (!api)
|
|
187
213
|
throw new Error(`bootTtsc: ${apiName} global was not set — was the wasm built with host.Expose(${JSON.stringify(apiName)}, ...)?`);
|
|
@@ -189,8 +215,111 @@ async function bootTtscOnce(options, apiName) {
|
|
|
189
215
|
}
|
|
190
216
|
catch (err) {
|
|
191
217
|
restoreGlobals();
|
|
218
|
+
if (runtimeStarted)
|
|
219
|
+
throw poisonBootWorker(apiName, err);
|
|
192
220
|
throw err;
|
|
193
221
|
}
|
|
222
|
+
finally {
|
|
223
|
+
cancellation.dispose();
|
|
224
|
+
// Drop this attempt's readiness bridge on every path so failed fetches,
|
|
225
|
+
// instantiation failures, and cancellations cannot poison the next boot.
|
|
226
|
+
if (globalAny[apiName + "Ready"] === readyCb)
|
|
227
|
+
delete globalAny[apiName + "Ready"];
|
|
228
|
+
if (globalAny[apiName + "Failed"] === failedCb)
|
|
229
|
+
delete globalAny[apiName + "Failed"];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function throwIfBootWorkerTerminated(apiName) {
|
|
233
|
+
const failure = terminalBootFailuresByApiName.get(apiName);
|
|
234
|
+
if (failure)
|
|
235
|
+
throw failure;
|
|
236
|
+
}
|
|
237
|
+
function poisonBootWorker(apiName, cause) {
|
|
238
|
+
const existing = terminalBootFailuresByApiName.get(apiName);
|
|
239
|
+
if (existing)
|
|
240
|
+
return existing;
|
|
241
|
+
const failure = new BootTtscWorkerTerminationError_1.BootTtscWorkerTerminationError(apiName, cause);
|
|
242
|
+
terminalBootFailuresByApiName.set(apiName, failure);
|
|
243
|
+
return failure;
|
|
244
|
+
}
|
|
245
|
+
function resolveBootTimeout(timeoutMs) {
|
|
246
|
+
const value = timeoutMs ?? exports.DEFAULT_BOOT_TIMEOUT_MS;
|
|
247
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > 2_147_483_647)
|
|
248
|
+
throw new RangeError("bootTtsc: timeoutMs must be a positive integer no greater than 2147483647.");
|
|
249
|
+
return value;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Couple every caller's cancellation policy to the shared single-flight. A
|
|
253
|
+
* duplicate caller is joining the same mutable boot, so its cancellation
|
|
254
|
+
* cancels that shared attempt rather than pretending only one waiter left.
|
|
255
|
+
*/
|
|
256
|
+
function attachBootCancellation(entry, callerSignal, timeoutMs) {
|
|
257
|
+
const abortFromCaller = () => {
|
|
258
|
+
if (!entry.controller.signal.aborted)
|
|
259
|
+
entry.controller.abort({
|
|
260
|
+
kind: "abort",
|
|
261
|
+
reason: callerSignal?.reason,
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
if (callerSignal?.aborted)
|
|
265
|
+
abortFromCaller();
|
|
266
|
+
else
|
|
267
|
+
callerSignal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
268
|
+
const timer = setTimeout(() => {
|
|
269
|
+
if (!entry.controller.signal.aborted)
|
|
270
|
+
entry.controller.abort({
|
|
271
|
+
kind: "timeout",
|
|
272
|
+
timeoutMs,
|
|
273
|
+
});
|
|
274
|
+
}, timeoutMs);
|
|
275
|
+
const cleanup = () => {
|
|
276
|
+
clearTimeout(timer);
|
|
277
|
+
callerSignal?.removeEventListener("abort", abortFromCaller);
|
|
278
|
+
};
|
|
279
|
+
void entry.promise.then(cleanup, cleanup);
|
|
280
|
+
}
|
|
281
|
+
function createBootCancellationPromise(signal, apiName, getPhase) {
|
|
282
|
+
let rejectCancellation;
|
|
283
|
+
const promise = new Promise((_resolve, reject) => {
|
|
284
|
+
rejectCancellation = reject;
|
|
285
|
+
});
|
|
286
|
+
// Some synchronous boundaries observe cancellation with throwIfBootCanceled
|
|
287
|
+
// before a Promise.race consumes this branch. Keep that rejection owned.
|
|
288
|
+
void promise.catch(() => { });
|
|
289
|
+
const onAbort = () => {
|
|
290
|
+
rejectCancellation(bootCancellationError(signal, apiName, getPhase()));
|
|
291
|
+
};
|
|
292
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
293
|
+
if (signal.aborted)
|
|
294
|
+
onAbort();
|
|
295
|
+
return {
|
|
296
|
+
promise,
|
|
297
|
+
dispose: () => signal.removeEventListener("abort", onAbort),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
async function raceBootCancellation(work, cancellation, signal, apiName, getPhase) {
|
|
301
|
+
try {
|
|
302
|
+
return await Promise.race([work, cancellation]);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
if (signal.aborted)
|
|
306
|
+
throw bootCancellationError(signal, apiName, getPhase());
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function throwIfBootCanceled(signal, apiName, phase) {
|
|
311
|
+
if (signal.aborted)
|
|
312
|
+
throw bootCancellationError(signal, apiName, phase);
|
|
313
|
+
}
|
|
314
|
+
function bootCancellationError(signal, apiName, phase) {
|
|
315
|
+
const reason = signal.reason;
|
|
316
|
+
if (reason?.kind === "timeout")
|
|
317
|
+
return new Error(`bootTtsc: timed out after ${reason.timeoutMs}ms while ${phase} for ${apiName}.`);
|
|
318
|
+
const error = new Error(`bootTtsc: aborted while ${phase} for ${apiName}.`);
|
|
319
|
+
const cause = reason?.kind === "abort" ? reason.reason : signal.reason;
|
|
320
|
+
if (cause !== undefined)
|
|
321
|
+
error.cause = cause;
|
|
322
|
+
return error;
|
|
194
323
|
}
|
|
195
324
|
/**
|
|
196
325
|
* Derive the `wasm_exec.js` URL from the wasm URL by replacing the filename.
|
package/lib/src/bootTtsc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootTtsc.js","sourceRoot":"","sources":["../../src/bootTtsc.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bootTtsc.js","sourceRoot":"","sources":["../../src/bootTtsc.ts"],"names":[],"mappings":"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"}
|
package/lib/src/index.d.ts
CHANGED
package/lib/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
__exportStar(require("./bootTtsc"), exports);
|
|
24
|
+
__exportStar(require("./BootTtscWorkerTerminationError"), exports);
|
|
24
25
|
__exportStar(require("./createMemFS"), exports);
|
|
25
26
|
__exportStar(require("./MemFSError"), exports);
|
|
26
27
|
__exportStar(require("./parseResult"), exports);
|
package/lib/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,wEAAwE;AACxE,oBAAoB;;;;;;;;;;;;;;;;AAEpB,6CAA2B;AAC3B,gDAA8B;AAC9B,+CAA6B;AAC7B,gDAA8B;AAC9B,qDAAmC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,wEAAwE;AACxE,oBAAoB;;;;;;;;;;;;;;;;AAEpB,6CAA2B;AAC3B,mEAAiD;AACjD,gDAA8B;AAC9B,+CAA6B;AAC7B,gDAA8B;AAC9B,qDAAmC"}
|
|
@@ -3,6 +3,17 @@ import type { IMemFSHost } from "./IMemFSHost";
|
|
|
3
3
|
export interface IBootTtscOptions {
|
|
4
4
|
/** URL of the .wasm to fetch. */
|
|
5
5
|
wasmUrl: string;
|
|
6
|
+
/** Cancel this boot attempt, including a shared in-flight attempt. */
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
/**
|
|
9
|
+
* Maximum initialization time in milliseconds. Defaults to 60 seconds.
|
|
10
|
+
*
|
|
11
|
+
* The deadline covers queued, fetch, instantiate, and readiness phases.
|
|
12
|
+
* `importScripts` is synchronous and can only observe cancellation before or
|
|
13
|
+
* after it returns. A failure after `go.run` starts requires terminating the
|
|
14
|
+
* Worker; later boots of the same API name reject terminally.
|
|
15
|
+
*/
|
|
16
|
+
timeoutMs?: number;
|
|
6
17
|
/** URL of wasm_exec.js. Defaults to the same directory as wasmUrl. */
|
|
7
18
|
wasmExecUrl?: string;
|
|
8
19
|
/**
|
package/package.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A boot failure that requires replacing the current Web Worker.
|
|
3
|
+
*
|
|
4
|
+
* Once `go.run` starts, JavaScript cannot stop that Go runtime. Retrying the
|
|
5
|
+
* same API name in the same Worker would install a new readiness bridge that
|
|
6
|
+
* the stale runtime could invoke, so `bootTtsc` terminally rejects later boots
|
|
7
|
+
* until the caller terminates the Worker.
|
|
8
|
+
*/
|
|
9
|
+
export class BootTtscWorkerTerminationError extends Error {
|
|
10
|
+
public static readonly CODE = "TTSC_WASM_WORKER_TERMINATION_REQUIRED";
|
|
11
|
+
|
|
12
|
+
public readonly apiName: string;
|
|
13
|
+
public readonly code = BootTtscWorkerTerminationError.CODE;
|
|
14
|
+
public override readonly cause: unknown;
|
|
15
|
+
|
|
16
|
+
public constructor(apiName: string, cause: unknown) {
|
|
17
|
+
const causeMessage =
|
|
18
|
+
cause instanceof Error ? cause.message : String(cause ?? "boot failed");
|
|
19
|
+
super(
|
|
20
|
+
`[${BootTtscWorkerTerminationError.CODE}] ${causeMessage} bootTtsc: the ${apiName} Go runtime already started; terminate and replace this Worker before retrying.`,
|
|
21
|
+
);
|
|
22
|
+
this.name = "BootTtscWorkerTerminationError";
|
|
23
|
+
this.apiName = apiName;
|
|
24
|
+
this.cause = cause;
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/bootTtsc.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// The boot helper is parameterized by `apiName` so any wasm built with
|
|
8
8
|
// `host.Expose(...)` can be loaded the same way. The base wasm uses "ttsc";
|
|
9
9
|
// downstream consumers pick their own (e.g. "ttscPlayground", "ttscTypia").
|
|
10
|
+
import { BootTtscWorkerTerminationError } from "./BootTtscWorkerTerminationError";
|
|
10
11
|
import { createMemFS } from "./createMemFS";
|
|
11
12
|
import type { IBootResult } from "./structures/IBootResult";
|
|
12
13
|
import type { IBootTtscOptions } from "./structures/IBootTtscOptions";
|
|
@@ -22,7 +23,28 @@ declare const importScripts: (...urls: string[]) => void;
|
|
|
22
23
|
* composite key lets HMR / cache-busting query strings get a fresh boot while
|
|
23
24
|
* still single-flighting genuine concurrent duplicate calls.
|
|
24
25
|
*/
|
|
25
|
-
|
|
26
|
+
interface BootInFlight {
|
|
27
|
+
controller: AbortController;
|
|
28
|
+
promise: Promise<IBootResult>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface BootCancellationReason {
|
|
32
|
+
kind: "abort" | "timeout";
|
|
33
|
+
reason?: unknown;
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const bootsInFlight = new Map<string, BootInFlight>();
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Terminal failures keyed by the global API bridge they own. A failed runtime
|
|
41
|
+
* cannot be stopped after `go.run` starts, so no later boot may install another
|
|
42
|
+
* readiness bridge for that API name inside the same Worker.
|
|
43
|
+
*/
|
|
44
|
+
const terminalBootFailuresByApiName = new Map<
|
|
45
|
+
string,
|
|
46
|
+
BootTtscWorkerTerminationError
|
|
47
|
+
>();
|
|
26
48
|
|
|
27
49
|
/**
|
|
28
50
|
* Per-apiName serialization chain. Two concurrent boots with the same apiName
|
|
@@ -34,6 +56,9 @@ const bootsInFlight = new Map<string, Promise<IBootResult>>();
|
|
|
34
56
|
*/
|
|
35
57
|
const bootChainByApiName = new Map<string, Promise<unknown>>();
|
|
36
58
|
|
|
59
|
+
/** Finite default for queueing, fetch, instantiation, and Go readiness. */
|
|
60
|
+
export const DEFAULT_BOOT_TIMEOUT_MS = 60_000;
|
|
61
|
+
|
|
37
62
|
function bootKey(apiName: string, wasmUrl: string): string {
|
|
38
63
|
return `${apiName}|${resolveWasmUrl(wasmUrl)}`;
|
|
39
64
|
}
|
|
@@ -61,8 +86,10 @@ function resolveWasmUrl(wasmUrl: string): string {
|
|
|
61
86
|
* Concurrent calls with the same `(apiName, wasmUrl)` pair share the same
|
|
62
87
|
* in-flight boot. Calls with the same `apiName` but different `wasmUrl` are
|
|
63
88
|
* serialized via `bootChainByApiName` so they don't race on the shared
|
|
64
|
-
* `globalThis[apiName+"Ready"]` resolver slot.
|
|
65
|
-
*
|
|
89
|
+
* `globalThis[apiName+"Ready"]` resolver slot. A rejection before `go.run`
|
|
90
|
+
* clears the cache entries so the next call can retry from scratch. A rejection
|
|
91
|
+
* after `go.run` terminally poisons that API name because JavaScript cannot
|
|
92
|
+
* stop the old runtime; replace the Worker before retrying.
|
|
66
93
|
*
|
|
67
94
|
* **Single-Worker caveat.** Even with the chain, a second boot loaded into the
|
|
68
95
|
* SAME Worker after a first boot completes will overlay its Go runtime on top
|
|
@@ -76,26 +103,52 @@ function resolveWasmUrl(wasmUrl: string): string {
|
|
|
76
103
|
export function bootTtsc(options: IBootTtscOptions): Promise<IBootResult> {
|
|
77
104
|
const apiName = options.apiName ?? "ttsc";
|
|
78
105
|
const key = bootKey(apiName, options.wasmUrl);
|
|
106
|
+
const timeoutMs = resolveBootTimeout(options.timeoutMs);
|
|
107
|
+
const terminalFailure = terminalBootFailuresByApiName.get(apiName);
|
|
108
|
+
if (terminalFailure) return Promise.reject(terminalFailure);
|
|
79
109
|
const inflight = bootsInFlight.get(key);
|
|
80
|
-
if (inflight)
|
|
110
|
+
if (inflight) {
|
|
111
|
+
attachBootCancellation(inflight, options.signal, timeoutMs);
|
|
112
|
+
return inflight.promise;
|
|
113
|
+
}
|
|
114
|
+
const controller = new AbortController();
|
|
81
115
|
const prior = bootChainByApiName.get(apiName) ?? Promise.resolve();
|
|
82
|
-
const
|
|
116
|
+
const queuedCancellation = createBootCancellationPromise(
|
|
117
|
+
controller.signal,
|
|
118
|
+
apiName,
|
|
119
|
+
() => "waiting for an earlier boot",
|
|
120
|
+
);
|
|
121
|
+
const queued = prior
|
|
83
122
|
.catch(() => {
|
|
84
123
|
/* don't propagate a prior boot's rejection — let this one run */
|
|
85
124
|
})
|
|
86
|
-
.then(() =>
|
|
125
|
+
.then(() => {
|
|
126
|
+
throwIfBootWorkerTerminated(apiName);
|
|
127
|
+
throwIfBootCanceled(
|
|
128
|
+
controller.signal,
|
|
129
|
+
apiName,
|
|
130
|
+
"waiting for an earlier boot",
|
|
131
|
+
);
|
|
132
|
+
queuedCancellation.dispose();
|
|
133
|
+
return bootTtscOnce(options, apiName, controller.signal);
|
|
134
|
+
});
|
|
135
|
+
let entry!: BootInFlight;
|
|
136
|
+
const promise = Promise.race([queued, queuedCancellation.promise])
|
|
87
137
|
.catch((err) => {
|
|
88
|
-
bootsInFlight.delete(key);
|
|
138
|
+
if (bootsInFlight.get(key) === entry) bootsInFlight.delete(key);
|
|
89
139
|
throw err;
|
|
90
|
-
})
|
|
91
|
-
|
|
140
|
+
})
|
|
141
|
+
.finally(queuedCancellation.dispose);
|
|
142
|
+
entry = { controller, promise };
|
|
143
|
+
bootsInFlight.set(key, entry);
|
|
144
|
+
attachBootCancellation(entry, options.signal, timeoutMs);
|
|
92
145
|
// Track the chain head for this apiName so the next boot waits on it.
|
|
93
146
|
// Swallow on the chain head specifically: we already throw to the
|
|
94
147
|
// immediate caller; surfacing it through the chain would reject every
|
|
95
148
|
// subsequent boot just because this one failed.
|
|
96
149
|
bootChainByApiName.set(
|
|
97
150
|
apiName,
|
|
98
|
-
|
|
151
|
+
queued.catch(() => {}),
|
|
99
152
|
);
|
|
100
153
|
return promise;
|
|
101
154
|
}
|
|
@@ -103,9 +156,19 @@ export function bootTtsc(options: IBootTtscOptions): Promise<IBootResult> {
|
|
|
103
156
|
async function bootTtscOnce(
|
|
104
157
|
options: IBootTtscOptions,
|
|
105
158
|
apiName: string,
|
|
159
|
+
signal: AbortSignal,
|
|
106
160
|
): Promise<IBootResult> {
|
|
107
161
|
const wasmUrl = options.wasmUrl;
|
|
108
162
|
const wasmExecUrl = options.wasmExecUrl ?? defaultWasmExecUrl(wasmUrl);
|
|
163
|
+
let phase = "preparing the wasm host";
|
|
164
|
+
const cancellation = createBootCancellationPromise(
|
|
165
|
+
signal,
|
|
166
|
+
apiName,
|
|
167
|
+
() => phase,
|
|
168
|
+
);
|
|
169
|
+
let readyCb: (() => void) | undefined;
|
|
170
|
+
let failedCb: ((err: unknown) => void) | undefined;
|
|
171
|
+
let runtimeStarted = false;
|
|
109
172
|
|
|
110
173
|
const host = options.host ?? createMemFS();
|
|
111
174
|
const globalAny = globalThis as Record<string, unknown>;
|
|
@@ -137,15 +200,19 @@ async function bootTtscOnce(
|
|
|
137
200
|
};
|
|
138
201
|
|
|
139
202
|
try {
|
|
203
|
+
throwIfBootCanceled(signal, apiName, phase);
|
|
140
204
|
// wasm_exec.js installs `globalThis.Go`. It also reads globalThis.fs at
|
|
141
205
|
// module-eval time, so this import must follow the assignment above.
|
|
206
|
+
phase = `loading ${wasmExecUrl}`;
|
|
142
207
|
importScripts(wasmExecUrl);
|
|
208
|
+
// importScripts is synchronous and therefore cannot be interrupted while
|
|
209
|
+
// the script evaluates. Observe a cancellation that arrived around that
|
|
210
|
+
// boundary before starting any asynchronous work.
|
|
211
|
+
throwIfBootCanceled(signal, apiName, phase);
|
|
143
212
|
|
|
144
213
|
// Race the Ready resolver against a Failed signal so a wasm-side fault
|
|
145
214
|
// (e.g. `host.Expose` refusing a duplicate call) surfaces here instead of
|
|
146
215
|
// hanging on `await ready` forever.
|
|
147
|
-
let readyCb!: () => void;
|
|
148
|
-
let failedCb!: (err: unknown) => void;
|
|
149
216
|
const ready = new Promise<void>((resolve, reject) => {
|
|
150
217
|
readyCb = () => {
|
|
151
218
|
delete globalAny[apiName + "Failed"];
|
|
@@ -167,15 +234,26 @@ async function bootTtscOnce(
|
|
|
167
234
|
}
|
|
168
235
|
const go = new goCtor();
|
|
169
236
|
|
|
170
|
-
|
|
237
|
+
phase = `fetching ${wasmUrl}`;
|
|
238
|
+
const response = await raceBootCancellation(
|
|
239
|
+
fetch(wasmUrl, { signal }),
|
|
240
|
+
cancellation.promise,
|
|
241
|
+
signal,
|
|
242
|
+
apiName,
|
|
243
|
+
() => phase,
|
|
244
|
+
);
|
|
171
245
|
if (!response.ok) {
|
|
172
246
|
throw new Error(
|
|
173
247
|
`bootTtsc: failed to fetch ${wasmUrl}: ${response.status}`,
|
|
174
248
|
);
|
|
175
249
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
go.importObject,
|
|
250
|
+
phase = `instantiating ${wasmUrl}`;
|
|
251
|
+
const wasm = await raceBootCancellation(
|
|
252
|
+
WebAssembly.instantiateStreaming(response, go.importObject),
|
|
253
|
+
cancellation.promise,
|
|
254
|
+
signal,
|
|
255
|
+
apiName,
|
|
256
|
+
() => phase,
|
|
179
257
|
);
|
|
180
258
|
|
|
181
259
|
// A normal host keeps `go.run` pending forever after signaling Ready, so a
|
|
@@ -186,6 +264,7 @@ async function bootTtscOnce(
|
|
|
186
264
|
// The standard Go runner discards the exit code, so an unknown early exit
|
|
187
265
|
// can only synthesize a generic message; known host validation failures
|
|
188
266
|
// reject through Failed above and keep their original cause.
|
|
267
|
+
runtimeStarted = true;
|
|
189
268
|
const runPromise = Promise.resolve(go.run(wasm.instance));
|
|
190
269
|
const earlyExit = runPromise.then(
|
|
191
270
|
() => {
|
|
@@ -206,16 +285,14 @@ async function bootTtscOnce(
|
|
|
206
285
|
// the losing branch.
|
|
207
286
|
earlyExit.catch(() => {});
|
|
208
287
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
delete globalAny[apiName + "Failed"];
|
|
218
|
-
}
|
|
288
|
+
phase = `waiting for ${apiName} readiness`;
|
|
289
|
+
await raceBootCancellation(
|
|
290
|
+
Promise.race([ready, earlyExit]),
|
|
291
|
+
cancellation.promise,
|
|
292
|
+
signal,
|
|
293
|
+
apiName,
|
|
294
|
+
() => phase,
|
|
295
|
+
);
|
|
219
296
|
|
|
220
297
|
const api = (globalAny as Record<string, ITtscApi | undefined>)[apiName];
|
|
221
298
|
if (!api)
|
|
@@ -225,10 +302,147 @@ async function bootTtscOnce(
|
|
|
225
302
|
return { api, host };
|
|
226
303
|
} catch (err) {
|
|
227
304
|
restoreGlobals();
|
|
305
|
+
if (runtimeStarted) throw poisonBootWorker(apiName, err);
|
|
228
306
|
throw err;
|
|
307
|
+
} finally {
|
|
308
|
+
cancellation.dispose();
|
|
309
|
+
// Drop this attempt's readiness bridge on every path so failed fetches,
|
|
310
|
+
// instantiation failures, and cancellations cannot poison the next boot.
|
|
311
|
+
if (globalAny[apiName + "Ready"] === readyCb)
|
|
312
|
+
delete globalAny[apiName + "Ready"];
|
|
313
|
+
if (globalAny[apiName + "Failed"] === failedCb)
|
|
314
|
+
delete globalAny[apiName + "Failed"];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function throwIfBootWorkerTerminated(apiName: string): void {
|
|
319
|
+
const failure = terminalBootFailuresByApiName.get(apiName);
|
|
320
|
+
if (failure) throw failure;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function poisonBootWorker(
|
|
324
|
+
apiName: string,
|
|
325
|
+
cause: unknown,
|
|
326
|
+
): BootTtscWorkerTerminationError {
|
|
327
|
+
const existing = terminalBootFailuresByApiName.get(apiName);
|
|
328
|
+
if (existing) return existing;
|
|
329
|
+
const failure = new BootTtscWorkerTerminationError(apiName, cause);
|
|
330
|
+
terminalBootFailuresByApiName.set(apiName, failure);
|
|
331
|
+
return failure;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
interface BootCancellation {
|
|
335
|
+
promise: Promise<never>;
|
|
336
|
+
dispose: () => void;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function resolveBootTimeout(timeoutMs: number | undefined): number {
|
|
340
|
+
const value = timeoutMs ?? DEFAULT_BOOT_TIMEOUT_MS;
|
|
341
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > 2_147_483_647)
|
|
342
|
+
throw new RangeError(
|
|
343
|
+
"bootTtsc: timeoutMs must be a positive integer no greater than 2147483647.",
|
|
344
|
+
);
|
|
345
|
+
return value;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Couple every caller's cancellation policy to the shared single-flight. A
|
|
350
|
+
* duplicate caller is joining the same mutable boot, so its cancellation
|
|
351
|
+
* cancels that shared attempt rather than pretending only one waiter left.
|
|
352
|
+
*/
|
|
353
|
+
function attachBootCancellation(
|
|
354
|
+
entry: BootInFlight,
|
|
355
|
+
callerSignal: AbortSignal | undefined,
|
|
356
|
+
timeoutMs: number,
|
|
357
|
+
): void {
|
|
358
|
+
const abortFromCaller = (): void => {
|
|
359
|
+
if (!entry.controller.signal.aborted)
|
|
360
|
+
entry.controller.abort({
|
|
361
|
+
kind: "abort",
|
|
362
|
+
reason: callerSignal?.reason,
|
|
363
|
+
} satisfies BootCancellationReason);
|
|
364
|
+
};
|
|
365
|
+
if (callerSignal?.aborted) abortFromCaller();
|
|
366
|
+
else callerSignal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
367
|
+
|
|
368
|
+
const timer = setTimeout(() => {
|
|
369
|
+
if (!entry.controller.signal.aborted)
|
|
370
|
+
entry.controller.abort({
|
|
371
|
+
kind: "timeout",
|
|
372
|
+
timeoutMs,
|
|
373
|
+
} satisfies BootCancellationReason);
|
|
374
|
+
}, timeoutMs);
|
|
375
|
+
const cleanup = (): void => {
|
|
376
|
+
clearTimeout(timer);
|
|
377
|
+
callerSignal?.removeEventListener("abort", abortFromCaller);
|
|
378
|
+
};
|
|
379
|
+
void entry.promise.then(cleanup, cleanup);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function createBootCancellationPromise(
|
|
383
|
+
signal: AbortSignal,
|
|
384
|
+
apiName: string,
|
|
385
|
+
getPhase: () => string,
|
|
386
|
+
): BootCancellation {
|
|
387
|
+
let rejectCancellation!: (error: Error) => void;
|
|
388
|
+
const promise = new Promise<never>((_resolve, reject) => {
|
|
389
|
+
rejectCancellation = reject;
|
|
390
|
+
});
|
|
391
|
+
// Some synchronous boundaries observe cancellation with throwIfBootCanceled
|
|
392
|
+
// before a Promise.race consumes this branch. Keep that rejection owned.
|
|
393
|
+
void promise.catch(() => {});
|
|
394
|
+
const onAbort = (): void => {
|
|
395
|
+
rejectCancellation(bootCancellationError(signal, apiName, getPhase()));
|
|
396
|
+
};
|
|
397
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
398
|
+
if (signal.aborted) onAbort();
|
|
399
|
+
return {
|
|
400
|
+
promise,
|
|
401
|
+
dispose: () => signal.removeEventListener("abort", onAbort),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async function raceBootCancellation<T>(
|
|
406
|
+
work: Promise<T>,
|
|
407
|
+
cancellation: Promise<never>,
|
|
408
|
+
signal: AbortSignal,
|
|
409
|
+
apiName: string,
|
|
410
|
+
getPhase: () => string,
|
|
411
|
+
): Promise<T> {
|
|
412
|
+
try {
|
|
413
|
+
return await Promise.race([work, cancellation]);
|
|
414
|
+
} catch (error) {
|
|
415
|
+
if (signal.aborted)
|
|
416
|
+
throw bootCancellationError(signal, apiName, getPhase());
|
|
417
|
+
throw error;
|
|
229
418
|
}
|
|
230
419
|
}
|
|
231
420
|
|
|
421
|
+
function throwIfBootCanceled(
|
|
422
|
+
signal: AbortSignal,
|
|
423
|
+
apiName: string,
|
|
424
|
+
phase: string,
|
|
425
|
+
): void {
|
|
426
|
+
if (signal.aborted) throw bootCancellationError(signal, apiName, phase);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function bootCancellationError(
|
|
430
|
+
signal: AbortSignal,
|
|
431
|
+
apiName: string,
|
|
432
|
+
phase: string,
|
|
433
|
+
): Error {
|
|
434
|
+
const reason = signal.reason as BootCancellationReason | undefined;
|
|
435
|
+
if (reason?.kind === "timeout")
|
|
436
|
+
return new Error(
|
|
437
|
+
`bootTtsc: timed out after ${reason.timeoutMs}ms while ${phase} for ${apiName}.`,
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
const error = new Error(`bootTtsc: aborted while ${phase} for ${apiName}.`);
|
|
441
|
+
const cause = reason?.kind === "abort" ? reason.reason : signal.reason;
|
|
442
|
+
if (cause !== undefined) (error as Error & { cause?: unknown }).cause = cause;
|
|
443
|
+
return error;
|
|
444
|
+
}
|
|
445
|
+
|
|
232
446
|
/**
|
|
233
447
|
* Derive the `wasm_exec.js` URL from the wasm URL by replacing the filename.
|
|
234
448
|
*
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,17 @@ import type { IMemFSHost } from "./IMemFSHost";
|
|
|
4
4
|
export interface IBootTtscOptions {
|
|
5
5
|
/** URL of the .wasm to fetch. */
|
|
6
6
|
wasmUrl: string;
|
|
7
|
+
/** Cancel this boot attempt, including a shared in-flight attempt. */
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
/**
|
|
10
|
+
* Maximum initialization time in milliseconds. Defaults to 60 seconds.
|
|
11
|
+
*
|
|
12
|
+
* The deadline covers queued, fetch, instantiate, and readiness phases.
|
|
13
|
+
* `importScripts` is synchronous and can only observe cancellation before or
|
|
14
|
+
* after it returns. A failure after `go.run` starts requires terminating the
|
|
15
|
+
* Worker; later boots of the same API name reject terminally.
|
|
16
|
+
*/
|
|
17
|
+
timeoutMs?: number;
|
|
7
18
|
/** URL of wasm_exec.js. Defaults to the same directory as wasmUrl. */
|
|
8
19
|
wasmExecUrl?: string;
|
|
9
20
|
/**
|