@nimbus-sh/worker 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_shared/session-router.d.ts +8 -0
- package/dist/_shared/session-router.d.ts.map +1 -1
- package/dist/_shared/session-router.js +8 -0
- package/dist/facets/cirrus-real.js +1 -1
- package/dist/facets/manager.d.ts +40 -126
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +97 -313
- package/dist/facets/opencode-staging.d.ts +1 -1
- package/dist/facets/opencode-staging.d.ts.map +1 -1
- package/dist/facets/opencode-staging.js +4 -0
- package/dist/facets/vite-dev-server.d.ts +2 -2
- package/dist/facets/vite-dev-server.js +4 -4
- package/dist/git/commands.d.ts +8 -0
- package/dist/git/commands.d.ts.map +1 -1
- package/dist/git/commands.js +41 -9
- package/dist/git/network-facet.d.ts +1 -1
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +35 -12
- package/dist/git-bundle.generated.d.ts +2 -2
- package/dist/git-bundle.generated.d.ts.map +1 -1
- package/dist/git-bundle.generated.js +3 -3
- package/dist/index.js +1 -1
- package/dist/loaders/child-process/spawn-facet.js +1 -1
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +2 -2
- package/dist/loaders/generated-workers.d.ts +1 -1
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +2 -2
- package/dist/loaders/npm-resolve-preamble.d.ts +3 -3
- package/dist/loaders/npm-resolve-preamble.js +3 -3
- package/dist/loaders/pre-bundle-preamble.d.ts +5 -5
- package/dist/loaders/pre-bundle-preamble.js +6 -6
- package/dist/loaders/process-host.d.ts +6 -107
- package/dist/loaders/process-host.d.ts.map +1 -1
- package/dist/loaders/process-host.js +6 -405
- package/dist/npm/install-batch-facet.js +1 -1
- package/dist/npm/installer.d.ts +4 -4
- package/dist/npm/installer.js +18 -18
- package/dist/npm/pre-bundle-facet.d.ts +2 -2
- package/dist/npm/pre-bundle-facet.js +5 -5
- package/dist/npm/resolve-facet.d.ts +1 -1
- package/dist/npm/resolve-facet.js +1 -1
- package/dist/npm/resolve-one-facet.d.ts +5 -5
- package/dist/npm/resolve-one-facet.js +5 -5
- package/dist/router/index.js +1 -1
- package/dist/router/remote-api.js +8 -0
- package/dist/runtime/bun-repl.d.ts +1 -1
- package/dist/runtime/bun-repl.js +3 -3
- package/dist/runtime/facet-loader-host.d.ts +5 -8
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +7 -11
- package/dist/runtime/node-repl.js +2 -2
- package/dist/runtime/python-repl.js +3 -3
- package/dist/runtime/ruby-repl.js +2 -2
- package/dist/runtime/ruby-resident.js +1 -1
- package/dist/session/hibernation.d.ts +18 -51
- package/dist/session/hibernation.d.ts.map +1 -1
- package/dist/session/hibernation.js +49 -186
- package/dist/session/init-phases.d.ts +2 -2
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +1 -1
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +6 -3
- package/dist/session/keys.d.ts +14 -37
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js +17 -37
- package/dist/session/nimbus-session.d.ts +10 -4
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +20 -11
- package/dist/session/port-capability.d.ts +43 -0
- package/dist/session/port-capability.d.ts.map +1 -0
- package/dist/session/port-capability.js +49 -0
- package/dist/session/programmatic.d.ts +41 -5
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +147 -15
- package/dist/session/routes.d.ts +2 -0
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +91 -16
- package/dist/session/rpc.d.ts +35 -8
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +73 -14
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +2 -0
- package/dist/session/state-store.d.ts +3 -2
- package/dist/session/state-store.d.ts.map +1 -1
- package/dist/session/state-store.js +3 -2
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +5 -0
- package/dist/session/ws.d.ts +2 -2
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +2 -2
- package/dist/wrangler/nimbus-wrangler.js +1 -1
- package/package.json +3 -2
- package/dist/facets/inner-do-registry.d.ts +0 -41
- package/dist/facets/inner-do-registry.d.ts.map +0 -1
- package/dist/facets/inner-do-registry.js +0 -51
- package/dist/facets/launch-pacer.d.ts +0 -111
- package/dist/facets/launch-pacer.d.ts.map +0 -1
- package/dist/facets/launch-pacer.js +0 -130
- package/dist/loaders/fanout-pool.d.ts +0 -207
- package/dist/loaders/fanout-pool.d.ts.map +0 -1
- package/dist/loaders/fanout-pool.js +0 -363
- package/dist/loaders/loader-pool.d.ts +0 -294
- package/dist/loaders/loader-pool.d.ts.map +0 -1
- package/dist/loaders/loader-pool.js +0 -642
- package/dist/loaders/process-fabric.d.ts +0 -509
- package/dist/loaders/process-fabric.d.ts.map +0 -1
- package/dist/loaders/process-fabric.js +0 -360
- package/dist/loaders/vendor/errors.d.ts +0 -24
- package/dist/loaders/vendor/errors.d.ts.map +0 -1
- package/dist/loaders/vendor/errors.js +0 -46
- package/dist/loaders/vendor/serialize.d.ts +0 -3
- package/dist/loaders/vendor/serialize.d.ts.map +0 -1
- package/dist/loaders/vendor/serialize.js +0 -25
- package/dist/loaders/vendor/types.d.ts +0 -69
- package/dist/loaders/vendor/types.d.ts.map +0 -1
- package/dist/loaders/vendor/types.js +0 -4
- package/dist/loaders/workerd-facet-host.d.ts +0 -141
- package/dist/loaders/workerd-facet-host.d.ts.map +0 -1
- package/dist/loaders/workerd-facet-host.js +0 -284
- package/dist/session/bindings.d.ts +0 -209
- package/dist/session/bindings.d.ts.map +0 -1
- package/dist/session/bindings.js +0 -680
- package/dist/session/ctx-exports.d.ts +0 -16
- package/dist/session/ctx-exports.d.ts.map +0 -1
- package/dist/session/ctx-exports.js +0 -22
- package/dist/session/ws-hibernation-config.d.ts +0 -64
- package/dist/session/ws-hibernation-config.d.ts.map +0 -1
- package/dist/session/ws-hibernation-config.js +0 -89
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* process-fabric.ts — the resident-process scheduler, and the process half of
|
|
3
|
-
* the substrate it runs on.
|
|
4
|
-
*
|
|
5
|
-
* Every long-lived process Nimbus runs — node servers, python/ruby socket
|
|
6
|
-
* servers, the opencode TUI and its headless server — runs as a **DO Facet**:
|
|
7
|
-
* a named child actor whose class comes from a dynamic worker, opened by
|
|
8
|
-
* `openResidentFacet` in `loaders/workerd-facet-host.ts`.
|
|
9
|
-
*
|
|
10
|
-
* ctx.facets.get(`proc-${pid}`, () => ({
|
|
11
|
-
* class: env.LOADER.get(workerKey, buildConfig)
|
|
12
|
-
* .getDurableObjectClass('NimbusProcess'),
|
|
13
|
-
* }))
|
|
14
|
-
*
|
|
15
|
-
* There is ONE process implementation. What varies is WHOSE `ctx` and `env`
|
|
16
|
-
* that call runs against — the user's own session DO, or a sibling DO acting
|
|
17
|
-
* as a host — and that choice is a single deployment-wide config value read in
|
|
18
|
-
* `loaders/process-host.ts`. Nothing here, and nothing above here, branches on
|
|
19
|
-
* which program is running: no spawn site picks its own substrate, and no
|
|
20
|
-
* program name, mode or payload size reaches the selection.
|
|
21
|
-
*
|
|
22
|
-
* What each substrate costs, all of it measured on the production
|
|
23
|
-
* compatibility shape (see `loaders/process-host.ts` for the operator-facing
|
|
24
|
-
* version of this table):
|
|
25
|
-
*
|
|
26
|
-
* facet — spawn 8-16 ms warm. Memory independent: its OWN ~208 MiB
|
|
27
|
-
* envelope, identical whether the coordinator holds 0 or 128 MiB,
|
|
28
|
-
* with 1,664 MiB live across 8 facets + parent. CPU SHARED with
|
|
29
|
-
* its siblings, because facets are separate isolates inside one
|
|
30
|
-
* actor thread: awaiting I/O yields that thread completely (a
|
|
31
|
-
* sibling's RPC latency while a facet parks on a socket, on stdin
|
|
32
|
-
* or on an outbound call is indistinguishable from idle) but
|
|
33
|
-
* sustained CPU stalls every sibling for its full duration —
|
|
34
|
-
* a python HTTP server at 32-way saturation held siblings under
|
|
35
|
-
* 1.06 s (p50 231 ms), the opencode attach TUI held them at the
|
|
36
|
-
* 77 ms idle baseline, and a deliberate 9,956 ms CPU burn stalled
|
|
37
|
-
* them for 9,966 ms.
|
|
38
|
-
* peer — spawn 242-359 ms, because every spawn pays a DO create plus a
|
|
39
|
-
* SQLite open. Memory AND CPU both independent: the process runs
|
|
40
|
-
* in a different workerd process, verified per placement rather
|
|
41
|
-
* than assumed (see `_place` in `loaders/process-host.ts`).
|
|
42
|
-
*
|
|
43
|
-
* Both give the process its own SQLite. Neither changes what the process is:
|
|
44
|
-
* the runner, the boot spec, the class name, the writer handshake and the
|
|
45
|
-
* lifecycle contract are the same code either way.
|
|
46
|
-
*
|
|
47
|
-
* The facet's SUPERVISOR binding is minted for the COORDINATOR's doId, so
|
|
48
|
-
* every syscall — VFS read/write, stdout/stderr frames, stdin pump,
|
|
49
|
-
* registerPort, loopback HTTP — lands on the user's session DO wherever the
|
|
50
|
-
* process runs. Because that binding is minted by an actor rather than by a
|
|
51
|
-
* stateless entrypoint, it lives as long as the process does; nothing has to
|
|
52
|
-
* hold a call open to keep it alive.
|
|
53
|
-
*
|
|
54
|
-
* Boot specs
|
|
55
|
-
* ──────────
|
|
56
|
-
* A resident process boots from one of two specs, and in both cases the module
|
|
57
|
-
* map is assembled LAZILY inside the loader's cache-miss callback — so the
|
|
58
|
-
* artifact sources are materialized only when the facet actually starts, and
|
|
59
|
-
* only for as long as the load takes:
|
|
60
|
-
*
|
|
61
|
-
* staged — an OpencodeStageSpec; `assembleOpencodeFacetConfig` fetches the
|
|
62
|
-
* artifact sources from ASSETS.
|
|
63
|
-
* code — a generated module map (node / python / ruby runners). Fixed-size
|
|
64
|
-
* module text rides inline; anything sized by the user's disk is
|
|
65
|
-
* named BY VFS PATH and read through the injected disk reader. A
|
|
66
|
-
* ruby server's `ruby+stdlib.wasm` alone is 34.3 MiB and a node
|
|
67
|
-
* facet's disk snapshot reached 44 MB for pi.
|
|
68
|
-
*
|
|
69
|
-
* By-path is what lets a boot spec reach EITHER substrate. Inline, pi's node
|
|
70
|
-
* snapshot serialized to 44,252,709 bytes and died at workerd's 32 MiB RPC
|
|
71
|
-
* ceiling the moment it had to cross to a peer; named by path it sends zero
|
|
72
|
-
* bytes, and the host reads them off the coordinator's own disk through the
|
|
73
|
-
* `ResidentDiskReader` it was given.
|
|
74
|
-
*/
|
|
75
|
-
import { z } from 'zod/v4';
|
|
76
|
-
import { OpencodeStageSpecSchema } from '../facets/opencode-staging.js';
|
|
77
|
-
/**
|
|
78
|
-
* The class every generated resident runner exports. One name for every
|
|
79
|
-
* runtime: the fabric names it unconditionally, so nothing about which program
|
|
80
|
-
* is running reaches this module.
|
|
81
|
-
*/
|
|
82
|
-
export const RESIDENT_PROCESS_CLASS = 'NimbusProcess';
|
|
83
|
-
/**
|
|
84
|
-
* A generated module map. Only bounded, fixed-size module text rides inline;
|
|
85
|
-
* anything whose size is a function of the user's disk is named by VFS path
|
|
86
|
-
* and read when the facet loads, so the bytes are transient rather than
|
|
87
|
-
* resident in the coordinator's heap.
|
|
88
|
-
*/
|
|
89
|
-
export const ResidentCodeSpecSchema = z.object({
|
|
90
|
-
compatibilityDate: z.string().min(1),
|
|
91
|
-
compatibilityFlags: z.array(z.string()),
|
|
92
|
-
mainModule: z.string().min(1),
|
|
93
|
-
/**
|
|
94
|
-
* Inline modules: fixed-size generated source, plus small wasm sidecars that
|
|
95
|
-
* come from the worker's own ASSETS rather than the user's disk.
|
|
96
|
-
*/
|
|
97
|
-
modules: z.record(z.string(), z.union([z.string(), z.object({ wasm: z.instanceof(ArrayBuffer) })])),
|
|
98
|
-
/**
|
|
99
|
-
* Module name → absolute VFS path of a wasm image to materialize at load.
|
|
100
|
-
* This is how the big user-installed runtimes travel: ruby's
|
|
101
|
-
* interpreter+stdlib image alone is 34.3 MiB.
|
|
102
|
-
*/
|
|
103
|
-
vfsWasmModules: z.record(z.string(), z.string()).optional(),
|
|
104
|
-
/**
|
|
105
|
-
* Module name → absolute VFS path of a GENERATED module source, read as
|
|
106
|
-
* UTF-8 at load. The same by-path posture as `vfsWasmModules`, for module
|
|
107
|
-
* text whose size is a function of the user's disk.
|
|
108
|
-
*
|
|
109
|
-
* A node facet carries a snapshot of that disk, and it is the largest thing
|
|
110
|
-
* Nimbus generates: pi's is 3096 cells and inline it serialized to
|
|
111
|
-
* 44,252,709 bytes. That text cannot be rebuilt from the user's files at
|
|
112
|
-
* load time either — two thirds of the cells are esbuild ESM→CJS output, and
|
|
113
|
-
* the manifest and metadata members are walks of the tree rather than files
|
|
114
|
-
* in it. So the generator materializes its output in the content-addressed
|
|
115
|
-
* image store below and the spec names it.
|
|
116
|
-
*/
|
|
117
|
-
vfsTextModules: z.record(z.string(), z.string()).optional(),
|
|
118
|
-
});
|
|
119
|
-
export const ResidentBootSpecSchema = z.discriminatedUnion('kind', [
|
|
120
|
-
z.object({ kind: z.literal('staged'), stage: OpencodeStageSpecSchema }),
|
|
121
|
-
z.object({ kind: z.literal('code'), code: ResidentCodeSpecSchema }),
|
|
122
|
-
]);
|
|
123
|
-
// ── Boot-image store ────────────────────────────────────────────────────────
|
|
124
|
-
/**
|
|
125
|
-
* Where a generated module source is materialized so a boot spec can name it.
|
|
126
|
-
*
|
|
127
|
-
* Outside any user working tree on purpose. The passes that build a node
|
|
128
|
-
* facet's snapshot enumerate the process's cwd, so an image written under one
|
|
129
|
-
* would be swept into the next snapshot — and that snapshot is what produced
|
|
130
|
-
* the image, so each spawn would grow the thing it just wrote.
|
|
131
|
-
*
|
|
132
|
-
* Kernel-owned and world-readable: the generator writes as CRED_KERNEL, and
|
|
133
|
-
* every process reads through a supervisor binding that enforces its own
|
|
134
|
-
* credential. Mode 0644 is what makes the read succeed for any process by
|
|
135
|
-
* construction rather than by a privilege carve-out in the permission layer,
|
|
136
|
-
* and leaves the bytes beyond reach of the user whose program they encode.
|
|
137
|
-
*/
|
|
138
|
-
export const FACET_IMAGE_DIR = 'var/lib/nimbus/facet-images';
|
|
139
|
-
/**
|
|
140
|
-
* An image is named by the SHA-256 of its own bytes, so its name IS its
|
|
141
|
-
* integrity check and a stale image is not something to invalidate but
|
|
142
|
-
* something that cannot be addressed: different generated text is a different
|
|
143
|
-
* path.
|
|
144
|
-
*
|
|
145
|
-
* What that actually dedups, measured on a deployed worker rather than
|
|
146
|
-
* assumed: a RESTART resolves to the image already there, because the fabric
|
|
147
|
-
* replays one unchanged boot spec. Two separate spawns of the same tool do
|
|
148
|
-
* NOT, whenever the generated text carries anything per-process: an
|
|
149
|
-
* attached-TTY spawn bakes `NIMBUS_CP_CHILD_PID` into `__NIMBUS_ARGS`, so `pi`
|
|
150
|
-
* twice wrote two images (2c3a90ad… then c5b74f1a…). A spawn with no attached
|
|
151
|
-
* TTY has no pid in its args and does dedup. Lifting argv/env/pid out of the
|
|
152
|
-
* generated text into `startArgs` would make every image per-PROGRAM and
|
|
153
|
-
* shareable across spawns and sessions; the sweep bounds the store either way.
|
|
154
|
-
*/
|
|
155
|
-
export async function facetImageDigest(source) {
|
|
156
|
-
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(source));
|
|
157
|
-
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');
|
|
158
|
-
}
|
|
159
|
-
export function facetImagePath(digest) {
|
|
160
|
-
return `/${FACET_IMAGE_DIR}/${digest}.js`;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* The digest an image path claims, for the reader's verify-on-read. Content
|
|
164
|
-
* addressing only holds if the bytes are checked against the name they were
|
|
165
|
-
* fetched under; without that a truncated or overwritten image boots as
|
|
166
|
-
* silently-wrong code, which in a facet surfaces as an unattributable
|
|
167
|
-
* "Cannot find module" a long way from the corruption.
|
|
168
|
-
*/
|
|
169
|
-
export function facetImagePathDigest(path) {
|
|
170
|
-
const match = /(?:^|\/)([0-9a-f]{64})\.js$/.exec(path);
|
|
171
|
-
return match ? match[1] : null;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Complete a resident-process module map: read every member the spec named by
|
|
175
|
-
* path, verifying each generated image against the digest its own path claims.
|
|
176
|
-
* Runs inside the loader's cache-miss callback, so the bytes exist only for
|
|
177
|
-
* the duration of the load.
|
|
178
|
-
*/
|
|
179
|
-
export async function residentLoaderConfig(spec, disk) {
|
|
180
|
-
const resolved = {};
|
|
181
|
-
for (const [moduleName, path] of Object.entries(spec.vfsWasmModules ?? {})) {
|
|
182
|
-
const bytes = await disk.readFile(path);
|
|
183
|
-
resolved[moduleName] = {
|
|
184
|
-
wasm: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength),
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
for (const [moduleName, path] of Object.entries(spec.vfsTextModules ?? {})) {
|
|
188
|
-
resolved[moduleName] = await readFacetImage(disk, path);
|
|
189
|
-
}
|
|
190
|
-
return {
|
|
191
|
-
compatibilityDate: spec.compatibilityDate,
|
|
192
|
-
compatibilityFlags: spec.compatibilityFlags,
|
|
193
|
-
mainModule: spec.mainModule,
|
|
194
|
-
modules: { ...spec.modules, ...resolved },
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Read one content-addressed facet image and verify it against the digest its
|
|
199
|
-
* path claims. Content addressing is only a guarantee if the bytes are checked
|
|
200
|
-
* against the name they arrived under: an image that was truncated, or
|
|
201
|
-
* replaced by something the generator never wrote, would otherwise be loaded
|
|
202
|
-
* as the program and fail somewhere inside it with no way back to the cause.
|
|
203
|
-
*/
|
|
204
|
-
async function readFacetImage(disk, path) {
|
|
205
|
-
const expected = facetImagePathDigest(path);
|
|
206
|
-
if (!expected) {
|
|
207
|
-
throw new Error(`Nimbus: '${path}' is not a content-addressed facet image path`);
|
|
208
|
-
}
|
|
209
|
-
const source = new TextDecoder().decode(await disk.readFile(path));
|
|
210
|
-
const actual = await facetImageDigest(source);
|
|
211
|
-
if (actual !== expected) {
|
|
212
|
-
throw new Error(`Nimbus: facet image '${path}' does not match its digest (read ${actual}); `
|
|
213
|
-
+ 'the image store is corrupt and the process cannot boot from it');
|
|
214
|
-
}
|
|
215
|
-
return source;
|
|
216
|
-
}
|
|
217
|
-
// ── Handle ──────────────────────────────────────────────────────────────────
|
|
218
|
-
/**
|
|
219
|
-
* Resource handle for one resident process — the whole surface the kernel
|
|
220
|
-
* above this module sees: `booted()` for the boot payload, `done` for death,
|
|
221
|
-
* `kill()` for teardown, `routeTarget` for inbound HTTP. Substrate-free: the
|
|
222
|
-
* kernel cannot tell from it where the process is running, and never asks.
|
|
223
|
-
*
|
|
224
|
-
* `done` settles when the process ends: for a `lifetime` runner that is its
|
|
225
|
-
* held-open startProcess settling (resolve on exit, reject on host death);
|
|
226
|
-
* for a `boot` runner it is the kill that releases the host.
|
|
227
|
-
*
|
|
228
|
-
* The handle is disposable so FacetManager's existing per-pid resource
|
|
229
|
-
* tracking tears a process down exactly the way it releases any other
|
|
230
|
-
* per-process resource.
|
|
231
|
-
*/
|
|
232
|
-
export class ResidentProcessHandle {
|
|
233
|
-
done;
|
|
234
|
-
/**
|
|
235
|
-
* Inbound-HTTP target for PortRegistry: the running facet's own stub. A
|
|
236
|
-
* facet is a child actor, so its stub stays usable in request contexts long
|
|
237
|
-
* after the one that created it — which is the whole reason a resident
|
|
238
|
-
* process can serve a port at all.
|
|
239
|
-
*/
|
|
240
|
-
routeTarget;
|
|
241
|
-
#booted;
|
|
242
|
-
#kill;
|
|
243
|
-
#killed = false;
|
|
244
|
-
#describe;
|
|
245
|
-
constructor(init) {
|
|
246
|
-
this.done = init.done;
|
|
247
|
-
this.#booted = init.booted;
|
|
248
|
-
this.routeTarget = init.routeTarget;
|
|
249
|
-
this.#kill = init.kill;
|
|
250
|
-
this.#describe = init.describe;
|
|
251
|
-
// Symbol.dispose may be absent from older lib targets; wire defensively
|
|
252
|
-
// so disposeRpcResource() (which probes for it) finds the disposer.
|
|
253
|
-
const disposeSym = Symbol.dispose;
|
|
254
|
-
if (disposeSym) {
|
|
255
|
-
Object.defineProperty(this, disposeSym, { value: () => this.kill() });
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* The runner's startProcess payload. The runner is started as part of the
|
|
260
|
-
* spawn, so this is a handle on that one boot — awaiting it twice is safe
|
|
261
|
-
* and never re-starts anything. For a `lifetime` runner it settles at exit.
|
|
262
|
-
*/
|
|
263
|
-
booted() {
|
|
264
|
-
return this.#booted();
|
|
265
|
-
}
|
|
266
|
-
get killed() {
|
|
267
|
-
return this.#killed;
|
|
268
|
-
}
|
|
269
|
-
/** Human-readable placement, for the NIMBUS_DEBUG process-log line. */
|
|
270
|
-
describePlacement() {
|
|
271
|
-
return this.#describe();
|
|
272
|
-
}
|
|
273
|
-
/** Idempotent: abort the facet and release its isolate. */
|
|
274
|
-
kill() {
|
|
275
|
-
if (this.#killed)
|
|
276
|
-
return;
|
|
277
|
-
this.#killed = true;
|
|
278
|
-
try {
|
|
279
|
-
this.#kill();
|
|
280
|
-
}
|
|
281
|
-
catch { /* best-effort teardown */ }
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
/** A promise that settles only when the process is killed. */
|
|
285
|
-
function heldUntilKilled() {
|
|
286
|
-
let release = () => { };
|
|
287
|
-
const promise = new Promise((resolve) => { release = resolve; });
|
|
288
|
-
return { promise, release };
|
|
289
|
-
}
|
|
290
|
-
export class ProcessFabric {
|
|
291
|
-
host;
|
|
292
|
-
constructor(host) {
|
|
293
|
-
this.host = host;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Boot a resident process on this deployment's substrate and return its
|
|
297
|
-
* handle. Resolves once the process is up and its runner has been started;
|
|
298
|
-
* rejects on boot failure.
|
|
299
|
-
*
|
|
300
|
-
* There is no decision in here. The substrate was chosen once, for the
|
|
301
|
-
* deployment, and the only thing this method knows about it is the
|
|
302
|
-
* `ProcessHost` interface.
|
|
303
|
-
*/
|
|
304
|
-
async startResidentProcess(spawn) {
|
|
305
|
-
// The facet-local append sequence starts at one when its module evaluates.
|
|
306
|
-
// Bind that sequence to this concrete incarnation, then retire it only
|
|
307
|
-
// after the host is released; a later incarnation must use a fresh one.
|
|
308
|
-
const writerId = crypto.randomUUID();
|
|
309
|
-
spawn.onWriterActivated(writerId);
|
|
310
|
-
let hosted;
|
|
311
|
-
try {
|
|
312
|
-
hosted = await this.host.open({
|
|
313
|
-
pid: spawn.pid,
|
|
314
|
-
workerKey: spawn.workerKey,
|
|
315
|
-
boot: spawn.boot,
|
|
316
|
-
writerId,
|
|
317
|
-
startArgs: spawn.startArgs,
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
catch (error) {
|
|
321
|
-
spawn.onWriterRetired(writerId);
|
|
322
|
-
throw error;
|
|
323
|
-
}
|
|
324
|
-
const held = heldUntilKilled();
|
|
325
|
-
// Runs at most once, however it is reached — the lifecycle ending, a kill,
|
|
326
|
-
// or both. Retiring the writer twice would revoke an identity a later
|
|
327
|
-
// incarnation had already been granted.
|
|
328
|
-
let releasing = null;
|
|
329
|
-
const release = () => {
|
|
330
|
-
if (!releasing) {
|
|
331
|
-
releasing = (async () => {
|
|
332
|
-
try {
|
|
333
|
-
await hosted.release();
|
|
334
|
-
}
|
|
335
|
-
finally {
|
|
336
|
-
spawn.onWriterRetired(writerId);
|
|
337
|
-
}
|
|
338
|
-
})();
|
|
339
|
-
releasing.catch(() => { });
|
|
340
|
-
}
|
|
341
|
-
return releasing;
|
|
342
|
-
};
|
|
343
|
-
// `lifetime`: the runner's startProcess IS the process, so its settlement
|
|
344
|
-
// is the lifecycle — a host that dies under it rejects `started`.
|
|
345
|
-
// `boot`: the runner returns once it is up and the process stays resident
|
|
346
|
-
// on its host, so residency ends at a kill — or at the host dying, which
|
|
347
|
-
// is the same thing happening to the process without anyone asking for it.
|
|
348
|
-
const done = (spawn.startContract === 'lifetime'
|
|
349
|
-
? hosted.started.then(() => undefined)
|
|
350
|
-
: hosted.started.then(() => Promise.race([held.promise, hosted.lost]))).finally(() => release());
|
|
351
|
-
done.catch(() => { });
|
|
352
|
-
return new ResidentProcessHandle({
|
|
353
|
-
done,
|
|
354
|
-
booted: () => hosted.started,
|
|
355
|
-
routeTarget: { handleHttpRequest: (request) => hosted.handleHttpRequest(request) },
|
|
356
|
-
kill: () => { held.release(); void release(); },
|
|
357
|
-
describe: () => hosted.describe(),
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export declare class ParallelError extends Error {
|
|
2
|
-
constructor(message: string);
|
|
3
|
-
}
|
|
4
|
-
export declare class SerializationError extends ParallelError {
|
|
5
|
-
constructor(message: string);
|
|
6
|
-
}
|
|
7
|
-
export declare class ExecutionError extends ParallelError {
|
|
8
|
-
readonly remoteMessage: string;
|
|
9
|
-
readonly remoteStack?: string;
|
|
10
|
-
constructor(message: string, remoteStack?: string);
|
|
11
|
-
}
|
|
12
|
-
export declare class TimeoutError extends ParallelError {
|
|
13
|
-
readonly deadlineMs: number;
|
|
14
|
-
constructor(deadlineMs: number);
|
|
15
|
-
}
|
|
16
|
-
export declare class RetryExhaustedError extends ParallelError {
|
|
17
|
-
readonly lastError: Error;
|
|
18
|
-
readonly attempts: number;
|
|
19
|
-
constructor(attempts: number, lastError: Error);
|
|
20
|
-
}
|
|
21
|
-
export declare class BindingError extends ParallelError {
|
|
22
|
-
constructor(message: string);
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/loaders/vendor/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,kBAAmB,SAAQ,aAAa;gBACvC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,cAAe,SAAQ,aAAa;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;CAMlD;AAED,qBAAa,YAAa,SAAQ,aAAa;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,UAAU,EAAE,MAAM;CAK/B;AAED,qBAAa,mBAAoB,SAAQ,aAAa;IACpD,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK;CAQ/C;AAED,qBAAa,YAAa,SAAQ,aAAa;gBACjC,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export class ParallelError extends Error {
|
|
2
|
-
constructor(message) {
|
|
3
|
-
super(message);
|
|
4
|
-
this.name = 'ParallelError';
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
export class SerializationError extends ParallelError {
|
|
8
|
-
constructor(message) {
|
|
9
|
-
super(message);
|
|
10
|
-
this.name = 'SerializationError';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export class ExecutionError extends ParallelError {
|
|
14
|
-
remoteMessage;
|
|
15
|
-
remoteStack;
|
|
16
|
-
constructor(message, remoteStack) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.name = 'ExecutionError';
|
|
19
|
-
this.remoteMessage = message;
|
|
20
|
-
this.remoteStack = remoteStack;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
export class TimeoutError extends ParallelError {
|
|
24
|
-
deadlineMs;
|
|
25
|
-
constructor(deadlineMs) {
|
|
26
|
-
super(`Task exceeded ${deadlineMs}ms deadline`);
|
|
27
|
-
this.name = 'TimeoutError';
|
|
28
|
-
this.deadlineMs = deadlineMs;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
export class RetryExhaustedError extends ParallelError {
|
|
32
|
-
lastError;
|
|
33
|
-
attempts;
|
|
34
|
-
constructor(attempts, lastError) {
|
|
35
|
-
super(`Task failed after ${attempts} attempt(s): ${lastError.message}`);
|
|
36
|
-
this.name = 'RetryExhaustedError';
|
|
37
|
-
this.attempts = attempts;
|
|
38
|
-
this.lastError = lastError;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
export class BindingError extends ParallelError {
|
|
42
|
-
constructor(message) {
|
|
43
|
-
super(message);
|
|
44
|
-
this.name = 'BindingError';
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../../src/loaders/vendor/serialize.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAyBtD;AAGD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMjD"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { SerializationError } from './errors.js';
|
|
2
|
-
export function serializeFunction(fn) {
|
|
3
|
-
if (typeof fn !== 'function') {
|
|
4
|
-
throw new SerializationError(`Expected a function, got ${typeof fn}`);
|
|
5
|
-
}
|
|
6
|
-
const source = fn.toString();
|
|
7
|
-
if (source.includes('[native code]')) {
|
|
8
|
-
throw new SerializationError(`Cannot serialize native function: ${fn.name || '(anonymous)'}. ` +
|
|
9
|
-
'Only user-defined functions can be dispatched to remote isolates.');
|
|
10
|
-
}
|
|
11
|
-
// `this` has no receiver in the remote isolate — reject it early.
|
|
12
|
-
if (/\bthis\b/.test(source)) {
|
|
13
|
-
throw new SerializationError(`Function "${fn.name || '(anonymous)'}" references \`this\`, which is ` +
|
|
14
|
-
'not available in a remote isolate. Pass values as explicit arguments instead.');
|
|
15
|
-
}
|
|
16
|
-
return source;
|
|
17
|
-
}
|
|
18
|
-
// djb2 hash — fast, deterministic, not cryptographic. Used only for loader cache key differentiation.
|
|
19
|
-
export function hashSource(source) {
|
|
20
|
-
let hash = 5381;
|
|
21
|
-
for (let i = 0; i < source.length; i++) {
|
|
22
|
-
hash = ((hash << 5) + hash + source.charCodeAt(i)) | 0;
|
|
23
|
-
}
|
|
24
|
-
return (hash >>> 0).toString(36);
|
|
25
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export interface JsModule {
|
|
2
|
-
js: string;
|
|
3
|
-
}
|
|
4
|
-
export interface CjsModule {
|
|
5
|
-
cjs: string;
|
|
6
|
-
}
|
|
7
|
-
export interface PyModule {
|
|
8
|
-
py: string;
|
|
9
|
-
}
|
|
10
|
-
export interface TextModule {
|
|
11
|
-
text: string;
|
|
12
|
-
}
|
|
13
|
-
export interface DataModule {
|
|
14
|
-
data: ArrayBuffer;
|
|
15
|
-
}
|
|
16
|
-
export interface JsonModule {
|
|
17
|
-
json: unknown;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* A compiled WebAssembly module, importable from a facet by module name.
|
|
21
|
-
* Nimbus ships these on every path — sql.js for node:sqlite, the interpreter
|
|
22
|
-
* images for python and ruby — so the omission here was the type lagging the
|
|
23
|
-
* API, not a kind the loader lacks.
|
|
24
|
-
*/
|
|
25
|
-
export interface WasmModule {
|
|
26
|
-
wasm: ArrayBuffer;
|
|
27
|
-
}
|
|
28
|
-
/** Plain string = type inferred from file extension (.js or .py). */
|
|
29
|
-
export type ModuleContent = string | JsModule | CjsModule | PyModule | TextModule | DataModule | JsonModule | WasmModule;
|
|
30
|
-
export interface WorkerCode {
|
|
31
|
-
compatibilityDate: string;
|
|
32
|
-
compatibilityFlags?: string[];
|
|
33
|
-
allowExperimental?: boolean;
|
|
34
|
-
mainModule: string;
|
|
35
|
-
modules: Record<string, ModuleContent>;
|
|
36
|
-
env?: Record<string, unknown>;
|
|
37
|
-
/**
|
|
38
|
-
* Network access control:
|
|
39
|
-
* - `undefined`: inherit parent's network access.
|
|
40
|
-
* - `null`: block all outbound (fetch/connect throw).
|
|
41
|
-
* - A service stub: redirect outbound through that binding.
|
|
42
|
-
*/
|
|
43
|
-
globalOutbound?: ServiceStub | null;
|
|
44
|
-
tails?: ServiceStub[];
|
|
45
|
-
}
|
|
46
|
-
export interface ServiceStub {
|
|
47
|
-
fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
|
|
48
|
-
}
|
|
49
|
-
export interface EntrypointOptions {
|
|
50
|
-
props?: Record<string, unknown>;
|
|
51
|
-
}
|
|
52
|
-
export interface WorkerStub {
|
|
53
|
-
getEntrypoint(): EntrypointStub;
|
|
54
|
-
getEntrypoint(name: string, opts?: EntrypointOptions): EntrypointStub;
|
|
55
|
-
}
|
|
56
|
-
export interface EntrypointStub {
|
|
57
|
-
fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
|
|
58
|
-
execute(...args: unknown[]): Promise<unknown>;
|
|
59
|
-
}
|
|
60
|
-
export type GetCodeCallback = () => Promise<WorkerCode>;
|
|
61
|
-
export interface WorkerLoader {
|
|
62
|
-
/**
|
|
63
|
-
* Load or reuse an isolate by ID. Same ID + same code may reuse an isolate;
|
|
64
|
-
* different IDs always get fresh isolates. `getCodeCallback` must return
|
|
65
|
-
* the same content for the same ID.
|
|
66
|
-
*/
|
|
67
|
-
get(id: string, getCodeCallback: GetCodeCallback): WorkerStub;
|
|
68
|
-
}
|
|
69
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/loaders/vendor/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAA;CAAE;AACxC,MAAM,WAAW,SAAS;IAAG,GAAG,EAAE,MAAM,CAAA;CAAE;AAC1C,MAAM,WAAW,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAA;CAAE;AACxC,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAA;CAAE;AAC5C,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,WAAW,CAAA;CAAE;AACjD,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,OAAO,CAAA;CAAE;AAC7C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,WAAW,CAAA;CAAE;AAEjD,qEAAqE;AACrE,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,UAAU,GACV,UAAU,GACV,UAAU,CAAC;AAEf,MAAM,WAAW,UAAU;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE9B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,IAAI,cAAc,CAAC;IAChC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC;CACvE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/C;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,GAAG,UAAU,CAAC;CAC/D"}
|