@nimbus-sh/worker 0.5.0 → 0.7.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 +2 -2
- package/dist/_shared/preview-host.d.ts +26 -8
- package/dist/_shared/preview-host.d.ts.map +1 -1
- package/dist/_shared/preview-host.js +51 -11
- package/dist/cirrus-npm-cjs.generated.d.ts +1 -1
- package/dist/cirrus-npm-cjs.generated.js +1 -1
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +6 -5
- package/dist/facets/compose.d.ts +148 -0
- package/dist/facets/compose.d.ts.map +1 -0
- package/dist/facets/compose.js +146 -0
- package/dist/facets/durable-images.d.ts +18 -5
- package/dist/facets/durable-images.d.ts.map +1 -1
- package/dist/facets/durable-images.js +36 -7
- package/dist/facets/durable-slots.d.ts +1 -1
- package/dist/facets/durable-slots.d.ts.map +1 -1
- package/dist/facets/durable-slots.js +2 -1
- package/dist/facets/esbuild-bundle-pool.d.ts +49 -0
- package/dist/facets/esbuild-bundle-pool.d.ts.map +1 -0
- package/dist/facets/esbuild-bundle-pool.js +122 -0
- package/dist/facets/exec-telemetry.d.ts +8 -0
- package/dist/facets/exec-telemetry.d.ts.map +1 -1
- package/dist/facets/manager.d.ts +337 -123
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +1100 -449
- package/dist/facets/process.d.ts +20 -16
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +59 -28
- package/dist/facets/real-vite-fs-shim.js +2 -2
- package/dist/facets/resident-identity.d.ts +23 -0
- package/dist/facets/resident-identity.d.ts.map +1 -0
- package/dist/facets/resident-identity.js +31 -0
- package/dist/facets/vite-dev-server.d.ts +25 -47
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +185 -219
- package/dist/facets/wasm-image-digest.d.ts +25 -0
- package/dist/facets/wasm-image-digest.d.ts.map +1 -0
- package/dist/facets/wasm-image-digest.js +25 -0
- package/dist/facets/wasm-swap-registry.d.ts +51 -92
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
- package/dist/facets/wasm-swap-registry.js +68 -227
- package/dist/git/commands.d.ts +0 -1
- package/dist/git/commands.d.ts.map +1 -1
- package/dist/git/commands.js +0 -3
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +6 -5
- package/dist/loaders/child-process/spawn-pool.d.ts +3 -2
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +3 -2
- package/dist/loaders/generated-workers.d.ts +2 -2
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -3
- package/dist/loaders/npm-resolve-preamble.d.ts +3 -6
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
- package/dist/loaders/npm-resolve-preamble.js +34 -108
- package/dist/node-shims-artifact.generated.js +3 -3
- package/dist/npm/install-batch-facet.d.ts +3 -2
- package/dist/npm/install-batch-facet.d.ts.map +1 -1
- package/dist/npm/install-batch-facet.js +45 -15
- package/dist/npm/installer.d.ts +22 -17
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +209 -193
- package/dist/npm/pre-bundle-facet.d.ts +1 -1
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +1 -1
- package/dist/npm/resolve-facet.d.ts +6 -0
- package/dist/npm/resolve-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.d.ts +21 -20
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +85 -84
- package/dist/npm/semver.d.ts +59 -0
- package/dist/npm/semver.d.ts.map +1 -0
- package/dist/npm/semver.js +226 -0
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +47 -10
- package/dist/router/public-directory.d.ts +5 -1
- package/dist/router/public-directory.d.ts.map +1 -1
- package/dist/router/public-directory.js +9 -2
- package/dist/router/remote-api.d.ts +18 -0
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +64 -5
- package/dist/runtime/cpython-resident.d.ts.map +1 -1
- package/dist/runtime/cpython-resident.js +3 -1
- package/dist/runtime/html-entrypoint.d.ts +7 -0
- package/dist/runtime/html-entrypoint.d.ts.map +1 -1
- package/dist/runtime/html-entrypoint.js +18 -2
- package/dist/runtime/node-runner.d.ts +3 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +7 -0
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +305 -29
- package/dist/runtime/opencode-facet-runner.js +2 -2
- package/dist/runtime/package-manager.d.ts +44 -1
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +168 -3
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +2 -0
- package/dist/session/agent.d.ts.map +1 -1
- package/dist/session/agent.js +28 -1
- package/dist/session/helpers.d.ts +38 -0
- package/dist/session/helpers.d.ts.map +1 -1
- package/dist/session/helpers.js +76 -0
- package/dist/session/init-phases.d.ts +1 -1
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +2 -12
- package/dist/session/init.d.ts +15 -1
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +91 -603
- package/dist/session/keys.d.ts +4 -0
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js +4 -0
- package/dist/session/nimbus-session.d.ts +90 -23
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +200 -110
- package/dist/session/npm-install-port.d.ts +16 -0
- package/dist/session/npm-install-port.d.ts.map +1 -0
- package/dist/session/npm-install-port.js +37 -0
- package/dist/session/port-capability.d.ts +90 -0
- package/dist/session/port-capability.d.ts.map +1 -1
- package/dist/session/port-capability.js +219 -9
- package/dist/session/programmatic.d.ts +125 -5
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +439 -89
- package/dist/session/routes.d.ts +39 -17
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +67 -131
- package/dist/session/rpc.d.ts +39 -16
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +137 -130
- package/dist/session/serving-port.d.ts +27 -0
- package/dist/session/serving-port.d.ts.map +1 -0
- package/dist/session/serving-port.js +27 -0
- package/dist/session/start-real-vite.d.ts +12 -0
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +4 -4
- package/dist/session/supervisor-op.d.ts +41 -4
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +101 -73
- package/dist/session/supervisor-rpc.d.ts +7 -7
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +6 -3
- package/dist/session/vite-command.d.ts +18 -0
- package/dist/session/vite-command.d.ts.map +1 -0
- package/dist/session/vite-command.js +609 -0
- package/dist/session/ws.d.ts +61 -0
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +122 -12
- package/dist/shell/npm-bin-entrypoints.d.ts +0 -35
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +12 -85
- package/package.json +24 -4
- package/public/_assets/runtime/{node-shims-9e1380d9ee430fcc.js → node-shims-8f8c64edd2a1f0f5.js} +305 -29
- package/public/index.html +1 -1
- package/scripts/bundle-facet-workers.mjs +1 -1
- package/scripts/bundle-npm-cjs.mjs +2 -2
- package/dist/facets/on-demand-bundle-gate.d.ts +0 -65
- package/dist/facets/on-demand-bundle-gate.d.ts.map +0 -1
- package/dist/facets/on-demand-bundle-gate.js +0 -125
- package/dist/npm/install-args.d.ts +0 -21
- package/dist/npm/install-args.d.ts.map +0 -1
- package/dist/npm/install-args.js +0 -70
- package/dist/npm/npm-log.d.ts +0 -31
- package/dist/npm/npm-log.d.ts.map +0 -1
- package/dist/npm/npm-log.js +0 -51
package/README.md
CHANGED
|
@@ -216,8 +216,8 @@ config })`. The route requires a valid Nimbus JWT and `sandbox:use` scope.
|
|
|
216
216
|
For an app server, start the process and expose its virtual port:
|
|
217
217
|
|
|
218
218
|
```ts
|
|
219
|
-
await box.files.write('/home/user/app/server.js', serverSource);
|
|
220
|
-
const proc = await box.startProcess('node --watch /home/user/app/server.js');
|
|
219
|
+
await box.files.write('/home/user/example-app/server.js', serverSource);
|
|
220
|
+
const proc = await box.startProcess('node --watch /home/user/example-app/server.js');
|
|
221
221
|
const port = await box.ports.expose(3000);
|
|
222
222
|
// proc.pid is live immediately: box.processes.logs(proc.pid) streams output,
|
|
223
223
|
// box.processes.kill(proc.pid) stops it
|
|
@@ -10,25 +10,36 @@
|
|
|
10
10
|
* `buildPreviewHost` and `parsePreviewHost` are exact inverses: every
|
|
11
11
|
* `(sid, port)` has exactly ONE valid origin. Without that bijection a cookie
|
|
12
12
|
* set on the canonical host is missing from an equivalent-but-different one.
|
|
13
|
+
*
|
|
14
|
+
* The middle label may be a NAME instead of a port — `<name>--<sid>` and
|
|
15
|
+
* `<cap>--<name>--<sid>` — for an application whose reservation carries a
|
|
16
|
+
* name alias. A numeric label is a port; anything else that is a DNS label
|
|
17
|
+
* is a name. The scoped name form is resolved to a port inside the session
|
|
18
|
+
* (its reservation records); the public name form through the directory.
|
|
13
19
|
*/
|
|
14
20
|
export interface PreviewHost {
|
|
15
|
-
port
|
|
21
|
+
/** The port, on the port forms. Absent on a name form — the name resolves to it. */
|
|
22
|
+
port?: number;
|
|
23
|
+
/** The name alias, on the name forms `<name>--<sid>` / `<cap>--<name>--<sid>`. */
|
|
24
|
+
name?: string;
|
|
16
25
|
sid: string;
|
|
17
26
|
/**
|
|
18
|
-
* Present only on the public capability
|
|
19
|
-
* bearer is the capability itself, so the
|
|
20
|
-
* auth entirely — the session decides by the
|
|
27
|
+
* Present only on the public capability forms `<cap>--<port>--<sid>` and
|
|
28
|
+
* `<cap>--<name>--<sid>`: the bearer is the capability itself, so the
|
|
29
|
+
* request skips session-attach auth entirely — the session decides by the
|
|
30
|
+
* port's stored visibility.
|
|
21
31
|
*/
|
|
22
32
|
capability?: string;
|
|
23
33
|
}
|
|
24
|
-
|
|
34
|
+
/** `<port>--<sid>` or `<name>--<sid>`: the middle label is a port number or a name alias. */
|
|
35
|
+
export declare function buildPreviewHost(sid: string, target: number | string, suffix: string): string;
|
|
25
36
|
/**
|
|
26
|
-
* `<capability>--<port>--<sid>.<suffix>` — the unauthenticated sibling
|
|
27
|
-
* `buildPreviewHost`, for applications whose visibility is `public`. The
|
|
37
|
+
* `<capability>--<port|name>--<sid>.<suffix>` — the unauthenticated sibling
|
|
38
|
+
* of `buildPreviewHost`, for applications whose visibility is `public`. The
|
|
28
39
|
* capability is the bearer: 24 lowercase hex, the same shape the port
|
|
29
40
|
* registry mints.
|
|
30
41
|
*/
|
|
31
|
-
export declare function buildPublicPreviewHost(sid: string,
|
|
42
|
+
export declare function buildPublicPreviewHost(sid: string, target: number | string, capability: string, suffix: string): string;
|
|
32
43
|
export declare function isPreviewHostSafeSid(sid: string): boolean;
|
|
33
44
|
/**
|
|
34
45
|
* Read the configured preview-host suffix out of a bindings env.
|
|
@@ -39,6 +50,13 @@ export declare function isPreviewHostSafeSid(sid: string): boolean;
|
|
|
39
50
|
*/
|
|
40
51
|
export declare function readPreviewHostSuffix(env: unknown): string | null;
|
|
41
52
|
export declare function parsePreviewHost(host: string, suffix: string | undefined | null): PreviewHost | null;
|
|
53
|
+
/**
|
|
54
|
+
* A name label: a DNS label that is neither a port (all digits) nor a
|
|
55
|
+
* capability (24 lowercase hex), and contains no `--` host-label separator.
|
|
56
|
+
* The same rule the session applies when it stores a
|
|
57
|
+
* name on a reservation, so every name it accepts is a host it can parse.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isPreviewHostName(label: string): boolean;
|
|
42
60
|
/**
|
|
43
61
|
* True when `url` addresses a port preview. Embedders MUST test this BEFORE
|
|
44
62
|
* their own route table: a preview host serves untrusted user code at the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-host.d.ts","sourceRoot":"","sources":["../../src/_shared/preview-host.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"preview-host.d.ts","sourceRoot":"","sources":["../../src/_shared/preview-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAkBH,MAAM,WAAW,WAAW;IAC1B,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7F;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,MAAM,CAER;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAGjE;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAChC,WAAW,GAAG,IAAI,CA8CpB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKxD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAEpE"}
|
|
@@ -10,28 +10,39 @@
|
|
|
10
10
|
* `buildPreviewHost` and `parsePreviewHost` are exact inverses: every
|
|
11
11
|
* `(sid, port)` has exactly ONE valid origin. Without that bijection a cookie
|
|
12
12
|
* set on the canonical host is missing from an equivalent-but-different one.
|
|
13
|
+
*
|
|
14
|
+
* The middle label may be a NAME instead of a port — `<name>--<sid>` and
|
|
15
|
+
* `<cap>--<name>--<sid>` — for an application whose reservation carries a
|
|
16
|
+
* name alias. A numeric label is a port; anything else that is a DNS label
|
|
17
|
+
* is a name. The scoped name form is resolved to a port inside the session
|
|
18
|
+
* (its reservation records); the public name form through the directory.
|
|
13
19
|
*/
|
|
14
20
|
const PREVIEW_HOST_SAFE_SID_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
15
21
|
/** Canonical port form only: no leading zeros, so `03000--x` is not a host. */
|
|
16
22
|
const PREVIEW_HOST_LABEL_RE = /^(0|[1-9]\d*)--(.+)$/;
|
|
23
|
+
/** The scoped name form: a non-numeric DNS label in the port's place. */
|
|
24
|
+
const PREVIEW_NAME_HOST_LABEL_RE = /^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)--([a-z0-9-]{1,63})$/;
|
|
17
25
|
// The public capability form: the bearer is the capability itself, no
|
|
18
26
|
// attach token and no embedder credential ever crosses this hostname.
|
|
19
27
|
// The label carries everything a request needs — which port, which
|
|
20
28
|
// session, and which token — so it works with no server-side lookup.
|
|
21
29
|
const PREVIEW_CAPABILITY_HOST_LABEL_RE = /^([a-f0-9]{24})--(\d{1,5})--([a-z0-9-]{1,63})$/;
|
|
30
|
+
/** The public name form: the capability names the session in the directory, the name is verified there. */
|
|
31
|
+
const PREVIEW_CAPABILITY_NAME_HOST_LABEL_RE = /^([a-f0-9]{24})--([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)--([a-z0-9-]{1,63})$/;
|
|
22
32
|
/** Binding that carries the deployment's preview-host suffix. */
|
|
23
33
|
const PREVIEW_HOST_SUFFIX_BINDING = 'NIMBUS_PREVIEW_HOST_SUFFIX';
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
/** `<port>--<sid>` or `<name>--<sid>`: the middle label is a port number or a name alias. */
|
|
35
|
+
export function buildPreviewHost(sid, target, suffix) {
|
|
36
|
+
return `${target}--${sid}.${suffix}`;
|
|
26
37
|
}
|
|
27
38
|
/**
|
|
28
|
-
* `<capability>--<port>--<sid>.<suffix>` — the unauthenticated sibling
|
|
29
|
-
* `buildPreviewHost`, for applications whose visibility is `public`. The
|
|
39
|
+
* `<capability>--<port|name>--<sid>.<suffix>` — the unauthenticated sibling
|
|
40
|
+
* of `buildPreviewHost`, for applications whose visibility is `public`. The
|
|
30
41
|
* capability is the bearer: 24 lowercase hex, the same shape the port
|
|
31
42
|
* registry mints.
|
|
32
43
|
*/
|
|
33
|
-
export function buildPublicPreviewHost(sid,
|
|
34
|
-
return `${capability}--${
|
|
44
|
+
export function buildPublicPreviewHost(sid, target, capability, suffix) {
|
|
45
|
+
return `${capability}--${target}--${sid}.${suffix}`;
|
|
35
46
|
}
|
|
36
47
|
export function isPreviewHostSafeSid(sid) {
|
|
37
48
|
return sid.length <= 56 && PREVIEW_HOST_SAFE_SID_RE.test(sid);
|
|
@@ -71,14 +82,43 @@ export function parsePreviewHost(host, suffix) {
|
|
|
71
82
|
return null;
|
|
72
83
|
return { port, sid, capability };
|
|
73
84
|
}
|
|
85
|
+
const capabilityNameMatch = label.match(PREVIEW_CAPABILITY_NAME_HOST_LABEL_RE);
|
|
86
|
+
if (capabilityNameMatch) {
|
|
87
|
+
const capability = capabilityNameMatch[1];
|
|
88
|
+
const name = capabilityNameMatch[2];
|
|
89
|
+
const sid = capabilityNameMatch[4];
|
|
90
|
+
if (!isPreviewHostName(name) || !isPreviewHostSafeSid(sid))
|
|
91
|
+
return null;
|
|
92
|
+
return { name, sid, capability };
|
|
93
|
+
}
|
|
74
94
|
const match = label.match(PREVIEW_HOST_LABEL_RE);
|
|
75
|
-
if (
|
|
95
|
+
if (match) {
|
|
96
|
+
const port = Number(match[1]);
|
|
97
|
+
const sid = match[2];
|
|
98
|
+
if (port < 1 || port > 65535 || !isPreviewHostSafeSid(sid))
|
|
99
|
+
return null;
|
|
100
|
+
return { port, sid };
|
|
101
|
+
}
|
|
102
|
+
const nameMatch = label.match(PREVIEW_NAME_HOST_LABEL_RE);
|
|
103
|
+
if (!nameMatch)
|
|
76
104
|
return null;
|
|
77
|
-
const
|
|
78
|
-
const sid =
|
|
79
|
-
if (
|
|
105
|
+
const name = nameMatch[1];
|
|
106
|
+
const sid = nameMatch[3];
|
|
107
|
+
if (!isPreviewHostName(name) || !isPreviewHostSafeSid(sid))
|
|
80
108
|
return null;
|
|
81
|
-
return {
|
|
109
|
+
return { name, sid };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A name label: a DNS label that is neither a port (all digits) nor a
|
|
113
|
+
* capability (24 lowercase hex), and contains no `--` host-label separator.
|
|
114
|
+
* The same rule the session applies when it stores a
|
|
115
|
+
* name on a reservation, so every name it accepts is a host it can parse.
|
|
116
|
+
*/
|
|
117
|
+
export function isPreviewHostName(label) {
|
|
118
|
+
return /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(label)
|
|
119
|
+
&& !label.includes('--')
|
|
120
|
+
&& !/^\d+$/.test(label)
|
|
121
|
+
&& !/^[a-f0-9]{24}$/.test(label);
|
|
82
122
|
}
|
|
83
123
|
/**
|
|
84
124
|
* True when `url` addresses a port preview. Embedders MUST test this BEFORE
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* (react, react-dom, scheduler). Ships as a static Worker asset alongside
|
|
7
7
|
* cirrus-plugin-react.
|
|
8
8
|
* Consumed by src/cirrus-real.ts — injected as LOADER modules, and
|
|
9
|
-
* the Vite resolver's output (e.g. /home/user/app/node_modules/react/index.js)
|
|
9
|
+
* the Vite resolver's output (e.g. /home/user/example-app/node_modules/react/index.js)
|
|
10
10
|
* is rewritten to point at one of these bundles at request time.
|
|
11
11
|
*/
|
|
12
12
|
import { type AssetsFetcher } from '@nimbus-sh/core/runtime/assets-loader.js';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* (react, react-dom, scheduler). Ships as a static Worker asset alongside
|
|
7
7
|
* cirrus-plugin-react.
|
|
8
8
|
* Consumed by src/cirrus-real.ts — injected as LOADER modules, and
|
|
9
|
-
* the Vite resolver's output (e.g. /home/user/app/node_modules/react/index.js)
|
|
9
|
+
* the Vite resolver's output (e.g. /home/user/example-app/node_modules/react/index.js)
|
|
10
10
|
* is rewritten to point at one of these bundles at request time.
|
|
11
11
|
*/
|
|
12
12
|
import { loadAssetText } from '@nimbus-sh/core/runtime/assets-loader.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cirrus-real.d.ts","sourceRoot":"","sources":["../../src/facets/cirrus-real.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAyBH,OAAO,EACL,SAAS,EAIV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AAmBtF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD,UAAU,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,mBAAmB,CAAC,GAAG,SAAS,CAAC;CACpF,GAAG,OAAO,CAiBV;AA0cD,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,mBAAmB,CAAyB;IAEpD,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,UAAU,CAAa;IAC/B;6BACyB;IACzB,OAAO,CAAC,UAAU,CAAuB;IACzC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,CAAgD;IAC7D;;;;4CAIwC;IACxC,OAAO,CAAC,gBAAgB,CAAa;IACrC;2BACuB;IACvB,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,cAAc,CAON;IAEhB,kEAAkE;IAC3D,GAAG,EAAE,SAAS,CAAmB;IAExC,6CAA6C;IAC7C,OAAO,CAAC,SAAS,CAA6B;gBAElC,IAAI,EAAE;QAChB,GAAG,EAAE,GAAG,CAAC;QACT,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,SAAS,CAAC;QACf,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;QACnC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC9C;IAWD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBnC;IAED;;;iEAG6D;IAC7D,OAAO,CAAC,YAAY,CAAc;IAE5B,KAAK,CAAC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"cirrus-real.d.ts","sourceRoot":"","sources":["../../src/facets/cirrus-real.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAyBH,OAAO,EACL,SAAS,EAIV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AAmBtF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD,UAAU,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,mBAAmB,CAAC,GAAG,SAAS,CAAC;CACpF,GAAG,OAAO,CAiBV;AA0cD,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,mBAAmB,CAAyB;IAEpD,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,UAAU,CAAa;IAC/B;6BACyB;IACzB,OAAO,CAAC,UAAU,CAAuB;IACzC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,CAAgD;IAC7D;;;;4CAIwC;IACxC,OAAO,CAAC,gBAAgB,CAAa;IACrC;2BACuB;IACvB,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,cAAc,CAON;IAEhB,kEAAkE;IAC3D,GAAG,EAAE,SAAS,CAAmB;IAExC,6CAA6C;IAC7C,OAAO,CAAC,SAAS,CAA6B;gBAElC,IAAI,EAAE;QAChB,GAAG,EAAE,GAAG,CAAC;QACT,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,SAAS,CAAC;QACf,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;QACnC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC9C;IAWD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBnC;IAED;;;iEAG6D;IAC7D,OAAO,CAAC,YAAY,CAAc;IAE5B,KAAK,CAAC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoQhE,IAAI,CAAC,GAAG,CAAC,EAAE,kBAAkB,GAAG,IAAI;IAqBpC;;;;OAIG;IACH,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM;IAItC,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIjC,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAItD;;;;OAIG;IACG,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IA8C1E;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAsBzD"}
|
|
@@ -741,11 +741,12 @@ export class CirrusReal {
|
|
|
741
741
|
// [D'.1] Spawn cirrus-real as a DO Facet, not a Worker.
|
|
742
742
|
//
|
|
743
743
|
// Step A: LOADER.get(stableId, configCb) → DynamicWorker stub. The
|
|
744
|
-
// stable ID per-(supervisor-DO + cirrus-real-version) makes the
|
|
744
|
+
// stable ID per-(supervisor-DO + cirrus-real-version + pid) makes the
|
|
745
745
|
// loader cache the bundled vite + plugin code across hibernation.
|
|
746
|
-
//
|
|
747
|
-
//
|
|
748
|
-
//
|
|
746
|
+
// The pid is in the key because env.SUPERVISOR bakes it — a wake
|
|
747
|
+
// strides pids, and a warm worker credentialed to the dead pid
|
|
748
|
+
// would fail every supervisor RPC with "process pid … does not
|
|
749
|
+
// exist". Two restarts on the same pid hit the warm cache.
|
|
749
750
|
// Step B: worker.getDurableObjectClass('CirrusRealVite') → the
|
|
750
751
|
// class constructor from the dynamic worker's exports. Same
|
|
751
752
|
// pattern facet-manager.ts uses for NodeProcess.
|
|
@@ -756,7 +757,7 @@ export class CirrusReal {
|
|
|
756
757
|
// DO so eviction is coherent. The same name returns the same
|
|
757
758
|
// facet on subsequent calls — that's what makes the cookie
|
|
758
759
|
// persist across supervisor reconnects.
|
|
759
|
-
const stableLoaderId = `${ctx.id.toString()}:cirrus-real-vite:${REAL_VITE_VERSION}`;
|
|
760
|
+
const stableLoaderId = `${ctx.id.toString()}:cirrus-real-vite:${REAL_VITE_VERSION}:${pid}`;
|
|
760
761
|
const facetName = 'cirrus-real-vite';
|
|
761
762
|
try {
|
|
762
763
|
const worker = this.env.LOADER.get(stableLoaderId, async () => ({
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* facets/compose.ts — the one composition of a FacetManager.
|
|
3
|
+
*
|
|
4
|
+
* A FacetManager is the session's spawn path for every resident process —
|
|
5
|
+
* `spawnWorker`, `spawnNode`, the launch journal, `_redrive`, cold-start
|
|
6
|
+
* recovery and the paced launch pump — and until this module it was
|
|
7
|
+
* constructed in exactly one place, inside NimbusSession, with hooks that
|
|
8
|
+
* closed over the session. An embedder that composes NimbusWorkspace as a
|
|
9
|
+
* library over its own Durable Object has no session and must not boot one
|
|
10
|
+
* over its filesystem (a second process table hands out pids whose write
|
|
11
|
+
* authority the filesystem already revoked), so everything behind the
|
|
12
|
+
* manager was closed to it.
|
|
13
|
+
*
|
|
14
|
+
* `composeFacetManager` is that construction, taking exactly what an
|
|
15
|
+
* embedder can supply: the Durable Object's ctx and env, its process
|
|
16
|
+
* supervisor, port registry and SQLite filesystem, an optional shared
|
|
17
|
+
* esbuild service, and the hooks a host has to answer. NimbusSession's own
|
|
18
|
+
* `ensureFacetManager` calls this same factory — there is one path — and
|
|
19
|
+
* keeps only what is session-specific (terminal writes, scrollback notices,
|
|
20
|
+
* the alarm that grants launch turns) inside its hook implementations.
|
|
21
|
+
*
|
|
22
|
+
* What the factory owns and the host does not:
|
|
23
|
+
*
|
|
24
|
+
* - `transformLargeEsm`: the isolated esbuild transform for module text
|
|
25
|
+
* past the in-isolate size bound. It needs `env.LOADER`, `env.ASSETS`
|
|
26
|
+
* and `ctx.facets` and nothing of any host, so it is the factory's
|
|
27
|
+
* default rather than every host's copy.
|
|
28
|
+
* - `resolveWorkerLaunchFallback`: the durable image-store resolver a
|
|
29
|
+
* self-owned worker launch (the session's python/ruby residents, or an
|
|
30
|
+
* embedder spawn that let the manager persist its image) re-drives
|
|
31
|
+
* from. It needs only the filesystem. An embedder that keeps its own
|
|
32
|
+
* content store answers `resolveWorkerLaunch` and this is never reached
|
|
33
|
+
* for its recipes (see `WorkerRecipe.resident`).
|
|
34
|
+
*/
|
|
35
|
+
import type { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
|
|
36
|
+
import type { PortRegistry } from '@nimbus-sh/core/runtime/port-registry.js';
|
|
37
|
+
import type { SessionProcessSupervisor } from '@nimbus-sh/core/runtime/session-process-supervisor.js';
|
|
38
|
+
import type { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
|
|
39
|
+
import { FacetManager, type FacetManagerHooks } from './manager.js';
|
|
40
|
+
import { type PortVisibility } from '../session/port-capability.js';
|
|
41
|
+
export type { FacetManagerHooks, LongRunningWorkerSpawnOptions, ResidentSpawnOptions, ResolvedWorkerLaunch, ResidentAppSummary, ResidentIdentity, ResidentRestartPolicy, SpawnedWorker, WorkerFacet, WorkerRecipe, } from './manager.js';
|
|
42
|
+
export { FacetManager, DEFAULT_WORKER_MAIN_MODULE } from './manager.js';
|
|
43
|
+
/**
|
|
44
|
+
* The hooks a host answers. Names and types are the FacetManager's own
|
|
45
|
+
* (`FacetManagerHooks`); the three a host cannot do without are required
|
|
46
|
+
* here because a manager composed without them loses launches silently —
|
|
47
|
+
* a launch turn nobody grants never resumes, an exit nobody hears leaves a
|
|
48
|
+
* process table lying, a notice nobody shows is a reset the user never
|
|
49
|
+
* learns about.
|
|
50
|
+
*/
|
|
51
|
+
export interface FacetManagerHostHooks {
|
|
52
|
+
onExternalExit: NonNullable<FacetManagerHooks['onExternalExit']>;
|
|
53
|
+
notify: NonNullable<FacetManagerHooks['notify']>;
|
|
54
|
+
requestLaunchTurn: NonNullable<FacetManagerHooks['requestLaunchTurn']>;
|
|
55
|
+
onSpawn?: FacetManagerHooks['onSpawn'];
|
|
56
|
+
resolveWorkerLaunch?: FacetManagerHooks['resolveWorkerLaunch'];
|
|
57
|
+
}
|
|
58
|
+
/** Everything a FacetManager is composed over. */
|
|
59
|
+
export interface FacetManagerDeps {
|
|
60
|
+
ctx: DurableObjectState;
|
|
61
|
+
/** The Worker's bindings: `LOADER` is required, `ASSETS` serves the esbuild wasm. */
|
|
62
|
+
env: unknown;
|
|
63
|
+
processes: SessionProcessSupervisor;
|
|
64
|
+
portRegistry: PortRegistry;
|
|
65
|
+
vfs: SqliteVFS;
|
|
66
|
+
/** A host's already-warm esbuild, shared so the wasm is initialized once. */
|
|
67
|
+
esbuild?: EsbuildService;
|
|
68
|
+
hooks: FacetManagerHostHooks;
|
|
69
|
+
}
|
|
70
|
+
/** What `composeFacetManager` hands back. */
|
|
71
|
+
export interface ComposedFacetManager {
|
|
72
|
+
manager: FacetManager;
|
|
73
|
+
/**
|
|
74
|
+
* The paced launch pump — the manager's own `pumpResidentLaunches`, not a
|
|
75
|
+
* second one. The host drives it from the turn `hooks.requestLaunchTurn`
|
|
76
|
+
* arranged (the session: its `'resident-launch'` alarm); the first pump on
|
|
77
|
+
* a fresh incarnation also drains cold-start recovery of the launch
|
|
78
|
+
* journal (fabric's `FencedWork.recoverInterrupted`, registered on the
|
|
79
|
+
* manager's `onColdStart`), so it is the cold-start recovery entry too:
|
|
80
|
+
* the session calls it once from initSession for exactly that reason.
|
|
81
|
+
*/
|
|
82
|
+
pumpLaunches: () => Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* The durable-application verbs a host answers for its embedder — the
|
|
85
|
+
* session answers the same four through its `_rpc*` surface. Each is a
|
|
86
|
+
* short composition of the exported port-capability primitives
|
|
87
|
+
* (`@nimbus-sh/worker/port-capability`) and the manager's own methods, so
|
|
88
|
+
* an embedder that wants a different composition has every piece; what is
|
|
89
|
+
* NOT here is what only the session has (dev-server restore, HMR sockets,
|
|
90
|
+
* the self-refreshing "starting" page).
|
|
91
|
+
*/
|
|
92
|
+
apps: {
|
|
93
|
+
/**
|
|
94
|
+
* Reserve (or re-answer) the port `owner` holds, minting the capability
|
|
95
|
+
* its URL is built on — minted here, stored on the reservation, so a URL
|
|
96
|
+
* handed out before the application has ever booted is the one its
|
|
97
|
+
* eventual binding re-adopts, and the one a reset re-adopts again.
|
|
98
|
+
*/
|
|
99
|
+
ensureDurableApp(input: {
|
|
100
|
+
owner: string;
|
|
101
|
+
preferredPort?: number;
|
|
102
|
+
visibility?: PortVisibility;
|
|
103
|
+
name?: string;
|
|
104
|
+
}): Promise<{
|
|
105
|
+
port: number;
|
|
106
|
+
capability: string | null;
|
|
107
|
+
visibility: PortVisibility;
|
|
108
|
+
}>;
|
|
109
|
+
/**
|
|
110
|
+
* End a durable application's contract: every launch the owner claims is
|
|
111
|
+
* killed, its journal rows purged, its port released and its durable slot
|
|
112
|
+
* freed. `port` is the address that was held; `removed` is false only
|
|
113
|
+
* when no durable application held that owner at all.
|
|
114
|
+
*/
|
|
115
|
+
removeDurableApp(owner: string): Promise<{
|
|
116
|
+
owner: string;
|
|
117
|
+
removed: boolean;
|
|
118
|
+
port: number | null;
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Every registered port with its live capability, persisted at the
|
|
122
|
+
* moment the embedder is told it — a capability nobody has been handed
|
|
123
|
+
* does not need to survive anything.
|
|
124
|
+
*/
|
|
125
|
+
listPorts(): Promise<Array<{
|
|
126
|
+
port: number;
|
|
127
|
+
pid: number;
|
|
128
|
+
registeredAt: number;
|
|
129
|
+
capability: string;
|
|
130
|
+
}>>;
|
|
131
|
+
/**
|
|
132
|
+
* Route a request carrying a port capability to the process on `port`:
|
|
133
|
+
* a durable application a reset left dead is re-driven first
|
|
134
|
+
* (`manager.ensureDurableAppOnPort`), the persisted capability is
|
|
135
|
+
* re-adopted, and a wrong capability is a 404 — never a 403, which would
|
|
136
|
+
* confirm the port is listening. 503 with `Retry-After` while a re-drive
|
|
137
|
+
* is mid-launch; 502 when nothing serves the port. WebSocket upgrades
|
|
138
|
+
* keep fetch semantics through the registered facet.
|
|
139
|
+
*/
|
|
140
|
+
routeCapabilityPort(port: number, capability: string, request: Request, innerPath: string): Promise<Response>;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Compose a FacetManager over a host's own ctx, env, process supervisor, port
|
|
145
|
+
* registry and filesystem. See the module comment for what is defaulted.
|
|
146
|
+
*/
|
|
147
|
+
export declare function composeFacetManager(deps: FacetManagerDeps): ComposedFacetManager;
|
|
148
|
+
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/facets/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAEjF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAqB,MAAM,cAAc,CAAC;AAGvF,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,+BAA+B,CAAC;AASvC,YAAY,EACV,iBAAiB,EACjB,6BAA6B,EAC7B,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,WAAW,EACX,YAAY,GACb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAExE;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,WAAW,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACjE,MAAM,EAAE,WAAW,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,iBAAiB,EAAE,WAAW,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACvE,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACvC,mBAAmB,CAAC,EAAE,iBAAiB,CAAC,qBAAqB,CAAC,CAAC;CAChE;AAED,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,kBAAkB,CAAC;IACxB,qFAAqF;IACrF,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,wBAAwB,CAAC;IACpC,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,SAAS,CAAC;IACf,6EAA6E;IAC7E,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,qBAAqB,CAAC;CAC9B;AAED,6CAA6C;AAC7C,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,YAAY,CAAC;IACtB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC;;;;;;;;OAQG;IACH,IAAI,EAAE;QACJ;;;;;WAKG;QACH,gBAAgB,CAAC,KAAK,EAAE;YACtB,KAAK,EAAE,MAAM,CAAC;YACd,aAAa,CAAC,EAAE,MAAM,CAAC;YACvB,UAAU,CAAC,EAAE,cAAc,CAAC;YAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,GAAG,OAAO,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,UAAU,EAAE,cAAc,CAAA;SAAE,CAAC,CAAC;QACrF;;;;;WAKG;QACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,OAAO,CAAC;YAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAC,CAAC;QACnG;;;;WAIG;QACH,SAAS,IAAI,OAAO,CAAC,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC,CAAC;QACrG;;;;;;;;WAQG;QACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;KAC/G,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,CA8EhF"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* facets/compose.ts — the one composition of a FacetManager.
|
|
3
|
+
*
|
|
4
|
+
* A FacetManager is the session's spawn path for every resident process —
|
|
5
|
+
* `spawnWorker`, `spawnNode`, the launch journal, `_redrive`, cold-start
|
|
6
|
+
* recovery and the paced launch pump — and until this module it was
|
|
7
|
+
* constructed in exactly one place, inside NimbusSession, with hooks that
|
|
8
|
+
* closed over the session. An embedder that composes NimbusWorkspace as a
|
|
9
|
+
* library over its own Durable Object has no session and must not boot one
|
|
10
|
+
* over its filesystem (a second process table hands out pids whose write
|
|
11
|
+
* authority the filesystem already revoked), so everything behind the
|
|
12
|
+
* manager was closed to it.
|
|
13
|
+
*
|
|
14
|
+
* `composeFacetManager` is that construction, taking exactly what an
|
|
15
|
+
* embedder can supply: the Durable Object's ctx and env, its process
|
|
16
|
+
* supervisor, port registry and SQLite filesystem, an optional shared
|
|
17
|
+
* esbuild service, and the hooks a host has to answer. NimbusSession's own
|
|
18
|
+
* `ensureFacetManager` calls this same factory — there is one path — and
|
|
19
|
+
* keeps only what is session-specific (terminal writes, scrollback notices,
|
|
20
|
+
* the alarm that grants launch turns) inside its hook implementations.
|
|
21
|
+
*
|
|
22
|
+
* What the factory owns and the host does not:
|
|
23
|
+
*
|
|
24
|
+
* - `transformLargeEsm`: the isolated esbuild transform for module text
|
|
25
|
+
* past the in-isolate size bound. It needs `env.LOADER`, `env.ASSETS`
|
|
26
|
+
* and `ctx.facets` and nothing of any host, so it is the factory's
|
|
27
|
+
* default rather than every host's copy.
|
|
28
|
+
* - `resolveWorkerLaunchFallback`: the durable image-store resolver a
|
|
29
|
+
* self-owned worker launch (the session's python/ruby residents, or an
|
|
30
|
+
* embedder spawn that let the manager persist its image) re-drives
|
|
31
|
+
* from. It needs only the filesystem. An embedder that keeps its own
|
|
32
|
+
* content store answers `resolveWorkerLaunch` and this is never reached
|
|
33
|
+
* for its recipes (see `WorkerRecipe.resident`).
|
|
34
|
+
*/
|
|
35
|
+
import { createPortCapability } from '@nimbus-sh/core/runtime/port-registry.js';
|
|
36
|
+
import { FacetManager } from './manager.js';
|
|
37
|
+
import { processHostFor } from '../loaders/process-host.js';
|
|
38
|
+
import { resolveDurableWorkerImage } from './durable-images.js';
|
|
39
|
+
import { persistPortCapability, readPortReservation, readPortReservationByOwner, reservePort, routeCapabilityPort, } from '../session/port-capability.js';
|
|
40
|
+
import { bindPublicPortCapability } from '../router/public-directory.js';
|
|
41
|
+
import { ESBUILD_TRANSFORM_WORKER_ID, esbuildTransformWorkerCode, } from './esbuild-transform.js';
|
|
42
|
+
import { fetchEsbuildWasmBytes } from '../runtime/esbuild-wasm-bytes.js';
|
|
43
|
+
export { FacetManager, DEFAULT_WORKER_MAIN_MODULE } from './manager.js';
|
|
44
|
+
/**
|
|
45
|
+
* Compose a FacetManager over a host's own ctx, env, process supervisor, port
|
|
46
|
+
* registry and filesystem. See the module comment for what is defaulted.
|
|
47
|
+
*/
|
|
48
|
+
export function composeFacetManager(deps) {
|
|
49
|
+
const { ctx, env, vfs } = deps;
|
|
50
|
+
const hooks = {
|
|
51
|
+
onExternalExit: deps.hooks.onExternalExit,
|
|
52
|
+
notify: deps.hooks.notify,
|
|
53
|
+
requestLaunchTurn: deps.hooks.requestLaunchTurn,
|
|
54
|
+
...(deps.hooks.onSpawn !== undefined ? { onSpawn: deps.hooks.onSpawn } : {}),
|
|
55
|
+
...(deps.hooks.resolveWorkerLaunch !== undefined
|
|
56
|
+
? { resolveWorkerLaunch: deps.hooks.resolveWorkerLaunch }
|
|
57
|
+
: {}),
|
|
58
|
+
transformLargeEsm: isolatedEsmTransform(ctx, env),
|
|
59
|
+
resolveWorkerLaunchFallback: (recipe) => resolveDurableWorkerImage(vfs, recipe),
|
|
60
|
+
};
|
|
61
|
+
const manager = new FacetManager(ctx, env, deps.processes, deps.portRegistry, processHostFor, hooks);
|
|
62
|
+
manager.setVfs(vfs);
|
|
63
|
+
if (deps.esbuild)
|
|
64
|
+
manager.setEsbuildService(deps.esbuild);
|
|
65
|
+
const { portRegistry } = deps;
|
|
66
|
+
const capabilityHost = { ctx, portRegistry };
|
|
67
|
+
return {
|
|
68
|
+
manager,
|
|
69
|
+
pumpLaunches: () => manager.pumpResidentLaunches(),
|
|
70
|
+
apps: {
|
|
71
|
+
async ensureDurableApp(input) {
|
|
72
|
+
if (typeof input.owner !== 'string' || input.owner.length === 0) {
|
|
73
|
+
throw new Error('ensureDurableApp: owner must be a non-empty string');
|
|
74
|
+
}
|
|
75
|
+
const occupied = new Set(portRegistry.getAll().map((entry) => entry.port));
|
|
76
|
+
const port = await reservePort(ctx, {
|
|
77
|
+
owner: input.owner,
|
|
78
|
+
preferredPort: input.preferredPort,
|
|
79
|
+
occupiedPorts: occupied,
|
|
80
|
+
capability: createPortCapability(),
|
|
81
|
+
...(input.visibility !== undefined ? { visibility: input.visibility } : {}),
|
|
82
|
+
...(input.name !== undefined ? { name: input.name } : {}),
|
|
83
|
+
});
|
|
84
|
+
const record = await readPortReservation(ctx, port);
|
|
85
|
+
if (record?.visibility === 'public' && record.capability !== null) {
|
|
86
|
+
// A no-op for a host whose Durable Object is not a session (the
|
|
87
|
+
// public directory keys on the session name); loud when it is a
|
|
88
|
+
// session without the binding.
|
|
89
|
+
await bindPublicPortCapability({ env, ctx }, record.capability, port, input.name);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
port,
|
|
93
|
+
capability: record?.capability ?? null,
|
|
94
|
+
visibility: record?.visibility ?? 'scoped',
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
async removeDurableApp(owner) {
|
|
98
|
+
if (typeof owner !== 'string' || owner.length === 0) {
|
|
99
|
+
throw new Error('removeDurableApp: owner must be a non-empty string');
|
|
100
|
+
}
|
|
101
|
+
const held = await readPortReservationByOwner(ctx, owner);
|
|
102
|
+
const removed = await manager.removeDurableApp(owner);
|
|
103
|
+
return { owner, removed, port: held?.port ?? null };
|
|
104
|
+
},
|
|
105
|
+
async listPorts() {
|
|
106
|
+
const entries = portRegistry.getAll();
|
|
107
|
+
await Promise.all(entries.map((entry) => persistPortCapability(capabilityHost, entry.port, entry.capability)));
|
|
108
|
+
return entries.map((entry) => ({
|
|
109
|
+
port: Number(entry.port),
|
|
110
|
+
pid: Number(entry.pid),
|
|
111
|
+
registeredAt: Number(entry.registeredAt),
|
|
112
|
+
capability: String(entry.capability),
|
|
113
|
+
}));
|
|
114
|
+
},
|
|
115
|
+
routeCapabilityPort(port, capability, request, innerPath) {
|
|
116
|
+
// The session's routeToSessionPort is the one implementation —
|
|
117
|
+
// the capability gate AND the public-bearer visibility gate live
|
|
118
|
+
// there; a copy here dropped the latter. Session-only concerns
|
|
119
|
+
// (dev-server restore, the vite-shim branch) no-op on this host.
|
|
120
|
+
return routeCapabilityPort({ ctx, portRegistry, ensureDurableAppOnPort: (p) => manager.ensureDurableAppOnPort(p) }, port, capability, request, innerPath);
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The isolated esbuild transform: module text past the in-isolate bound is
|
|
127
|
+
* transformed in a loader-backed facet that owns the esbuild wasm heap, so the
|
|
128
|
+
* host isolate never pays for it. Session-independent by construction —
|
|
129
|
+
* `env.LOADER`, `env.ASSETS` and `ctx.facets` are all it reads.
|
|
130
|
+
*/
|
|
131
|
+
function isolatedEsmTransform(ctx, env) {
|
|
132
|
+
return async (code, options) => {
|
|
133
|
+
const loader = Reflect.get(Object(env), 'LOADER');
|
|
134
|
+
if (!loader || typeof loader.get !== 'function') {
|
|
135
|
+
throw new Error('Nimbus: env.LOADER unavailable for isolated esbuild transform');
|
|
136
|
+
}
|
|
137
|
+
const assets = Reflect.get(Object(env), 'ASSETS');
|
|
138
|
+
if (!assets || typeof assets.fetch !== 'function') {
|
|
139
|
+
throw new Error('Nimbus: env.ASSETS unavailable for isolated esbuild transform');
|
|
140
|
+
}
|
|
141
|
+
const worker = await loader.get(ESBUILD_TRANSFORM_WORKER_ID, async () => esbuildTransformWorkerCode(await fetchEsbuildWasmBytes({ ASSETS: assets })));
|
|
142
|
+
const transformClass = worker.getDurableObjectClass('EsbuildTransformFacet');
|
|
143
|
+
const facet = ctx.facets.get(`esbuild-transform-${ESBUILD_TRANSFORM_WORKER_ID}`, async () => ({ class: transformClass }));
|
|
144
|
+
return facet.transform(code, options);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -12,17 +12,26 @@
|
|
|
12
12
|
* durable spawn exists to survive. Durable images are kept for the
|
|
13
13
|
* application's life and released only by explicit removal.
|
|
14
14
|
*
|
|
15
|
-
* Two blobs per launch: `runner` is the
|
|
16
|
-
* is a JSON payload of `{ modules, env, vfsWasmModules }
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* them
|
|
15
|
+
* Two blobs per launch: `runner` is the main module's source text,
|
|
16
|
+
* `application` is a JSON payload of `{ modules, env, vfsWasmModules }` plus,
|
|
17
|
+
* when the launch set them, `vfsTextModules`, `mainModule` and `startArgs`.
|
|
18
|
+
* The digests in the journal's `recipe.image` are sha256 hashes of those two
|
|
19
|
+
* payloads, and a self-owned spawn mints them at spawn time; an
|
|
20
|
+
* embedder-owned spawn is given them by its own bookkeeping instead.
|
|
20
21
|
*/
|
|
21
22
|
import type { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
|
|
22
23
|
import type { ResidentCodeSpec } from '@nimbus-sh/fabric/process-fabric.js';
|
|
23
24
|
import type { ResolvedWorkerLaunch, WorkerRecipe } from './manager.js';
|
|
24
25
|
/** The directory every durable application's image blobs live under. */
|
|
25
26
|
export declare const DURABLE_IMAGE_DIR = ".nimbus/images";
|
|
27
|
+
/** Remove only the owner's blobs that no other retained recipe references. */
|
|
28
|
+
export declare function purgeDurableWorkerImages(vfs: SqliteVFS, owned: Iterable<{
|
|
29
|
+
runner: string;
|
|
30
|
+
application: string;
|
|
31
|
+
}>, retained: Iterable<{
|
|
32
|
+
runner: string;
|
|
33
|
+
application: string;
|
|
34
|
+
}>): number;
|
|
26
35
|
/**
|
|
27
36
|
* Persist a launch's image blobs, minting their digests, for a self-owned
|
|
28
37
|
* durable spawn. Reads and writes as CRED_KERNEL: the directory is session
|
|
@@ -35,6 +44,10 @@ export declare function persistDurableWorkerImage(vfs: SqliteVFS, workerCode: st
|
|
|
35
44
|
}>;
|
|
36
45
|
env?: ResidentCodeSpec['env'];
|
|
37
46
|
vfsWasmModules?: Record<string, string>;
|
|
47
|
+
vfsTextModules?: Record<string, string>;
|
|
48
|
+
/** Recorded only when the launch named one; absent means `worker.js`. */
|
|
49
|
+
mainModule?: string;
|
|
50
|
+
startArgs?: unknown;
|
|
38
51
|
}): Promise<{
|
|
39
52
|
runner: string;
|
|
40
53
|
application: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-images.d.ts","sourceRoot":"","sources":["../../src/facets/durable-images.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"durable-images.d.ts","sourceRoot":"","sources":["../../src/facets/durable-images.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEvE,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AAIlD,8EAA8E;AAC9E,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,EACxD,QAAQ,EAAE,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1D,MAAM,CAaR;AAOD;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IACP,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;IACxD,GAAG,CAAC,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GACA,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAkBlD;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAiCtC"}
|