@nimbus-sh/worker 0.3.0 → 0.5.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 (136) hide show
  1. package/README.md +46 -43
  2. package/dist/_shared/preview-host.d.ts +14 -1
  3. package/dist/_shared/preview-host.d.ts.map +1 -1
  4. package/dist/_shared/preview-host.js +28 -3
  5. package/dist/_shared/session-router.d.ts +43 -0
  6. package/dist/_shared/session-router.d.ts.map +1 -1
  7. package/dist/_shared/session-router.js +59 -12
  8. package/dist/esbuild-wasm-bundle.generated.d.ts +1 -1
  9. package/dist/esbuild-wasm-bundle.generated.js +1 -1
  10. package/dist/facets/cirrus-real.js +1 -1
  11. package/dist/facets/durable-images.d.ts +50 -0
  12. package/dist/facets/durable-images.d.ts.map +1 -0
  13. package/dist/facets/durable-images.js +75 -0
  14. package/dist/facets/durable-slots.d.ts +45 -0
  15. package/dist/facets/durable-slots.d.ts.map +1 -0
  16. package/dist/facets/durable-slots.js +94 -0
  17. package/dist/facets/esbuild-transform.d.ts +10 -0
  18. package/dist/facets/esbuild-transform.d.ts.map +1 -0
  19. package/dist/facets/esbuild-transform.js +36 -0
  20. package/dist/facets/manager.d.ts +167 -12
  21. package/dist/facets/manager.d.ts.map +1 -1
  22. package/dist/facets/manager.js +575 -113
  23. package/dist/facets/opencode-staging.d.ts.map +1 -1
  24. package/dist/facets/opencode-staging.js +3 -4
  25. package/dist/facets/real-vite-hmr.js +1 -1
  26. package/dist/facets/vite-dev-server.d.ts +4 -4
  27. package/dist/facets/vite-dev-server.js +5 -5
  28. package/dist/git/commands.d.ts +22 -0
  29. package/dist/git/commands.d.ts.map +1 -1
  30. package/dist/git/commands.js +431 -424
  31. package/dist/git/network-facet.d.ts.map +1 -1
  32. package/dist/git/network-facet.js +6 -5
  33. package/dist/git-bundle.generated.d.ts +2 -2
  34. package/dist/git-bundle.generated.d.ts.map +1 -1
  35. package/dist/git-bundle.generated.js +3 -3
  36. package/dist/index.d.ts +2 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +11 -2
  39. package/dist/loaders/child-process/spawn-facet.js +1 -1
  40. package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
  41. package/dist/loaders/child-process/spawn-pool.js +2 -2
  42. package/dist/loaders/generated-workers.d.ts +2 -2
  43. package/dist/loaders/generated-workers.d.ts.map +1 -1
  44. package/dist/loaders/generated-workers.js +3 -3
  45. package/dist/loaders/npm-resolve-preamble.d.ts +3 -3
  46. package/dist/loaders/npm-resolve-preamble.js +3 -3
  47. package/dist/loaders/pre-bundle-preamble.d.ts +5 -5
  48. package/dist/loaders/pre-bundle-preamble.js +6 -6
  49. package/dist/node-shims-artifact.generated.js +3 -3
  50. package/dist/npm/install-batch-facet.d.ts +1 -1
  51. package/dist/npm/install-batch-facet.js +1 -1
  52. package/dist/npm/installer.d.ts +6 -6
  53. package/dist/npm/installer.d.ts.map +1 -1
  54. package/dist/npm/installer.js +28 -27
  55. package/dist/npm/pre-bundle-facet.d.ts +2 -2
  56. package/dist/npm/pre-bundle-facet.js +5 -5
  57. package/dist/npm/r2-cache.d.ts +1 -1
  58. package/dist/npm/resolve-facet.d.ts +1 -1
  59. package/dist/npm/resolve-facet.js +1 -1
  60. package/dist/npm/resolve-one-facet.d.ts +5 -5
  61. package/dist/npm/resolve-one-facet.js +5 -5
  62. package/dist/router/index.d.ts.map +1 -1
  63. package/dist/router/index.js +61 -4
  64. package/dist/router/public-directory-do.d.ts +17 -0
  65. package/dist/router/public-directory-do.d.ts.map +1 -0
  66. package/dist/router/public-directory-do.js +16 -0
  67. package/dist/router/public-directory.d.ts +75 -0
  68. package/dist/router/public-directory.d.ts.map +1 -0
  69. package/dist/router/public-directory.js +115 -0
  70. package/dist/router/remote-api.d.ts +9 -1
  71. package/dist/router/remote-api.d.ts.map +1 -1
  72. package/dist/router/remote-api.js +20 -3
  73. package/dist/runtime/bun-repl.d.ts +1 -1
  74. package/dist/runtime/bun-repl.js +3 -3
  75. package/dist/runtime/esbuild-wasm-bytes.js +1 -1
  76. package/dist/runtime/facet-loader-host.d.ts +2 -2
  77. package/dist/runtime/facet-loader-host.js +3 -3
  78. package/dist/runtime/node-repl.js +2 -2
  79. package/dist/runtime/node-shims-artifact.js +1 -1
  80. package/dist/runtime/node-shims.d.ts +4 -1
  81. package/dist/runtime/node-shims.d.ts.map +1 -1
  82. package/dist/runtime/node-shims.js +326 -7
  83. package/dist/runtime/opencode-artifact.js +1 -1
  84. package/dist/runtime/opentui-wasm-bytes.js +1 -1
  85. package/dist/runtime/python-repl.js +3 -3
  86. package/dist/runtime/ruby-repl.js +2 -2
  87. package/dist/runtime/sqlite-wasm-bytes.js +1 -1
  88. package/dist/session/agent.d.ts +11 -0
  89. package/dist/session/agent.d.ts.map +1 -1
  90. package/dist/session/diag.js +1 -1
  91. package/dist/session/helpers.js +1 -1
  92. package/dist/session/hibernation.d.ts +8 -8
  93. package/dist/session/hibernation.d.ts.map +1 -1
  94. package/dist/session/hibernation.js +15 -15
  95. package/dist/session/init-phases.d.ts +19 -26
  96. package/dist/session/init-phases.d.ts.map +1 -1
  97. package/dist/session/init-phases.js +20 -43
  98. package/dist/session/init.d.ts.map +1 -1
  99. package/dist/session/init.js +24 -19
  100. package/dist/session/keys.d.ts +8 -0
  101. package/dist/session/keys.d.ts.map +1 -1
  102. package/dist/session/keys.js +12 -3
  103. package/dist/session/nimbus-session.d.ts +33 -19
  104. package/dist/session/nimbus-session.d.ts.map +1 -1
  105. package/dist/session/nimbus-session.js +78 -37
  106. package/dist/session/port-capability.d.ts +95 -8
  107. package/dist/session/port-capability.d.ts.map +1 -1
  108. package/dist/session/port-capability.js +165 -8
  109. package/dist/session/programmatic.d.ts +50 -4
  110. package/dist/session/programmatic.d.ts.map +1 -1
  111. package/dist/session/programmatic.js +112 -15
  112. package/dist/session/routes.d.ts +17 -0
  113. package/dist/session/routes.d.ts.map +1 -1
  114. package/dist/session/routes.js +111 -43
  115. package/dist/session/rpc.d.ts +7 -7
  116. package/dist/session/rpc.d.ts.map +1 -1
  117. package/dist/session/rpc.js +34 -23
  118. package/dist/session/shell-socket.d.ts +76 -0
  119. package/dist/session/shell-socket.d.ts.map +1 -0
  120. package/dist/session/shell-socket.js +172 -0
  121. package/dist/session/start-real-vite.js +2 -2
  122. package/dist/session/supervisor-op.d.ts +5 -0
  123. package/dist/session/supervisor-op.d.ts.map +1 -0
  124. package/dist/session/supervisor-op.js +74 -0
  125. package/dist/session/supervisor-rpc.d.ts +6 -10
  126. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  127. package/dist/session/supervisor-rpc.js +98 -79
  128. package/dist/session/ws.d.ts +1 -3
  129. package/dist/session/ws.d.ts.map +1 -1
  130. package/dist/session/ws.js +23 -7
  131. package/package.json +4 -3
  132. package/public/_assets/runtime/{node-shims-fea906843f6d594e.js → node-shims-9e1380d9ee430fcc.js} +320 -5
  133. package/public/s/index.html +101 -5
  134. package/scripts/bundle-esbuild-wasm.mjs +2 -2
  135. package/scripts/bundle-facet-workers.mjs +5 -7
  136. package/scripts/bundle-sqlite-wasm.mjs +1 -1
package/README.md CHANGED
@@ -1,14 +1,18 @@
1
1
  # @nimbus-sh/worker
2
2
 
3
- The Cloudflare half of Nimbus: the `NimbusSession` Durable Object, router,
4
- static assets, facet machinery, and auth internals. The filesystem, shell,
5
- and WASI runtime layer live in
3
+ Run a Linux-like sandbox on Cloudflare Workers. A session gets a filesystem,
4
+ a shell, real processes, and ports you can reach from a browser, all inside
5
+ one Durable Object.
6
+
7
+ This package is the Cloudflare half: the `NimbusSession` Durable Object, the
8
+ router, the static assets, the facet machinery, and the auth internals. The
9
+ filesystem, shell, and WASI runtime layer live in
6
10
  [`@nimbus-sh/core`](https://www.npmjs.com/package/@nimbus-sh/core), which
7
- this package composes on — core also runs standalone in bun or node.
11
+ this package composes on. Core also runs standalone in bun or node.
8
12
 
9
- Application code should import the deploy-time API through
10
- `@nimbus-sh/sdk/worker`. This package is still installed because it carries
11
- the runtime implementation and static assets used by the SDK entrypoint.
13
+ Import the deploy-time API from `@nimbus-sh/sdk/worker`. Install this package
14
+ as well, because it carries the runtime implementation and the static assets
15
+ that entrypoint serves.
12
16
 
13
17
  ## Install
14
18
 
@@ -123,9 +127,9 @@ Cloudflare Dashboard once for the account, then rerun the setup command.
123
127
  single-tenant demos can set `NIMBUS_LEGACY_PUBLIC=1` and rely on URL
124
128
  possession instead.
125
129
 
126
- ## Composable API
130
+ ## createNimbusHandler
127
131
 
128
- `createNimbusHandler(options)` accepts:
132
+ The handler owns the router, auth, and the session protocol. Its options:
129
133
 
130
134
  ```ts
131
135
  {
@@ -144,8 +148,8 @@ possession instead.
144
148
 
145
149
  ### Custom routes
146
150
 
147
- Routes that return non-`null` short-circuit Nimbus's router. Use them
148
- for a token-mint endpoint, `/healthz`, SDK smoke tests, or backend
151
+ A route that returns anything but `null` short-circuits Nimbus's router. Use
152
+ one for a token-mint endpoint, `/healthz`, SDK smoke tests, or backend
149
153
  sandbox jobs.
150
154
 
151
155
  ```ts
@@ -209,8 +213,7 @@ Applications normally call it through `Nimbus.connect({ endpoint, token,
209
213
  config })`. The route requires a valid Nimbus JWT and `sandbox:use` scope.
210
214
  `box.destroy()` additionally requires `session:destroy` or `session:admin`.
211
215
 
212
- For long-running app servers, start an explicit long-running process and
213
- expose the virtual port:
216
+ For an app server, start the process and expose its virtual port:
214
217
 
215
218
  ```ts
216
219
  await box.files.write('/home/user/app/server.js', serverSource);
@@ -220,19 +223,39 @@ const port = await box.ports.expose(3000);
220
223
  // box.processes.kill(proc.pid) stops it
221
224
  ```
222
225
 
226
+ ### Hooks
227
+
228
+ ```ts
229
+ export default createNimbusHandler({
230
+ hooks: {
231
+ onSessionStart: ({ sessionId, tenantSegment, request }) => {
232
+ console.log(`[${tenantSegment}] session ${sessionId} attached from ${request.headers.get('cf-connecting-ip')}`);
233
+ },
234
+ },
235
+ });
236
+ ```
237
+
238
+ ### Auth modes
239
+
240
+ | Mode | Meaning |
241
+ |---|---|
242
+ | `'auto'` (default) | Verify token when `JWT_SECRET` is set AND `NIMBUS_LEGACY_PUBLIC` is unset. Otherwise legacy-public. |
243
+ | `'enforce'` | Always verify token; fail closed if `JWT_SECRET` is missing. |
244
+ | `'legacy'` | Never verify; all requests route to the single `legacy:public:_` tenant. Use only for single-tenant demos. |
245
+
223
246
  ## Session Agent
224
247
 
225
- The bundled session shell includes an Agent surface inside the editor
226
- workspace. The route lives inside the session Durable Object under
227
- `/api/agent/*`; the stable Cloudflare OAuth callback is
248
+ The bundled session shell carries an agent surface inside the editor
249
+ workspace. Its route lives in the session Durable Object under
250
+ `/api/agent/*`. The stable Cloudflare OAuth callback is
228
251
  `/api/nimbus/oauth/callback`.
229
252
 
230
- The agent can use the same session tools as the SDK: shell exec, files,
231
- runtime installs, long-running processes, logs, and preview ports. Model calls
232
- use the AI SDK with Cloudflare Workers AI's OpenAI-compatible endpoint and an
253
+ The agent uses the same session tools as the SDK: shell exec, files, runtime
254
+ installs, long-running processes, logs, and preview ports. Model calls use
255
+ the AI SDK with Cloudflare Workers AI's OpenAI-compatible endpoint and an
233
256
  optional AI Gateway name.
234
257
 
235
- For user-owned quota, create a Cloudflare OAuth client with response type
258
+ For user-owned quota, create a Cloudflare OAuth client. Use response type
236
259
  `Code`, grant type `Authorization Code`, token authentication method `None`,
237
260
  and redirect URL `https://<your-nimbus-host>/api/nimbus/oauth/callback`.
238
261
  Nimbus uses PKCE and stores user OAuth tokens only in encrypted `HttpOnly`,
@@ -257,26 +280,6 @@ npx wrangler secret put NIMBUS_AGENT_COOKIE_SECRET
257
280
  npx wrangler secret put NIMBUS_CLOUDFLARE_API_TOKEN
258
281
  ```
259
282
 
260
- ### Hooks
261
-
262
- ```ts
263
- export default createNimbusHandler({
264
- hooks: {
265
- onSessionStart: ({ sessionId, tenantSegment, request }) => {
266
- console.log(`[${tenantSegment}] session ${sessionId} attached from ${request.headers.get('cf-connecting-ip')}`);
267
- },
268
- },
269
- });
270
- ```
271
-
272
- ### Auth modes
273
-
274
- | Mode | Meaning |
275
- |---|---|
276
- | `'auto'` (default) | Verify token when `JWT_SECRET` is set AND `NIMBUS_LEGACY_PUBLIC` is unset. Otherwise legacy-public. |
277
- | `'enforce'` | Always verify token; fail closed if `JWT_SECRET` is missing. |
278
- | `'legacy'` | Never verify; all requests route to the single `legacy:public:_` tenant. Use only for single-tenant demos. |
279
-
280
283
  ## Subpath exports
281
284
 
282
285
  | Subpath | What |
@@ -288,7 +291,7 @@ export default createNimbusHandler({
288
291
 
289
292
  ## Required bindings
290
293
 
291
- Every binding is load-bearing — see `apps/hosted-demo/wrangler.jsonc` or
294
+ None of these is optional. See `apps/hosted-demo/wrangler.jsonc` or
292
295
  `@nimbus-sh/config` for the canonical set:
293
296
 
294
297
  - `NIMBUS_SESSION` (Durable Object) — per-session SQLite state
@@ -299,7 +302,7 @@ Every binding is load-bearing — see `apps/hosted-demo/wrangler.jsonc` or
299
302
 
300
303
  ## Status
301
304
 
302
- v0.1 — first public release. SemVer not yet stable; expect breaking
303
- changes through v0.x. Issues + PRs welcome.
305
+ v0.1 is the first public release. SemVer is not stable yet, so expect
306
+ breaking changes through v0.x. Issues + PRs welcome.
304
307
 
305
308
  MIT. © Ashish Kumar Singh + contributors.
@@ -14,9 +14,22 @@
14
14
  export interface PreviewHost {
15
15
  port: number;
16
16
  sid: string;
17
+ /**
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.
21
+ */
22
+ capability?: string;
17
23
  }
18
- export declare function isPreviewHostSafeSid(sid: string): boolean;
19
24
  export declare function buildPreviewHost(sid: string, port: number, suffix: string): string;
25
+ /**
26
+ * `<capability>--<port>--<sid>.<suffix>` — the unauthenticated sibling of
27
+ * `buildPreviewHost`, for applications whose visibility is `public`. The
28
+ * capability is the bearer: 24 lowercase hex, the same shape the port
29
+ * registry mints.
30
+ */
31
+ export declare function buildPublicPreviewHost(sid: string, port: number, capability: string, suffix: string): string;
32
+ export declare function isPreviewHostSafeSid(sid: string): boolean;
20
33
  /**
21
34
  * Read the configured preview-host suffix out of a bindings env.
22
35
  *
@@ -1 +1 @@
1
- {"version":3,"file":"preview-host.d.ts","sourceRoot":"","sources":["../../src/_shared/preview-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElF;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,CAqBpB;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;;;;;;;;;;;;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"}
@@ -14,14 +14,28 @@
14
14
  const PREVIEW_HOST_SAFE_SID_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
15
15
  /** Canonical port form only: no leading zeros, so `03000--x` is not a host. */
16
16
  const PREVIEW_HOST_LABEL_RE = /^(0|[1-9]\d*)--(.+)$/;
17
+ // The public capability form: the bearer is the capability itself, no
18
+ // attach token and no embedder credential ever crosses this hostname.
19
+ // The label carries everything a request needs — which port, which
20
+ // session, and which token — so it works with no server-side lookup.
21
+ const PREVIEW_CAPABILITY_HOST_LABEL_RE = /^([a-f0-9]{24})--(\d{1,5})--([a-z0-9-]{1,63})$/;
17
22
  /** Binding that carries the deployment's preview-host suffix. */
18
23
  const PREVIEW_HOST_SUFFIX_BINDING = 'NIMBUS_PREVIEW_HOST_SUFFIX';
19
- export function isPreviewHostSafeSid(sid) {
20
- return sid.length <= 56 && PREVIEW_HOST_SAFE_SID_RE.test(sid);
21
- }
22
24
  export function buildPreviewHost(sid, port, suffix) {
23
25
  return `${port}--${sid}.${suffix}`;
24
26
  }
27
+ /**
28
+ * `<capability>--<port>--<sid>.<suffix>` — the unauthenticated sibling of
29
+ * `buildPreviewHost`, for applications whose visibility is `public`. The
30
+ * capability is the bearer: 24 lowercase hex, the same shape the port
31
+ * registry mints.
32
+ */
33
+ export function buildPublicPreviewHost(sid, port, capability, suffix) {
34
+ return `${capability}--${port}--${sid}.${suffix}`;
35
+ }
36
+ export function isPreviewHostSafeSid(sid) {
37
+ return sid.length <= 56 && PREVIEW_HOST_SAFE_SID_RE.test(sid);
38
+ }
25
39
  /**
26
40
  * Read the configured preview-host suffix out of a bindings env.
27
41
  *
@@ -46,6 +60,17 @@ export function parsePreviewHost(host, suffix) {
46
60
  const label = normalizedHost.slice(0, -suffixWithDot.length);
47
61
  if (!label || label.includes('.'))
48
62
  return null;
63
+ // The capability form is checked first: its leading 24-hex run would
64
+ // otherwise parse as the port of the legacy form's widest match.
65
+ const capabilityMatch = label.match(PREVIEW_CAPABILITY_HOST_LABEL_RE);
66
+ if (capabilityMatch) {
67
+ const capability = capabilityMatch[1];
68
+ const port = Number(capabilityMatch[2]);
69
+ const sid = capabilityMatch[3];
70
+ if (port < 1 || port > 65535 || !isPreviewHostSafeSid(sid))
71
+ return null;
72
+ return { port, sid, capability };
73
+ }
49
74
  const match = label.match(PREVIEW_HOST_LABEL_RE);
50
75
  if (!match)
51
76
  return null;
@@ -51,6 +51,23 @@ export declare const TENANT_HEADER = "X-Nimbus-Tenant";
51
51
  * `Authorization` is preserved — the one route on which that is true.
52
52
  */
53
53
  export declare const PREVIEW_CAPABILITY_HEADER = "x-nimbus-preview-capability";
54
+ /**
55
+ * Header the Worker sets when a request arrived on the public capability
56
+ * host form `<cap>--<port>--<sid>` — the request was never attached to a
57
+ * session, so the capability in `PREVIEW_CAPABILITY_HEADER` is the only
58
+ * credential it carries, and the session may honor it ONLY for a port whose
59
+ * stored visibility is `public`. Any other request answering on that mark
60
+ * is 404.
61
+ */
62
+ export declare const PUBLIC_BEARER_HEADER = "x-nimbus-public-bearer";
63
+ /**
64
+ * Header the Worker sets carrying the caller's verified token scopes, so the
65
+ * DO can gate routes that need more than session attach (e.g. the _diag
66
+ * abort). The header is deleted from the caller's own request before the
67
+ * verified value is set — a forged inbound copy never survives forwarding.
68
+ * Absent/empty in legacy mode, where no scopes were verified.
69
+ */
70
+ export declare const CALLER_SCOPES_HEADER = "x-nimbus-caller-scopes";
54
71
  /**
55
72
  * DO-name segment used when tenant scoping is disabled (legacy-public).
56
73
  * Picked so it cannot collide with a verified token's
@@ -85,6 +102,17 @@ export declare function parseSessionRoute(pathname: string): ParsedSessionRoute
85
102
  export interface ForwardOptions {
86
103
  /** Verified tenant segment for DO naming. */
87
104
  tenantSegment: string;
105
+ /**
106
+ * Router-minted headers the DO must see — the preview capability, the
107
+ * public-bearer mark. Entries arrive only from code that vetted them.
108
+ */
109
+ extraHeaders?: Readonly<Record<string, string>>;
110
+ /**
111
+ * The caller's verified token scopes. Forwarded as
112
+ * {@link CALLER_SCOPES_HEADER} so scope-gated routes inside the DO can
113
+ * check them. Omitted in legacy mode or when the caller was not verified.
114
+ */
115
+ callerScopes?: readonly string[];
88
116
  }
89
117
  /**
90
118
  * Forward a request to the session's DO.
@@ -106,6 +134,21 @@ export interface ForwardOptions {
106
134
  * @param opts Tenant scoping. See {@link ForwardOptions}.
107
135
  */
108
136
  export declare function forwardToSession(request: Request, route: ParsedSessionRoute, env: any, opts: ForwardOptions): Promise<Response>;
137
+ /**
138
+ * The card every session-facing error/status page shares: dark, centered,
139
+ * mono title. `metaRefreshSeconds` opts the page into self-refresh — the
140
+ * "starting" page re-asks on its own timer, the invalid page never does.
141
+ */
142
+ export declare function renderSessionStatusPage(input: {
143
+ title: string;
144
+ heading: string;
145
+ body: string;
146
+ action?: {
147
+ href: string;
148
+ label: string;
149
+ };
150
+ metaRefreshSeconds?: number;
151
+ }): string;
109
152
  /** HTML body for the "invalid session ID" 400 page. Tiny, inline-only. */
110
153
  export declare function renderInvalidSessionHtml(attemptedId: string): string;
111
154
  //# sourceMappingURL=session-router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-router.d.ts","sourceRoot":"","sources":["../../src/_shared/session-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,gCAAgC,CAAC;AAEvE;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,oBAAoB,CAAC;AAS1D,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAa7E;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,kBAAkB,EACzB,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,QAAQ,CAAC,CAsCnB;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA6BpE"}
1
+ {"version":3,"file":"session-router.d.ts","sourceRoot":"","sources":["../../src/_shared/session-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,gCAAgC,CAAC;AAEvE;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAE7D;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,oBAAoB,CAAC;AAS1D,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAa7E;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AACD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,kBAAkB,EACzB,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,QAAQ,CAAC,CA8CnB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,GAAG,MAAM,CA8BT;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAapE"}
@@ -52,6 +52,23 @@ export const TENANT_HEADER = 'X-Nimbus-Tenant';
52
52
  * `Authorization` is preserved — the one route on which that is true.
53
53
  */
54
54
  export const PREVIEW_CAPABILITY_HEADER = 'x-nimbus-preview-capability';
55
+ /**
56
+ * Header the Worker sets when a request arrived on the public capability
57
+ * host form `<cap>--<port>--<sid>` — the request was never attached to a
58
+ * session, so the capability in `PREVIEW_CAPABILITY_HEADER` is the only
59
+ * credential it carries, and the session may honor it ONLY for a port whose
60
+ * stored visibility is `public`. Any other request answering on that mark
61
+ * is 404.
62
+ */
63
+ export const PUBLIC_BEARER_HEADER = 'x-nimbus-public-bearer';
64
+ /**
65
+ * Header the Worker sets carrying the caller's verified token scopes, so the
66
+ * DO can gate routes that need more than session attach (e.g. the _diag
67
+ * abort). The header is deleted from the caller's own request before the
68
+ * verified value is set — a forged inbound copy never survives forwarding.
69
+ * Absent/empty in legacy mode, where no scopes were verified.
70
+ */
71
+ export const CALLER_SCOPES_HEADER = 'x-nimbus-caller-scopes';
55
72
  /**
56
73
  * DO-name segment used when tenant scoping is disabled (legacy-public).
57
74
  * Picked so it cannot collide with a verified token's
@@ -115,6 +132,16 @@ export function forwardToSession(request, route, env, opts) {
115
132
  const headers = new Headers(request.headers);
116
133
  headers.set(BASE_PATH_HEADER, route.basePath);
117
134
  headers.set(TENANT_HEADER, opts.tenantSegment);
135
+ // Verified scopes come from the router alone: a caller-supplied copy is
136
+ // deleted before the real value is set, never appended to.
137
+ headers.delete(CALLER_SCOPES_HEADER);
138
+ if (opts.callerScopes !== undefined) {
139
+ headers.set(CALLER_SCOPES_HEADER, opts.callerScopes.join(' '));
140
+ }
141
+ if (opts.extraHeaders) {
142
+ for (const [name, value] of Object.entries(opts.extraHeaders))
143
+ headers.set(name, value);
144
+ }
118
145
  // Load-bearing, not hygiene: the query is a first-class auth channel
119
146
  // (`extractBearerToken` reads it), so anything forwarded with the token
120
147
  // still attached would carry a live credential into inner-DO logs, the
@@ -142,18 +169,23 @@ export function forwardToSession(request, route, env, opts) {
142
169
  const stub = env.NIMBUS_SESSION.get(id);
143
170
  return stub.fetch(inner);
144
171
  }
145
- /** HTML body for the "invalid session ID" 400 page. Tiny, inline-only. */
146
- export function renderInvalidSessionHtml(attemptedId) {
147
- // Escape the attempted ID for display. We don't use innerHTML anywhere,
148
- // but defensive escaping keeps the HTML validator happy and avoids any
149
- // future XSS footguns if someone refactors this to document.write().
150
- const safe = String(attemptedId).replace(/[&<>"']/g, (c) => ({
151
- '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
152
- }[c]));
172
+ /**
173
+ * The card every session-facing error/status page shares: dark, centered,
174
+ * mono title. `metaRefreshSeconds` opts the page into self-refresh — the
175
+ * "starting" page re-asks on its own timer, the invalid page never does.
176
+ */
177
+ export function renderSessionStatusPage(input) {
178
+ const refresh = input.metaRefreshSeconds !== undefined
179
+ ? `<meta http-equiv="refresh" content="${input.metaRefreshSeconds}">`
180
+ : '';
181
+ const action = input.action !== undefined
182
+ ? `<a class="btn" href="${input.action.href}">${input.action.label}</a>`
183
+ : '';
153
184
  return `<!DOCTYPE html>
154
185
  <html lang="en"><head>
155
186
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
156
- <title>Invalid session — Nimbus</title>
187
+ ${refresh}
188
+ <title>${input.title} — Nimbus</title>
157
189
  <style>
158
190
  *{margin:0;padding:0;box-sizing:border-box}
159
191
  html,body{height:100%}
@@ -168,8 +200,23 @@ export function renderInvalidSessionHtml(attemptedId) {
168
200
  a.btn:hover{filter:brightness(1.1)}
169
201
  </style></head>
170
202
  <body><div class="card">
171
- <h1>Invalid session</h1>
172
- <p>The ID <code>${safe}</code> isn&rsquo;t a valid Nimbus session URL.<br>Launch a new one to get started.</p>
173
- <a class="btn" href="/">&larr; Back to Nimbus</a>
203
+ <h1>${input.heading}</h1>
204
+ <p>${input.body}</p>
205
+ ${action}
174
206
  </div></body></html>`;
175
207
  }
208
+ /** HTML body for the "invalid session ID" 400 page. Tiny, inline-only. */
209
+ export function renderInvalidSessionHtml(attemptedId) {
210
+ // Escape the attempted ID for display. We don't use innerHTML anywhere,
211
+ // but defensive escaping keeps the HTML validator happy and avoids any
212
+ // future XSS footguns if someone refactors this to document.write().
213
+ const safe = String(attemptedId).replace(/[&<>"']/g, (c) => ({
214
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
215
+ }[c]));
216
+ return renderSessionStatusPage({
217
+ title: 'Invalid session',
218
+ heading: 'Invalid session',
219
+ body: `The ID <code>${safe}</code> isn&rsquo;t a valid Nimbus session URL.<br>Launch a new one to get started.`,
220
+ action: { href: '/', label: '&larr; Back to Nimbus' },
221
+ });
222
+ }
@@ -2,7 +2,7 @@
2
2
  * esbuild-wasm-bundle.generated.ts — AUTO-GENERATED by scripts/bundle-esbuild-wasm.mjs
3
3
  * DO NOT EDIT.
4
4
  *
5
- * Bundled esbuild-wasm 0.24.2 adapter for use inside NimbusLoaderPool
5
+ * Bundled esbuild-wasm 0.24.2 adapter for use inside NimbusIsolatePool
6
6
  * isolates. Only the JS adapter is bundled here (~117 KiB); the wasm
7
7
  * binary itself lives in the static-assets layer at
8
8
  * public/_assets/esbuild-0.24.2.wasm and is fetched on demand by
@@ -2,7 +2,7 @@
2
2
  * esbuild-wasm-bundle.generated.ts — AUTO-GENERATED by scripts/bundle-esbuild-wasm.mjs
3
3
  * DO NOT EDIT.
4
4
  *
5
- * Bundled esbuild-wasm 0.24.2 adapter for use inside NimbusLoaderPool
5
+ * Bundled esbuild-wasm 0.24.2 adapter for use inside NimbusIsolatePool
6
6
  * isolates. Only the JS adapter is bundled here (~117 KiB); the wasm
7
7
  * binary itself lives in the static-assets layer at
8
8
  * public/_assets/esbuild-0.24.2.wasm and is fetched on demand by
@@ -57,7 +57,7 @@ import { REAL_VITE_VERSION, VITE_CLIENT_MJS, VITE_ENV_MJS, getRealViteBundle, ge
57
57
  import { CIRRUS_PLUGIN_REACT_VERSION, getCirrusPluginReactBundle, } from '../cirrus-plugin-react.generated.js';
58
58
  import { CIRRUS_NPM_CJS_VERSIONS, getCirrusNpmCjsBundles } from '../cirrus-npm-cjs.generated.js';
59
59
  import { CF_COMPAT_DATE } from '@nimbus-sh/core/constants.js';
60
- import { getCtxExports } from '@nimbus-sh/fabric/ctx-exports.js';
60
+ import { getCtxExports } from '@nimbus-sh/fabric/composition.js';
61
61
  import { buildFsSnapshot, generateFsShimModuleCode, generateFsPromisesShimModuleCode, generateSyntheticModuleCode, } from './real-vite-fs-shim.js';
62
62
  import { HmrBridge, registerHmrBridge, generateWsShimModuleCode, generateChokidarShimModuleCode, } from './real-vite-hmr.js';
63
63
  import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * facets/durable-images.ts — the durable application's boot-image store.
3
+ *
4
+ * A durable spawn's journal row carries only digests — the launch's code,
5
+ * modules, and environment have to live somewhere a reset can still read
6
+ * them, and that is the kernel-space VFS under `.nimbus/images/<sha256>`,
7
+ * content-addressed so a re-drive with the same recipe reads the same bytes.
8
+ *
9
+ * The store is deliberately separate from the process boot-image sweep in
10
+ * `image-store.ts`: that sweep is rooted at live pids and would collect an
11
+ * application's image the moment its process ends — the precise condition a
12
+ * durable spawn exists to survive. Durable images are kept for the
13
+ * application's life and released only by explicit removal.
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.
20
+ */
21
+ import type { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
22
+ import type { ResidentCodeSpec } from '@nimbus-sh/fabric/process-fabric.js';
23
+ import type { ResolvedWorkerLaunch, WorkerRecipe } from './manager.js';
24
+ /** The directory every durable application's image blobs live under. */
25
+ export declare const DURABLE_IMAGE_DIR = ".nimbus/images";
26
+ /**
27
+ * Persist a launch's image blobs, minting their digests, for a self-owned
28
+ * durable spawn. Reads and writes as CRED_KERNEL: the directory is session
29
+ * kernel data, not user content, and a durable application's images must not
30
+ * be writable — or deletable — by the user process they belong to.
31
+ */
32
+ export declare function persistDurableWorkerImage(vfs: SqliteVFS, workerCode: string, payload: {
33
+ modules: Record<string, string | {
34
+ wasm: ArrayBuffer;
35
+ }>;
36
+ env?: ResidentCodeSpec['env'];
37
+ vfsWasmModules?: Record<string, string>;
38
+ }): Promise<{
39
+ runner: string;
40
+ application: string;
41
+ }>;
42
+ /**
43
+ * The default resolver a self-owned durable spawn answers through: read the
44
+ * two blobs the spawn persisted, restore the env, and hand back the launch a
45
+ * re-drive can boot. Returns null only when an image row has gone missing —
46
+ * which a re-drive treats as 'the application is gone', exactly as an
47
+ * embedder-owned launch whose embedder answers null.
48
+ */
49
+ export declare function resolveDurableWorkerImage(vfs: SqliteVFS, recipe: WorkerRecipe): Promise<ResolvedWorkerLaunch | null>;
50
+ //# sourceMappingURL=durable-images.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * facets/durable-images.ts — the durable application's boot-image store.
3
+ *
4
+ * A durable spawn's journal row carries only digests — the launch's code,
5
+ * modules, and environment have to live somewhere a reset can still read
6
+ * them, and that is the kernel-space VFS under `.nimbus/images/<sha256>`,
7
+ * content-addressed so a re-drive with the same recipe reads the same bytes.
8
+ *
9
+ * The store is deliberately separate from the process boot-image sweep in
10
+ * `image-store.ts`: that sweep is rooted at live pids and would collect an
11
+ * application's image the moment its process ends — the precise condition a
12
+ * durable spawn exists to survive. Durable images are kept for the
13
+ * application's life and released only by explicit removal.
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.
20
+ */
21
+ import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
22
+ /** The directory every durable application's image blobs live under. */
23
+ export const DURABLE_IMAGE_DIR = '.nimbus/images';
24
+ const imagePath = (digest) => `${DURABLE_IMAGE_DIR}/${digest}`;
25
+ async function sha256Hex(text) {
26
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));
27
+ return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');
28
+ }
29
+ /**
30
+ * Persist a launch's image blobs, minting their digests, for a self-owned
31
+ * durable spawn. Reads and writes as CRED_KERNEL: the directory is session
32
+ * kernel data, not user content, and a durable application's images must not
33
+ * be writable — or deletable — by the user process they belong to.
34
+ */
35
+ export async function persistDurableWorkerImage(vfs, workerCode, payload) {
36
+ const kernel = vfs.as(CRED_KERNEL);
37
+ kernel.mkdir(DURABLE_IMAGE_DIR, { recursive: true });
38
+ const runner = await sha256Hex(workerCode);
39
+ kernel.writeFile(imagePath(runner), workerCode);
40
+ const applicationPayload = JSON.stringify({
41
+ modules: payload.modules,
42
+ env: payload.env ?? null,
43
+ vfsWasmModules: payload.vfsWasmModules ?? null,
44
+ });
45
+ const application = await sha256Hex(applicationPayload);
46
+ kernel.writeFile(imagePath(application), applicationPayload);
47
+ return { runner, application };
48
+ }
49
+ /**
50
+ * The default resolver a self-owned durable spawn answers through: read the
51
+ * two blobs the spawn persisted, restore the env, and hand back the launch a
52
+ * re-drive can boot. Returns null only when an image row has gone missing —
53
+ * which a re-drive treats as 'the application is gone', exactly as an
54
+ * embedder-owned launch whose embedder answers null.
55
+ */
56
+ export async function resolveDurableWorkerImage(vfs, recipe) {
57
+ const kernel = vfs.as(CRED_KERNEL);
58
+ let runnerBytes;
59
+ let applicationBytes;
60
+ try {
61
+ runnerBytes = kernel.readFile(imagePath(recipe.image.runner));
62
+ applicationBytes = kernel.readFile(imagePath(recipe.image.application));
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ const runner = new TextDecoder().decode(runnerBytes);
68
+ const { modules = {}, env = null, vfsWasmModules = undefined } = JSON.parse(new TextDecoder().decode(applicationBytes));
69
+ return {
70
+ env: env ?? null,
71
+ globalOutbound: undefined,
72
+ modules: { 'worker.js': runner, ...modules },
73
+ ...(vfsWasmModules !== null ? { vfsWasmModules } : {}),
74
+ };
75
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * facets/durable-slots.ts — the durable application's facet-name allocator.
3
+ *
4
+ * A durable application's storage has to answer under ONE facet name for its
5
+ * whole life: abort ends the process, not the store, so relaunch and eviction
6
+ * re-attach the same SQLite — which is only true while the name is stable.
7
+ * The ephemeral slot book in `workerd-facet-host.ts` cannot supply it: it is
8
+ * in-memory, and a fresh incarnation restarts `proc-slot-<n>` numbering from
9
+ * zero, so a durable name needs a store of its own — the DO storage this file
10
+ * keeps it in.
11
+ *
12
+ * The rows:
13
+ *
14
+ * durable-slot:next — the lowest never-issued slot number. Minting burns
15
+ * one facet ID forever, so a freed name goes to
16
+ * `free`, never back to `next`.
17
+ * durable-slot:free — slot numbers whose applications were removed.
18
+ * durable-slot:<owner> — the owner's pinned slot. Written once, ever;
19
+ * re-read on every relaunch and re-drive.
20
+ *
21
+ * Names carry the `app-slot-` prefix, disjoint by construction from the
22
+ * ephemeral book's `proc-slot-` — the two namespaces share the facet-ID
23
+ * budget, and a collision would hand one application's retained storage to
24
+ * another process.
25
+ */
26
+ /** The facet name a durable slot number names. */
27
+ export declare function durableFacetName(slot: number): string;
28
+ /**
29
+ * The owner's facet name, minted on first call and pinned for the
30
+ * application's life. The owner key, counter and free list move inside one
31
+ * transaction, so a concurrent spawn cannot split the claim, and a re-read
32
+ * after a reset — or after eviction — answers the same name.
33
+ *
34
+ * A fresh name records the mint against the lifetime facet-ID ledger so the
35
+ * budget a durable spawn consumes is counted the same way an ephemeral one's
36
+ * is.
37
+ */
38
+ export declare function acquireDurableFacetSlot(ctx: DurableObjectState, owner: string): Promise<string>;
39
+ /**
40
+ * Hand the owner's slot back to the free list and drop its pin — the last
41
+ * step of explicit removal, after the facet's SQLite is already gone. Answers
42
+ * the freed name, or null when the owner held nothing.
43
+ */
44
+ export declare function freeDurableFacetSlot(ctx: DurableObjectState, owner: string): Promise<string | null>;
45
+ //# sourceMappingURL=durable-slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-slots.d.ts","sourceRoot":"","sources":["../../src/facets/durable-slots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAUH,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB"}