@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.
Files changed (164) hide show
  1. package/README.md +2 -2
  2. package/dist/_shared/preview-host.d.ts +26 -8
  3. package/dist/_shared/preview-host.d.ts.map +1 -1
  4. package/dist/_shared/preview-host.js +51 -11
  5. package/dist/cirrus-npm-cjs.generated.d.ts +1 -1
  6. package/dist/cirrus-npm-cjs.generated.js +1 -1
  7. package/dist/facets/cirrus-real.d.ts.map +1 -1
  8. package/dist/facets/cirrus-real.js +6 -5
  9. package/dist/facets/compose.d.ts +148 -0
  10. package/dist/facets/compose.d.ts.map +1 -0
  11. package/dist/facets/compose.js +146 -0
  12. package/dist/facets/durable-images.d.ts +18 -5
  13. package/dist/facets/durable-images.d.ts.map +1 -1
  14. package/dist/facets/durable-images.js +36 -7
  15. package/dist/facets/durable-slots.d.ts +1 -1
  16. package/dist/facets/durable-slots.d.ts.map +1 -1
  17. package/dist/facets/durable-slots.js +2 -1
  18. package/dist/facets/esbuild-bundle-pool.d.ts +49 -0
  19. package/dist/facets/esbuild-bundle-pool.d.ts.map +1 -0
  20. package/dist/facets/esbuild-bundle-pool.js +122 -0
  21. package/dist/facets/exec-telemetry.d.ts +8 -0
  22. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  23. package/dist/facets/manager.d.ts +337 -123
  24. package/dist/facets/manager.d.ts.map +1 -1
  25. package/dist/facets/manager.js +1100 -449
  26. package/dist/facets/process.d.ts +20 -16
  27. package/dist/facets/process.d.ts.map +1 -1
  28. package/dist/facets/process.js +59 -28
  29. package/dist/facets/real-vite-fs-shim.js +2 -2
  30. package/dist/facets/resident-identity.d.ts +23 -0
  31. package/dist/facets/resident-identity.d.ts.map +1 -0
  32. package/dist/facets/resident-identity.js +31 -0
  33. package/dist/facets/vite-dev-server.d.ts +25 -47
  34. package/dist/facets/vite-dev-server.d.ts.map +1 -1
  35. package/dist/facets/vite-dev-server.js +185 -219
  36. package/dist/facets/wasm-image-digest.d.ts +25 -0
  37. package/dist/facets/wasm-image-digest.d.ts.map +1 -0
  38. package/dist/facets/wasm-image-digest.js +25 -0
  39. package/dist/facets/wasm-swap-registry.d.ts +51 -92
  40. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  41. package/dist/facets/wasm-swap-registry.js +68 -227
  42. package/dist/git/commands.d.ts +0 -1
  43. package/dist/git/commands.d.ts.map +1 -1
  44. package/dist/git/commands.js +0 -3
  45. package/dist/git/network-facet.d.ts.map +1 -1
  46. package/dist/git/network-facet.js +6 -5
  47. package/dist/loaders/child-process/spawn-pool.d.ts +3 -2
  48. package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
  49. package/dist/loaders/child-process/spawn-pool.js +3 -2
  50. package/dist/loaders/generated-workers.d.ts +2 -2
  51. package/dist/loaders/generated-workers.d.ts.map +1 -1
  52. package/dist/loaders/generated-workers.js +3 -3
  53. package/dist/loaders/npm-resolve-preamble.d.ts +3 -6
  54. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  55. package/dist/loaders/npm-resolve-preamble.js +34 -108
  56. package/dist/node-shims-artifact.generated.js +3 -3
  57. package/dist/npm/install-batch-facet.d.ts +3 -2
  58. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  59. package/dist/npm/install-batch-facet.js +45 -15
  60. package/dist/npm/installer.d.ts +22 -17
  61. package/dist/npm/installer.d.ts.map +1 -1
  62. package/dist/npm/installer.js +209 -193
  63. package/dist/npm/pre-bundle-facet.d.ts +1 -1
  64. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  65. package/dist/npm/pre-bundle-facet.js +1 -1
  66. package/dist/npm/resolve-facet.d.ts +6 -0
  67. package/dist/npm/resolve-facet.d.ts.map +1 -1
  68. package/dist/npm/resolve-one-facet.d.ts +21 -20
  69. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  70. package/dist/npm/resolve-one-facet.js +85 -84
  71. package/dist/npm/semver.d.ts +59 -0
  72. package/dist/npm/semver.d.ts.map +1 -0
  73. package/dist/npm/semver.js +226 -0
  74. package/dist/router/index.d.ts.map +1 -1
  75. package/dist/router/index.js +47 -10
  76. package/dist/router/public-directory.d.ts +5 -1
  77. package/dist/router/public-directory.d.ts.map +1 -1
  78. package/dist/router/public-directory.js +9 -2
  79. package/dist/router/remote-api.d.ts +18 -0
  80. package/dist/router/remote-api.d.ts.map +1 -1
  81. package/dist/router/remote-api.js +64 -5
  82. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  83. package/dist/runtime/cpython-resident.js +3 -1
  84. package/dist/runtime/html-entrypoint.d.ts +7 -0
  85. package/dist/runtime/html-entrypoint.d.ts.map +1 -1
  86. package/dist/runtime/html-entrypoint.js +18 -2
  87. package/dist/runtime/node-runner.d.ts +3 -0
  88. package/dist/runtime/node-runner.d.ts.map +1 -1
  89. package/dist/runtime/node-runner.js +7 -0
  90. package/dist/runtime/node-shims.d.ts.map +1 -1
  91. package/dist/runtime/node-shims.js +305 -29
  92. package/dist/runtime/opencode-facet-runner.js +2 -2
  93. package/dist/runtime/package-manager.d.ts +44 -1
  94. package/dist/runtime/package-manager.d.ts.map +1 -1
  95. package/dist/runtime/package-manager.js +168 -3
  96. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  97. package/dist/runtime/ruby-resident.js +2 -0
  98. package/dist/session/agent.d.ts.map +1 -1
  99. package/dist/session/agent.js +28 -1
  100. package/dist/session/helpers.d.ts +38 -0
  101. package/dist/session/helpers.d.ts.map +1 -1
  102. package/dist/session/helpers.js +76 -0
  103. package/dist/session/init-phases.d.ts +1 -1
  104. package/dist/session/init-phases.d.ts.map +1 -1
  105. package/dist/session/init-phases.js +2 -12
  106. package/dist/session/init.d.ts +15 -1
  107. package/dist/session/init.d.ts.map +1 -1
  108. package/dist/session/init.js +91 -603
  109. package/dist/session/keys.d.ts +4 -0
  110. package/dist/session/keys.d.ts.map +1 -1
  111. package/dist/session/keys.js +4 -0
  112. package/dist/session/nimbus-session.d.ts +90 -23
  113. package/dist/session/nimbus-session.d.ts.map +1 -1
  114. package/dist/session/nimbus-session.js +200 -110
  115. package/dist/session/npm-install-port.d.ts +16 -0
  116. package/dist/session/npm-install-port.d.ts.map +1 -0
  117. package/dist/session/npm-install-port.js +37 -0
  118. package/dist/session/port-capability.d.ts +90 -0
  119. package/dist/session/port-capability.d.ts.map +1 -1
  120. package/dist/session/port-capability.js +219 -9
  121. package/dist/session/programmatic.d.ts +125 -5
  122. package/dist/session/programmatic.d.ts.map +1 -1
  123. package/dist/session/programmatic.js +439 -89
  124. package/dist/session/routes.d.ts +39 -17
  125. package/dist/session/routes.d.ts.map +1 -1
  126. package/dist/session/routes.js +67 -131
  127. package/dist/session/rpc.d.ts +39 -16
  128. package/dist/session/rpc.d.ts.map +1 -1
  129. package/dist/session/rpc.js +137 -130
  130. package/dist/session/serving-port.d.ts +27 -0
  131. package/dist/session/serving-port.d.ts.map +1 -0
  132. package/dist/session/serving-port.js +27 -0
  133. package/dist/session/start-real-vite.d.ts +12 -0
  134. package/dist/session/start-real-vite.d.ts.map +1 -1
  135. package/dist/session/start-real-vite.js +4 -4
  136. package/dist/session/supervisor-op.d.ts +41 -4
  137. package/dist/session/supervisor-op.d.ts.map +1 -1
  138. package/dist/session/supervisor-op.js +101 -73
  139. package/dist/session/supervisor-rpc.d.ts +7 -7
  140. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  141. package/dist/session/supervisor-rpc.js +6 -3
  142. package/dist/session/vite-command.d.ts +18 -0
  143. package/dist/session/vite-command.d.ts.map +1 -0
  144. package/dist/session/vite-command.js +609 -0
  145. package/dist/session/ws.d.ts +61 -0
  146. package/dist/session/ws.d.ts.map +1 -1
  147. package/dist/session/ws.js +122 -12
  148. package/dist/shell/npm-bin-entrypoints.d.ts +0 -35
  149. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  150. package/dist/shell/npm-bin-entrypoints.js +12 -85
  151. package/package.json +24 -4
  152. package/public/_assets/runtime/{node-shims-9e1380d9ee430fcc.js → node-shims-8f8c64edd2a1f0f5.js} +305 -29
  153. package/public/index.html +1 -1
  154. package/scripts/bundle-facet-workers.mjs +1 -1
  155. package/scripts/bundle-npm-cjs.mjs +2 -2
  156. package/dist/facets/on-demand-bundle-gate.d.ts +0 -65
  157. package/dist/facets/on-demand-bundle-gate.d.ts.map +0 -1
  158. package/dist/facets/on-demand-bundle-gate.js +0 -125
  159. package/dist/npm/install-args.d.ts +0 -21
  160. package/dist/npm/install-args.d.ts.map +0 -1
  161. package/dist/npm/install-args.js +0 -70
  162. package/dist/npm/npm-log.d.ts +0 -31
  163. package/dist/npm/npm-log.d.ts.map +0 -1
  164. 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: number;
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 form `<cap>--<port>--<sid>`: the
19
- * bearer is the capability itself, so the request skips session-attach
20
- * auth entirely — the session decides by the port's stored visibility.
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
- export declare function buildPreviewHost(sid: string, port: number, suffix: string): string;
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 of
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, port: number, capability: string, suffix: string): 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;;;;;;;;;;;;GAYG;AAcH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,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,CAgCpB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAEpE"}
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
- export function buildPreviewHost(sid, port, suffix) {
25
- return `${port}--${sid}.${suffix}`;
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 of
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, port, capability, suffix) {
34
- return `${capability}--${port}--${sid}.${suffix}`;
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 (!match)
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 port = Number(match[1]);
78
- const sid = match[2];
79
- if (port < 1 || port > 65535 || !isPreviewHostSafeSid(sid))
105
+ const name = nameMatch[1];
106
+ const sid = nameMatch[3];
107
+ if (!isPreviewHostName(name) || !isPreviewHostSafeSid(sid))
80
108
  return null;
81
- return { port, sid };
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;IAmQhE,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"}
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
- // Two restarts within the same supervisor isolate hit the warm
747
- // cache; a different DO ID gets a fresh slot.
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":"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"}
@@ -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 worker.js source text, `application`
16
- * is a JSON payload of `{ modules, env, vfsWasmModules }`. The digests in the
17
- * journal's `recipe.image` are sha256 hashes of those two payloads, and a
18
- * self-owned spawn mints them at spawn time; an embedder-owned spawn is given
19
- * them by its own bookkeeping instead.
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;;;;;;;;;;;;;;;;;;;GAmBG;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;AASlD;;;;;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;CACzC,GACA,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAalD;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAwBtC"}
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"}