@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
|
@@ -55,24 +55,42 @@ declare const PortVisibilitySchema: z.ZodEnum<{
|
|
|
55
55
|
public: "public";
|
|
56
56
|
}>;
|
|
57
57
|
export type PortVisibility = z.infer<typeof PortVisibilitySchema>;
|
|
58
|
+
/**
|
|
59
|
+
* The optional name alias a reservation may carry: one DNS label, so it can
|
|
60
|
+
* stand where the port stands in a preview host (`<name>--<sid>`). Never
|
|
61
|
+
* purely numeric — a numeric middle label IS a port. No `--`: that is the
|
|
62
|
+
* host-label separator. No 24 lowercase hex: that is ambiguous with a
|
|
63
|
+
* capability label, so parsing could mistake a name for a bearer.
|
|
64
|
+
*/
|
|
65
|
+
export declare const APP_NAME_RE: RegExp;
|
|
66
|
+
export declare function isValidAppName(name: string): boolean;
|
|
58
67
|
export declare const PortRecordSchema: z.ZodObject<{
|
|
68
|
+
kind: z.ZodDefault<z.ZodEnum<{
|
|
69
|
+
explicit: "explicit";
|
|
70
|
+
derived: "derived";
|
|
71
|
+
}>>;
|
|
59
72
|
capability: z.ZodNullable<z.ZodString>;
|
|
60
73
|
owner: z.ZodNullable<z.ZodString>;
|
|
61
74
|
visibility: z.ZodOptional<z.ZodEnum<{
|
|
62
75
|
scoped: "scoped";
|
|
63
76
|
public: "public";
|
|
64
77
|
}>>;
|
|
78
|
+
name: z.ZodOptional<z.ZodString>;
|
|
65
79
|
}, z.core.$strip>;
|
|
66
80
|
export interface PortExposure {
|
|
67
81
|
readonly capability: string;
|
|
68
82
|
readonly owner: string | null;
|
|
69
83
|
readonly visibility: PortVisibility;
|
|
84
|
+
readonly name?: string;
|
|
70
85
|
}
|
|
71
86
|
/** The per-port record as stored: a bare reservation has no capability yet. */
|
|
72
87
|
export interface PortReservation {
|
|
88
|
+
readonly kind: 'explicit' | 'derived';
|
|
73
89
|
readonly owner: string | null;
|
|
74
90
|
readonly capability: string | null;
|
|
75
91
|
readonly visibility: PortVisibility;
|
|
92
|
+
/** The reservation's name alias, when one was given at expose time. */
|
|
93
|
+
readonly name?: string;
|
|
76
94
|
}
|
|
77
95
|
export declare function portRecordKey(port: number): string;
|
|
78
96
|
/** Read the raw per-port record: a reservation, an exposure, or nothing. */
|
|
@@ -81,6 +99,24 @@ export declare function readPortReservation(ctx: {
|
|
|
81
99
|
}, port: number): Promise<PortReservation | null>;
|
|
82
100
|
/** Read retained exposure metadata without starting a session or restoring a listener. */
|
|
83
101
|
export declare function readPortExposure(ctx: PortCapabilityHost['ctx'], port: number): Promise<PortExposure | null>;
|
|
102
|
+
/** Every stored port record, keyed by port — the scan `apps.list` and the name lookups read. */
|
|
103
|
+
export declare function listPortReservations(ctx: {
|
|
104
|
+
storage: PortReservationTransaction;
|
|
105
|
+
}): Promise<Map<number, PortReservation>>;
|
|
106
|
+
/** The port a name alias resolves to inside this session, or null. */
|
|
107
|
+
export declare function readPortReservationByName(ctx: {
|
|
108
|
+
storage: PortReservationTransaction;
|
|
109
|
+
}, name: string): Promise<{
|
|
110
|
+
port: number;
|
|
111
|
+
reservation: PortReservation;
|
|
112
|
+
} | null>;
|
|
113
|
+
/** The port an owner holds, or null when the owner has no reservation. */
|
|
114
|
+
export declare function readPortReservationByOwner(ctx: {
|
|
115
|
+
storage: PortReservationTransaction;
|
|
116
|
+
}, owner: string): Promise<{
|
|
117
|
+
port: number;
|
|
118
|
+
reservation: PortReservation;
|
|
119
|
+
} | null>;
|
|
84
120
|
/**
|
|
85
121
|
* Hold a port for `owner` across instances. The owner's existing port is
|
|
86
122
|
* answered again; otherwise the preferred port is claimed, or the lowest
|
|
@@ -97,9 +133,12 @@ export declare function readPortExposure(ctx: PortCapabilityHost['ctx'], port: n
|
|
|
97
133
|
export declare function reservePort(ctx: PortReservationHost['ctx'], input: {
|
|
98
134
|
owner: string;
|
|
99
135
|
preferredPort?: number;
|
|
136
|
+
kind?: 'explicit' | 'derived';
|
|
100
137
|
occupiedPorts: ReadonlySet<number>;
|
|
101
138
|
capability?: string;
|
|
102
139
|
visibility?: PortVisibility;
|
|
140
|
+
/** A name alias for the reservation — unique per session, DNS-label-safe. */
|
|
141
|
+
name?: string;
|
|
103
142
|
}): Promise<number>;
|
|
104
143
|
/** End an owner's hold on a port. Another owner's record is left alone and refused. */
|
|
105
144
|
export declare function releasePortReservation(ctx: PortReservationHost['ctx'], input: {
|
|
@@ -120,11 +159,62 @@ export declare function restorePortCapability(self: PortCapabilityHost, port: nu
|
|
|
120
159
|
*/
|
|
121
160
|
export declare function restoreReservedPortCapability(self: PortCapabilityHost, port: number, owner: string): Promise<string | null>;
|
|
122
161
|
export declare function persistPortCapability(self: PortCapabilityHost, port: number, capability: string): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Replace the port's capability with a freshly minted one, in place: owner,
|
|
164
|
+
* visibility and name stay, every URL built on the old value stops
|
|
165
|
+
* resolving. Answers the new capability, or null when no record exists —
|
|
166
|
+
* there is nothing to rotate on a port nobody has reserved or exposed.
|
|
167
|
+
*/
|
|
168
|
+
export declare function rotatePortCapability(self: PortCapabilityHost, port: number, capability: string): Promise<string | null>;
|
|
123
169
|
/**
|
|
124
170
|
* Retire the durable capability for a port. Called before every registration
|
|
125
171
|
* that is not a restore, so a token handed out for the previous occupant of a
|
|
126
172
|
* port cannot reach the next one. An owner's reservation survives it.
|
|
127
173
|
*/
|
|
128
174
|
export declare function clearPortCapability(self: PortCapabilityHost, port: number): Promise<void>;
|
|
175
|
+
/** The host surface `routeToSessionPort` actually reads — the narrowest shape that serves every port-addressed route. */
|
|
176
|
+
export interface SessionPortHost {
|
|
177
|
+
ctx: DurableObjectState;
|
|
178
|
+
portRegistry: PortRegistry;
|
|
179
|
+
ensureDurableAppOnPort?: (port: number) => Promise<'absent' | 'started' | 'failed'>;
|
|
180
|
+
_viteShimPort?: number | null;
|
|
181
|
+
viteDevServer?: {
|
|
182
|
+
isRunning: boolean;
|
|
183
|
+
handleRequest(request: Request, innerPath: string, mountBase: string): Promise<Response>;
|
|
184
|
+
} | null;
|
|
185
|
+
cirrusReal?: {
|
|
186
|
+
isRunning: boolean;
|
|
187
|
+
} | null;
|
|
188
|
+
restorePersistedDevServer?: (onlyPort?: number) => Promise<void>;
|
|
189
|
+
acceptCirrusHmrWs?: (request: Request) => Response;
|
|
190
|
+
}
|
|
191
|
+
export declare function normalizeForwardedHttpPath(path: string): string;
|
|
192
|
+
/** True if `innerPath` targets the cirrus-real HMR socket, under any mount base. */
|
|
193
|
+
export declare function isCirrusHmrPath(innerPath: string): boolean;
|
|
194
|
+
/**
|
|
195
|
+
* Re-adopt a preview capability the embedder already holds, after a restore
|
|
196
|
+
* re-registered the port under a freshly minted one.
|
|
197
|
+
*/
|
|
198
|
+
export declare function readoptCapability(self: PortCapabilityHost, port: number, capability: string | null): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Route a request to whatever is listening on a session port.
|
|
201
|
+
*
|
|
202
|
+
* The one implementation behind every port-addressed surface: `/port/<n>/`,
|
|
203
|
+
* `/preview/?port=N`, the `<port>--<sid>` preview hostname (which the router
|
|
204
|
+
* forwards as `/port/<n>/`), and the embedder's capability route. They
|
|
205
|
+
* differ only in how the port and the inner path are spelled, so they must
|
|
206
|
+
* not differ in what answers.
|
|
207
|
+
*
|
|
208
|
+
* `mountBase` is the public URL prefix the served app is mounted at for THIS
|
|
209
|
+
* request — '' for a root-mounted `<port>--<sid>` host, '/s/<sid>/preview' for
|
|
210
|
+
* the preview path. The in-process Cirrus dev server rewrites base-relative
|
|
211
|
+
* URLs (module URLs, <base href>, BASE_URL, router basename), so it is handed
|
|
212
|
+
* the base directly: the generic port proxy strips the Nimbus base header at
|
|
213
|
+
* the untrusted-code boundary and cannot carry it, and a plain user server on
|
|
214
|
+
* any other port is mounted at root and needs no rewriting.
|
|
215
|
+
*/
|
|
216
|
+
export declare function routeToSessionPort(self: SessionPortHost, port: number, request: Request, innerPath: string, mountBase: string, capability?: string): Promise<Response>;
|
|
217
|
+
/** Route a capability-authenticated embedder request to a guest HTTP server. */
|
|
218
|
+
export declare function routeCapabilityPort(self: SessionPortHost, port: number, capability: string, request: Request, innerPath: string): Promise<Response>;
|
|
129
219
|
export {};
|
|
130
220
|
//# sourceMappingURL=port-capability.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"port-capability.d.ts","sourceRoot":"","sources":["../../src/session/port-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"port-capability.d.ts","sourceRoot":"","sources":["../../src/session/port-capability.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,15 +23,31 @@
|
|
|
23
23
|
* keeps the owner's reservation; only `releasePortReservation` ends it.
|
|
24
24
|
*/
|
|
25
25
|
import { z } from 'zod/v4';
|
|
26
|
+
import { PUBLIC_BEARER_HEADER, renderSessionStatusPage } from '../_shared/session-router.js';
|
|
26
27
|
import { PORT_CAPABILITY_KEY_PREFIX } from './keys.js';
|
|
27
28
|
/** The shape `createPortCapability` mints: 12 random bytes, hex. */
|
|
28
29
|
const PortCapabilitySchema = z.string().regex(/^[a-f0-9]{24}$/);
|
|
29
30
|
/** 'scoped' is the default: capability-checked but never the public bearer. */
|
|
30
31
|
const PortVisibilitySchema = z.enum(['scoped', 'public']);
|
|
32
|
+
/**
|
|
33
|
+
* The optional name alias a reservation may carry: one DNS label, so it can
|
|
34
|
+
* stand where the port stands in a preview host (`<name>--<sid>`). Never
|
|
35
|
+
* purely numeric — a numeric middle label IS a port. No `--`: that is the
|
|
36
|
+
* host-label separator. No 24 lowercase hex: that is ambiguous with a
|
|
37
|
+
* capability label, so parsing could mistake a name for a bearer.
|
|
38
|
+
*/
|
|
39
|
+
export const APP_NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
40
|
+
export function isValidAppName(name) {
|
|
41
|
+
return APP_NAME_RE.test(name) && !name.includes('--')
|
|
42
|
+
&& !/^\d+$/.test(name) && !/^[a-f0-9]{24}$/.test(name);
|
|
43
|
+
}
|
|
44
|
+
const AppNameSchema = z.string().refine(isValidAppName, 'app name must be a DNS label: no --, not numeric, not 24 lowercase hex');
|
|
31
45
|
export const PortRecordSchema = z.object({
|
|
46
|
+
kind: z.enum(['explicit', 'derived']).default('explicit'),
|
|
32
47
|
capability: PortCapabilitySchema.nullable(),
|
|
33
48
|
owner: z.string().nullable(),
|
|
34
49
|
visibility: PortVisibilitySchema.optional(),
|
|
50
|
+
name: AppNameSchema.optional(),
|
|
35
51
|
});
|
|
36
52
|
const RESERVATION_FIRST_PORT = 20000;
|
|
37
53
|
const RESERVATION_LAST_PORT = 65535;
|
|
@@ -52,9 +68,43 @@ export async function readPortReservation(ctx, port) {
|
|
|
52
68
|
export async function readPortExposure(ctx, port) {
|
|
53
69
|
const stored = await readPortReservation(ctx, port);
|
|
54
70
|
return stored !== null && stored.capability !== null
|
|
55
|
-
? {
|
|
71
|
+
? {
|
|
72
|
+
capability: stored.capability, owner: stored.owner, visibility: stored.visibility,
|
|
73
|
+
...(stored.name !== undefined ? { name: stored.name } : {}),
|
|
74
|
+
}
|
|
56
75
|
: null;
|
|
57
76
|
}
|
|
77
|
+
/** Every stored port record, keyed by port — the scan `apps.list` and the name lookups read. */
|
|
78
|
+
export async function listPortReservations(ctx) {
|
|
79
|
+
const rows = await ctx.storage.list({ prefix: PORT_CAPABILITY_KEY_PREFIX });
|
|
80
|
+
const out = new Map();
|
|
81
|
+
for (const [key, value] of rows) {
|
|
82
|
+
const port = Number(key.slice(PORT_CAPABILITY_KEY_PREFIX.length));
|
|
83
|
+
const parsed = PortRecordSchema.safeParse(value);
|
|
84
|
+
if (!Number.isInteger(port) || !parsed.success)
|
|
85
|
+
continue;
|
|
86
|
+
out.set(port, { ...parsed.data, visibility: parsed.data.visibility ?? 'scoped' });
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
/** The port a name alias resolves to inside this session, or null. */
|
|
91
|
+
export async function readPortReservationByName(ctx, name) {
|
|
92
|
+
if (!isValidAppName(name))
|
|
93
|
+
return null;
|
|
94
|
+
for (const [port, reservation] of await listPortReservations(ctx)) {
|
|
95
|
+
if (reservation.name === name)
|
|
96
|
+
return { port, reservation };
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/** The port an owner holds, or null when the owner has no reservation. */
|
|
101
|
+
export async function readPortReservationByOwner(ctx, owner) {
|
|
102
|
+
for (const [port, reservation] of await listPortReservations(ctx)) {
|
|
103
|
+
if (reservation.owner === owner)
|
|
104
|
+
return { port, reservation };
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
58
108
|
/**
|
|
59
109
|
* Hold a port for `owner` across instances. The owner's existing port is
|
|
60
110
|
* answered again; otherwise the preferred port is claimed, or the lowest
|
|
@@ -69,32 +119,52 @@ export async function readPortExposure(ctx, port) {
|
|
|
69
119
|
* capability is never rotated, a URL already handed out stays good.
|
|
70
120
|
*/
|
|
71
121
|
export async function reservePort(ctx, input) {
|
|
122
|
+
if (input.name !== undefined && !isValidAppName(input.name)) {
|
|
123
|
+
throw conflict(`'${input.name}' is not a valid app name (one DNS label, not numeric, not 24 hex)`);
|
|
124
|
+
}
|
|
72
125
|
const claim = async (txn) => {
|
|
73
126
|
const records = await txn.list({ prefix: PORT_CAPABILITY_KEY_PREFIX });
|
|
74
127
|
const taken = new Set(input.occupiedPorts);
|
|
128
|
+
const unowned = new Map();
|
|
75
129
|
let held = null;
|
|
76
130
|
for (const [storedKey, value] of records) {
|
|
77
131
|
const port = Number(storedKey.slice(PORT_CAPABILITY_KEY_PREFIX.length));
|
|
78
132
|
if (!Number.isInteger(port))
|
|
79
133
|
continue;
|
|
80
|
-
taken.add(port);
|
|
81
134
|
const parsed = PortRecordSchema.safeParse(value);
|
|
82
|
-
|
|
135
|
+
// listPorts may already have persisted the live capability without an
|
|
136
|
+
// owner. That is an exposure to adopt, not somebody else's reservation.
|
|
137
|
+
if (parsed.success && parsed.data.owner === null)
|
|
138
|
+
unowned.set(port, parsed.data);
|
|
139
|
+
else
|
|
140
|
+
taken.add(port);
|
|
141
|
+
if (!parsed.success)
|
|
142
|
+
continue;
|
|
143
|
+
if (parsed.data.owner === input.owner) {
|
|
83
144
|
held = { port, stored: parsed.data };
|
|
84
145
|
}
|
|
146
|
+
else if (input.name !== undefined && parsed.data.name === input.name
|
|
147
|
+
&& !(parsed.data.owner === null && port === input.preferredPort)) {
|
|
148
|
+
throw conflict(`name '${input.name}' is already taken by another app on port ${port}`);
|
|
149
|
+
}
|
|
85
150
|
}
|
|
86
151
|
const preferred = input.preferredPort;
|
|
87
152
|
if (held !== null) {
|
|
88
153
|
if (preferred !== undefined && preferred !== held.port) {
|
|
89
154
|
throw conflict(`owner already holds port ${held.port}, cannot reserve ${preferred}`);
|
|
90
155
|
}
|
|
91
|
-
// Upgrade in place: adopt a caller-supplied capability the row lacks
|
|
92
|
-
//
|
|
156
|
+
// Upgrade in place: adopt a caller-supplied capability the row lacks,
|
|
157
|
+
// apply the caller's visibility and/or name — never rotate an existing
|
|
158
|
+
// capability.
|
|
93
159
|
const stored = held.stored;
|
|
94
160
|
const capability = stored?.capability ?? input.capability ?? null;
|
|
95
161
|
const visibility = input.visibility ?? stored?.visibility ?? 'scoped';
|
|
96
|
-
|
|
97
|
-
|
|
162
|
+
const name = input.name ?? stored?.name;
|
|
163
|
+
if (stored === null || capability !== stored.capability
|
|
164
|
+
|| visibility !== (stored.visibility ?? 'scoped') || name !== stored.name) {
|
|
165
|
+
await txn.put(portRecordKey(held.port), {
|
|
166
|
+
owner: input.owner, kind: stored?.kind ?? input.kind ?? 'explicit', capability, visibility, ...(name !== undefined ? { name } : {}),
|
|
167
|
+
});
|
|
98
168
|
}
|
|
99
169
|
return held.port;
|
|
100
170
|
}
|
|
@@ -114,10 +184,14 @@ export async function reservePort(ctx, input) {
|
|
|
114
184
|
if (port > RESERVATION_LAST_PORT)
|
|
115
185
|
throw conflict('no free port left to reserve');
|
|
116
186
|
}
|
|
187
|
+
const exposure = unowned.get(port);
|
|
188
|
+
const name = input.name ?? exposure?.name;
|
|
117
189
|
await txn.put(portRecordKey(port), {
|
|
118
190
|
owner: input.owner,
|
|
119
|
-
|
|
120
|
-
|
|
191
|
+
kind: input.kind ?? 'explicit',
|
|
192
|
+
capability: exposure?.capability ?? input.capability ?? null,
|
|
193
|
+
visibility: input.visibility ?? exposure?.visibility ?? 'scoped',
|
|
194
|
+
...(name !== undefined ? { name } : {}),
|
|
121
195
|
});
|
|
122
196
|
return port;
|
|
123
197
|
};
|
|
@@ -178,12 +252,37 @@ export async function persistPortCapability(self, port, capability) {
|
|
|
178
252
|
await txn.put(portRecordKey(port), {
|
|
179
253
|
capability: PortCapabilitySchema.parse(capability),
|
|
180
254
|
owner: parsed.success ? parsed.data.owner : null,
|
|
255
|
+
kind: parsed.success ? parsed.data.kind : 'explicit',
|
|
181
256
|
...(parsed.success && parsed.data.visibility !== undefined
|
|
182
257
|
? { visibility: parsed.data.visibility }
|
|
183
258
|
: {}),
|
|
259
|
+
...(parsed.success && parsed.data.name !== undefined
|
|
260
|
+
? { name: parsed.data.name }
|
|
261
|
+
: {}),
|
|
184
262
|
});
|
|
185
263
|
});
|
|
186
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Replace the port's capability with a freshly minted one, in place: owner,
|
|
267
|
+
* visibility and name stay, every URL built on the old value stops
|
|
268
|
+
* resolving. Answers the new capability, or null when no record exists —
|
|
269
|
+
* there is nothing to rotate on a port nobody has reserved or exposed.
|
|
270
|
+
*/
|
|
271
|
+
export async function rotatePortCapability(self, port, capability) {
|
|
272
|
+
const rotated = await self.ctx.storage.transaction(async (txn) => {
|
|
273
|
+
const parsed = PortRecordSchema.safeParse(await txn.get(portRecordKey(port)));
|
|
274
|
+
if (!parsed.success)
|
|
275
|
+
return null;
|
|
276
|
+
await txn.put(portRecordKey(port), {
|
|
277
|
+
...parsed.data,
|
|
278
|
+
capability: PortCapabilitySchema.parse(capability),
|
|
279
|
+
});
|
|
280
|
+
return capability;
|
|
281
|
+
});
|
|
282
|
+
// Registry adoption belongs to the caller, after a fresh serving-owner
|
|
283
|
+
// check: the storage await above may have allowed a new listener to bind.
|
|
284
|
+
return rotated;
|
|
285
|
+
}
|
|
187
286
|
/**
|
|
188
287
|
* Retire the durable capability for a port. Called before every registration
|
|
189
288
|
* that is not a restore, so a token handed out for the previous occupant of a
|
|
@@ -196,11 +295,122 @@ export async function clearPortCapability(self, port) {
|
|
|
196
295
|
if (record !== null && record.owner !== null) {
|
|
197
296
|
await txn.put(portRecordKey(port), {
|
|
198
297
|
owner: record.owner,
|
|
298
|
+
kind: record.kind,
|
|
199
299
|
capability: null,
|
|
200
300
|
...(record.visibility !== undefined ? { visibility: record.visibility } : {}),
|
|
301
|
+
...(record.name !== undefined ? { name: record.name } : {}),
|
|
201
302
|
});
|
|
202
303
|
return;
|
|
203
304
|
}
|
|
204
305
|
await txn.delete(portRecordKey(port));
|
|
205
306
|
});
|
|
206
307
|
}
|
|
308
|
+
export function normalizeForwardedHttpPath(path) {
|
|
309
|
+
const text = path || '/';
|
|
310
|
+
return '/' + text.replace(/^\/+/, '');
|
|
311
|
+
}
|
|
312
|
+
/** True if `innerPath` targets the cirrus-real HMR socket, under any mount base. */
|
|
313
|
+
export function isCirrusHmrPath(innerPath) {
|
|
314
|
+
const p = innerPath.split('?')[0];
|
|
315
|
+
return p === '/__nimbus_hmr' || p.endsWith('/__nimbus_hmr');
|
|
316
|
+
}
|
|
317
|
+
/** The "durable application is (re)starting" 503 page — self-refreshing. */
|
|
318
|
+
function renderPortStartingHtml(port) {
|
|
319
|
+
return renderSessionStatusPage({
|
|
320
|
+
title: 'Starting',
|
|
321
|
+
heading: 'Starting…',
|
|
322
|
+
body: `The application on port <code>${port}</code> is restarting.<br>This page refreshes itself.`,
|
|
323
|
+
metaRefreshSeconds: 3,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Re-adopt a preview capability the embedder already holds, after a restore
|
|
328
|
+
* re-registered the port under a freshly minted one.
|
|
329
|
+
*/
|
|
330
|
+
export async function readoptCapability(self, port, capability) {
|
|
331
|
+
if (!capability)
|
|
332
|
+
return;
|
|
333
|
+
if (self.portRegistry.restoreCapability(port, capability)) {
|
|
334
|
+
await persistPortCapability(self, port, capability);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Route a request to whatever is listening on a session port.
|
|
339
|
+
*
|
|
340
|
+
* The one implementation behind every port-addressed surface: `/port/<n>/`,
|
|
341
|
+
* `/preview/?port=N`, the `<port>--<sid>` preview hostname (which the router
|
|
342
|
+
* forwards as `/port/<n>/`), and the embedder's capability route. They
|
|
343
|
+
* differ only in how the port and the inner path are spelled, so they must
|
|
344
|
+
* not differ in what answers.
|
|
345
|
+
*
|
|
346
|
+
* `mountBase` is the public URL prefix the served app is mounted at for THIS
|
|
347
|
+
* request — '' for a root-mounted `<port>--<sid>` host, '/s/<sid>/preview' for
|
|
348
|
+
* the preview path. The in-process Cirrus dev server rewrites base-relative
|
|
349
|
+
* URLs (module URLs, <base href>, BASE_URL, router basename), so it is handed
|
|
350
|
+
* the base directly: the generic port proxy strips the Nimbus base header at
|
|
351
|
+
* the untrusted-code boundary and cannot carry it, and a plain user server on
|
|
352
|
+
* any other port is mounted at root and needs no rewriting.
|
|
353
|
+
*/
|
|
354
|
+
export async function routeToSessionPort(self, port, request, innerPath, mountBase, capability) {
|
|
355
|
+
await self.restorePersistedDevServer?.(port);
|
|
356
|
+
// The durable seam: a request on a port nothing is serving may be a
|
|
357
|
+
// durable application a reset left dead — the alarm pump would re-drive
|
|
358
|
+
// it eventually, but a URL in someone's hands cannot wait for eventually.
|
|
359
|
+
// 'absent' falls through to the honest 502 unchanged; 'started' continues
|
|
360
|
+
// to normal routing with the port freshly bound; 'failed' is mid-launch
|
|
361
|
+
// or a lost boot, so the page re-asks on its own timer.
|
|
362
|
+
if (self.ensureDurableAppOnPort !== undefined) {
|
|
363
|
+
const durable = await self.ensureDurableAppOnPort(port);
|
|
364
|
+
if (durable === 'failed') {
|
|
365
|
+
return new Response(renderPortStartingHtml(port), {
|
|
366
|
+
status: 503,
|
|
367
|
+
headers: {
|
|
368
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
369
|
+
'Cache-Control': 'no-store',
|
|
370
|
+
'Retry-After': '3',
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (capability !== undefined) {
|
|
376
|
+
// A rebuilt supervisor holds a capability nobody was handed; the durable
|
|
377
|
+
// one is the value in circulation, so it wins before the check.
|
|
378
|
+
await restorePortCapability(self, port);
|
|
379
|
+
if (!self.portRegistry.hasCapability(port, capability)) {
|
|
380
|
+
// 404, not 403: a wrong capability must not confirm that the port is
|
|
381
|
+
// listening at all.
|
|
382
|
+
return new Response('Not found', { status: 404 });
|
|
383
|
+
}
|
|
384
|
+
// The public bearer form arrived without a session attach: the
|
|
385
|
+
// capability is real, but it authorises this route only for a port the
|
|
386
|
+
// owner deliberately exposed — the stored visibility is the gate.
|
|
387
|
+
if (request.headers.get(PUBLIC_BEARER_HEADER) !== null) {
|
|
388
|
+
const reservation = await readPortReservation(self.ctx, port);
|
|
389
|
+
if (reservation === null || reservation.visibility !== 'public') {
|
|
390
|
+
return new Response('Not found', { status: 404 });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (port === self._viteShimPort) {
|
|
395
|
+
// The preview HMR WebSocket can't cross the port-registry RPC, so it is
|
|
396
|
+
// accepted in-DO for both dev servers — the same handling `/preview/` uses.
|
|
397
|
+
if (self.cirrusReal?.isRunning && self.acceptCirrusHmrWs && isCirrusHmrPath(innerPath)) {
|
|
398
|
+
return self.acceptCirrusHmrWs(request);
|
|
399
|
+
}
|
|
400
|
+
// The Cirrus dev server rewrites base-relative URLs per request and the
|
|
401
|
+
// generic proxy strips the base header, so hand it the request directly.
|
|
402
|
+
if (self.viteDevServer?.isRunning) {
|
|
403
|
+
return self.viteDevServer.handleRequest(request, innerPath, mountBase);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const proxied = capability === undefined
|
|
407
|
+
? await self.portRegistry.routeRequest(port, request, innerPath)
|
|
408
|
+
: await self.portRegistry.routeCapabilityRequest(port, capability, request, innerPath);
|
|
409
|
+
if (proxied)
|
|
410
|
+
return proxied;
|
|
411
|
+
return new Response(`No process listening on port ${port}`, { status: 502 });
|
|
412
|
+
}
|
|
413
|
+
/** Route a capability-authenticated embedder request to a guest HTTP server. */
|
|
414
|
+
export function routeCapabilityPort(self, port, capability, request, innerPath) {
|
|
415
|
+
return routeToSessionPort(self, port, request, normalizeForwardedHttpPath(innerPath), '', capability);
|
|
416
|
+
}
|