@nimbus-sh/fabric 0.1.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/LICENSE +21 -0
- package/README.md +487 -0
- package/dist/alarms.d.ts +134 -0
- package/dist/alarms.d.ts.map +1 -0
- package/dist/alarms.js +214 -0
- package/dist/bindings.d.ts +316 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +678 -0
- package/dist/ctx-exports.d.ts +47 -0
- package/dist/ctx-exports.d.ts.map +1 -0
- package/dist/ctx-exports.js +54 -0
- package/dist/facet-image-store.d.ts +112 -0
- package/dist/facet-image-store.d.ts.map +1 -0
- package/dist/facet-image-store.js +181 -0
- package/dist/fanout-pool.d.ts +223 -0
- package/dist/fanout-pool.d.ts.map +1 -0
- package/dist/fanout-pool.js +368 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/inner-do-registry.d.ts +41 -0
- package/dist/inner-do-registry.d.ts.map +1 -0
- package/dist/inner-do-registry.js +51 -0
- package/dist/launch-journal.d.ts +170 -0
- package/dist/launch-journal.d.ts.map +1 -0
- package/dist/launch-journal.js +154 -0
- package/dist/launch-pacer.d.ts +173 -0
- package/dist/launch-pacer.d.ts.map +1 -0
- package/dist/launch-pacer.js +193 -0
- package/dist/loader-ledger.d.ts +57 -0
- package/dist/loader-ledger.d.ts.map +1 -0
- package/dist/loader-ledger.js +91 -0
- package/dist/loader-pool.d.ts +315 -0
- package/dist/loader-pool.d.ts.map +1 -0
- package/dist/loader-pool.js +666 -0
- package/dist/process-fabric.d.ts +524 -0
- package/dist/process-fabric.d.ts.map +1 -0
- package/dist/process-fabric.js +388 -0
- package/dist/process-host.d.ts +132 -0
- package/dist/process-host.d.ts.map +1 -0
- package/dist/process-host.js +444 -0
- package/dist/vendor/errors.d.ts +24 -0
- package/dist/vendor/errors.d.ts.map +1 -0
- package/dist/vendor/errors.js +46 -0
- package/dist/vendor/serialize.d.ts +3 -0
- package/dist/vendor/serialize.d.ts.map +1 -0
- package/dist/vendor/serialize.js +25 -0
- package/dist/vendor/types.d.ts +69 -0
- package/dist/vendor/types.d.ts.map +1 -0
- package/dist/vendor/types.js +4 -0
- package/dist/workerd-facet-host.d.ts +207 -0
- package/dist/workerd-facet-host.d.ts.map +1 -0
- package/dist/workerd-facet-host.js +508 -0
- package/dist/ws-hibernation-config.d.ts +73 -0
- package/dist/ws-hibernation-config.d.ts.map +1 -0
- package/dist/ws-hibernation-config.js +93 -0
- package/package.json +62 -0
- package/src/alarms.ts +275 -0
- package/src/bindings.ts +871 -0
- package/src/ctx-exports.ts +77 -0
- package/src/facet-image-store.ts +196 -0
- package/src/fanout-pool.ts +503 -0
- package/src/index.ts +26 -0
- package/src/inner-do-registry.ts +58 -0
- package/src/launch-journal.ts +229 -0
- package/src/launch-pacer.ts +231 -0
- package/src/loader-ledger.ts +112 -0
- package/src/loader-pool.ts +984 -0
- package/src/process-fabric.ts +729 -0
- package/src/process-host.ts +566 -0
- package/src/vendor/errors.ts +56 -0
- package/src/vendor/serialize.ts +37 -0
- package/src/vendor/types.ts +75 -0
- package/src/workerd-facet-host.ts +694 -0
- package/src/ws-hibernation-config.ts +123 -0
package/dist/alarms.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* alarms.ts — Durable Object alarm multiplexing + isolate-generation
|
|
3
|
+
* machinery, persisted across hibernation.
|
|
4
|
+
*
|
|
5
|
+
* Workerd hibernates Durable Objects between requests to free memory. On
|
|
6
|
+
* wake, the new isolate must rebuild its in-memory state from SQL — but it
|
|
7
|
+
* also needs to know "is this the same lifecycle as before, or did workerd
|
|
8
|
+
* recycle me?" That distinction matters for recovery (warmJoin vs cold init)
|
|
9
|
+
* and is captured by the isolate generation, a counter persisted across
|
|
10
|
+
* hibernations.
|
|
11
|
+
*
|
|
12
|
+
* A Durable Object has ONE alarm, and a second `setAlarm()` silently
|
|
13
|
+
* overwrites the first — so every alarm-driven subsystem coordinates through
|
|
14
|
+
* a single reason→deadline map and one dispatcher. Reasons are plain strings
|
|
15
|
+
* registered by the embedder: `scheduleAlarm` arms one, and `dispatchAlarm`
|
|
16
|
+
* runs the embedder-supplied handler for every reason whose deadline has
|
|
17
|
+
* passed.
|
|
18
|
+
*/
|
|
19
|
+
import { errorText } from '@nimbus-sh/core/_shared/error-text.js';
|
|
20
|
+
/**
|
|
21
|
+
* Multi-reason alarm coordination map.
|
|
22
|
+
*
|
|
23
|
+
* JSON-serialised `Record<reason, deadlineMsEpoch>` where keys are the
|
|
24
|
+
* embedder's canonical reason strings (e.g. 'w9-flush', 'log-janitor'). The
|
|
25
|
+
* alarm() dispatcher reads this on fire, dispatches every reason whose
|
|
26
|
+
* deadline has passed, and re-arms `ctx.storage.setAlarm` at the earliest
|
|
27
|
+
* remaining deadline.
|
|
28
|
+
*
|
|
29
|
+
* Why a map (not a single nextAlarmAt + reason): two subsystems can have
|
|
30
|
+
* distinct deadlines. Without the map, the later setAlarm() call would
|
|
31
|
+
* overwrite the earlier reason silently, breaking whichever subsystem
|
|
32
|
+
* expected its deadline.
|
|
33
|
+
*
|
|
34
|
+
* Forward-compat: the dispatcher silently drops unknown reasons so a
|
|
35
|
+
* rollback from a future deploy that added new reasons doesn't leave the
|
|
36
|
+
* alarm stuck.
|
|
37
|
+
*
|
|
38
|
+
* The VALUE is live production DO storage ('w1_next_alarm_reasons', from the
|
|
39
|
+
* workstream that introduced it) and must never change — renaming a storage
|
|
40
|
+
* key is a migration, and orphaned rows are the least of what it breaks.
|
|
41
|
+
*/
|
|
42
|
+
export const ALARM_REASONS_KEY = 'w1_next_alarm_reasons';
|
|
43
|
+
/**
|
|
44
|
+
* Storage key for the isolate-generation counter (cold-start +
|
|
45
|
+
* post-hibernation wake; one increment per fresh isolate).
|
|
46
|
+
*
|
|
47
|
+
* The VALUE is live production DO storage ('w9_isolate_gen') and must never
|
|
48
|
+
* change, same contract as {@link ALARM_REASONS_KEY}.
|
|
49
|
+
*/
|
|
50
|
+
export const ISOLATE_GEN_KEY = 'w9_isolate_gen';
|
|
51
|
+
/**
|
|
52
|
+
* Schedule (or re-schedule) an alarm reason. Coordinated via a single map in
|
|
53
|
+
* DO storage so multiple subsystems don't clobber each other's `setAlarm()`
|
|
54
|
+
* calls.
|
|
55
|
+
*
|
|
56
|
+
* Semantics:
|
|
57
|
+
* - Reads the existing reasons map.
|
|
58
|
+
* - Sets `map[reason] = whenMs` IF `whenMs` is sooner than the
|
|
59
|
+
* currently-pending deadline for that reason (or no entry exists).
|
|
60
|
+
* Later-than-pending requests are silently ignored — the existing
|
|
61
|
+
* alarm will fire and re-arm anyway.
|
|
62
|
+
* - Writes the map back and calls `ctx.storage.setAlarm(min(deadlines))`.
|
|
63
|
+
*
|
|
64
|
+
* Cost: 1 storage read + 1 storage write + 1 setAlarm per call. setAlarm
|
|
65
|
+
* itself is billed as 1 row written per DO pricing. At a 60s janitor
|
|
66
|
+
* cadence, this is ~$0.05/mo/session at scale — dwarfed by the
|
|
67
|
+
* hibernation duration savings.
|
|
68
|
+
*
|
|
69
|
+
* Fail-soft: any throw is swallowed with a warn. On older runtimes /
|
|
70
|
+
* wrangler-dev where setAlarm is unavailable, this is a no-op (the
|
|
71
|
+
* subsystem's in-isolate setTimeout fallback continues to work).
|
|
72
|
+
*/
|
|
73
|
+
export function scheduleAlarm(host, ctx, reason, whenMs) {
|
|
74
|
+
// Serialize every read-modify-write of the reasons map through one
|
|
75
|
+
// per-instance chain: two schedulers firing back-to-back from one activity
|
|
76
|
+
// hook would otherwise interleave their get→put cycles and silently drop
|
|
77
|
+
// whichever reason wrote first.
|
|
78
|
+
const run = async () => {
|
|
79
|
+
try {
|
|
80
|
+
const setAlarmFn = ctx?.storage?.setAlarm;
|
|
81
|
+
if (typeof setAlarmFn !== 'function')
|
|
82
|
+
return false;
|
|
83
|
+
const existing = (await ctx.storage.get(ALARM_REASONS_KEY));
|
|
84
|
+
const map = { ...(existing || {}) };
|
|
85
|
+
// Earliest-deadline-first: only update if new request is sooner or
|
|
86
|
+
// this reason has no pending entry.
|
|
87
|
+
if (!(reason in map) || whenMs < map[reason]) {
|
|
88
|
+
map[reason] = whenMs;
|
|
89
|
+
await ctx.storage.put(ALARM_REASONS_KEY, map);
|
|
90
|
+
}
|
|
91
|
+
const earliest = Math.min(...Object.values(map));
|
|
92
|
+
setAlarmFn.call(ctx.storage, earliest);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.warn('[nimbus/W1] scheduleAlarm threw:', errorText(e));
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const chained = (host._alarmChain ?? Promise.resolve()).then(run, run);
|
|
101
|
+
host._alarmChain = chained;
|
|
102
|
+
return chained;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Multi-reason alarm dispatcher. Called from the DO's `alarm()` handler with
|
|
106
|
+
* the embedder's handler map.
|
|
107
|
+
*
|
|
108
|
+
* For each pending reason whose deadline has passed, run its handler.
|
|
109
|
+
* Handlers are awaited in place: the alarm invocation is the fresh turn a
|
|
110
|
+
* re-entering subsystem asked for, and it has to stay the one paying for the
|
|
111
|
+
* work it just released.
|
|
112
|
+
*
|
|
113
|
+
* After running fireable reasons, re-arms `ctx.storage.setAlarm` at the
|
|
114
|
+
* earliest remaining deadline. If no reasons remain, deletes the map key and
|
|
115
|
+
* does NOT call setAlarm — the DO becomes hibernation-eligible after the 10s
|
|
116
|
+
* idle window.
|
|
117
|
+
*
|
|
118
|
+
* Forward/back-compat: unknown reasons silently dropped. `onLegacyAlarm`
|
|
119
|
+
* covers an alarm that fires with no map at all — a deploy from before the
|
|
120
|
+
* map existed left a bare `setAlarm` behind, and the embedder decides what
|
|
121
|
+
* that one-time fire means (one dispatch later the map is populated by the
|
|
122
|
+
* next scheduleAlarm call).
|
|
123
|
+
*/
|
|
124
|
+
export function dispatchAlarm(host, ctx, handlers, onLegacyAlarm) {
|
|
125
|
+
// Same serialization as scheduleAlarm: the dispatcher's read→handlers→write
|
|
126
|
+
// cycle must not interleave with an activity-hook scheduleAlarm.
|
|
127
|
+
const chained = (host._alarmChain ?? Promise.resolve()).then(() => dispatchAlarmBody(ctx, handlers, onLegacyAlarm), () => dispatchAlarmBody(ctx, handlers, onLegacyAlarm));
|
|
128
|
+
host._alarmChain = chained;
|
|
129
|
+
return chained;
|
|
130
|
+
}
|
|
131
|
+
async function dispatchAlarmBody(ctx, handlers, onLegacyAlarm) {
|
|
132
|
+
try {
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const existing = (await ctx?.storage?.get?.(ALARM_REASONS_KEY));
|
|
135
|
+
if (!existing || Object.keys(existing).length === 0) {
|
|
136
|
+
onLegacyAlarm?.();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const map = { ...existing };
|
|
140
|
+
// Snapshot fireable reasons BEFORE running any of them, so a
|
|
141
|
+
// handler that schedules itself for the next cycle doesn't get
|
|
142
|
+
// immediately re-fired in the same dispatch.
|
|
143
|
+
const fired = [];
|
|
144
|
+
for (const [reason, when] of Object.entries(map)) {
|
|
145
|
+
if (when <= now)
|
|
146
|
+
fired.push(reason);
|
|
147
|
+
}
|
|
148
|
+
for (const reason of fired) {
|
|
149
|
+
delete map[reason];
|
|
150
|
+
const handler = handlers[reason];
|
|
151
|
+
// Unknown reasons silently dropped (forward-compat).
|
|
152
|
+
if (!handler)
|
|
153
|
+
continue;
|
|
154
|
+
try {
|
|
155
|
+
const result = await handler(now);
|
|
156
|
+
if (result && typeof result.rearmAt === 'number') {
|
|
157
|
+
map[reason] = result.rearmAt;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
console.warn(`[nimbus/W1] dispatch ${reason} threw:`, errorText(e));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Re-arm or clear.
|
|
165
|
+
const setAlarmFn = ctx?.storage?.setAlarm;
|
|
166
|
+
if (Object.keys(map).length > 0) {
|
|
167
|
+
await ctx.storage.put(ALARM_REASONS_KEY, map);
|
|
168
|
+
const earliest = Math.min(...Object.values(map));
|
|
169
|
+
if (typeof setAlarmFn === 'function') {
|
|
170
|
+
setAlarmFn.call(ctx.storage, earliest);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
try {
|
|
175
|
+
await ctx.storage.delete(ALARM_REASONS_KEY);
|
|
176
|
+
}
|
|
177
|
+
catch { }
|
|
178
|
+
// No remaining reasons → no setAlarm call → DO becomes
|
|
179
|
+
// hibernation-eligible after the 10s idle window.
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
console.warn('[nimbus/W1] dispatchAlarm threw:', errorText(e));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** Increment + persist the isolate-gen counter once per fresh isolate. */
|
|
187
|
+
export async function maybeBumpIsolateGen(host, ctx) {
|
|
188
|
+
if (host._isolateGenPersisted)
|
|
189
|
+
return;
|
|
190
|
+
host._isolateGenPersisted = true;
|
|
191
|
+
try {
|
|
192
|
+
const prev = (await ctx.storage.get(ISOLATE_GEN_KEY));
|
|
193
|
+
// Adopt the persisted truth first, and adopt the bump only after the
|
|
194
|
+
// put resolves. An unpersisted `next` would be re-read as `prev` by the
|
|
195
|
+
// NEXT boot and re-issued — two instances sharing one generation is
|
|
196
|
+
// exactly the pid-aliasing this counter exists to prevent. Running on
|
|
197
|
+
// the previous persisted generation is the lesser lapse, and the
|
|
198
|
+
// put-failure case is replica-only in practice (replicas never spawn).
|
|
199
|
+
//
|
|
200
|
+
// What holds the guarantee is the output gate, not this await: measured,
|
|
201
|
+
// the block body resolves in 0 ms even with a confirmed put, because
|
|
202
|
+
// `await storage.put()` returns before durability. The gate is what
|
|
203
|
+
// keeps a pid from generation N from escaping before N is durable, which
|
|
204
|
+
// is why marking this put `allowUnconfirmed` is not a free speedup — see
|
|
205
|
+
// scratchpad/coldstart-s1.md.
|
|
206
|
+
host._isolateGen = typeof prev === 'number' ? prev : 0;
|
|
207
|
+
const next = host._isolateGen + 1;
|
|
208
|
+
await ctx.storage.put(ISOLATE_GEN_KEY, next);
|
|
209
|
+
host._isolateGen = next;
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
console.warn('[nimbus/W9] isolate-gen bump failed:', errorText(e));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session/bindings.ts — Inner-Worker + assets binding shims (W10).
|
|
3
|
+
*
|
|
4
|
+
* `nimbus-wrangler dev` runs a USER worker as a child process. That
|
|
5
|
+
* child needs working `env` bindings (env.ASSETS, env.LOADER, env.MY_DO,
|
|
6
|
+
* etc.) but the DO's `env` belongs to the supervisor's contract — we
|
|
7
|
+
* can't pass it through directly. Workerd's enable_ctx_exports
|
|
8
|
+
* (compat date 2026-04-01+) auto-populates Service Bindings from
|
|
9
|
+
* top-level WorkerEntrypoint classes; these classes ARE those entry
|
|
10
|
+
* points. They forward each binding kind back to the supervisor DO via
|
|
11
|
+
* RPC stub (`env.NIMBUS_SESSION.idFromString(doId).get()...`).
|
|
12
|
+
*
|
|
13
|
+
* The shims have NO interaction with NimbusSession internals except
|
|
14
|
+
* through that RPC stub. Co-located here for grep-ability.
|
|
15
|
+
*
|
|
16
|
+
* NimbusAssetsRPC, NimbusLoaderRPC, NimbusLoadedWorker,
|
|
17
|
+
* NimbusLoadedEntrypoint, NimbusDurableObjectNamespace and NimbusDOStub are
|
|
18
|
+
* public API of the embedder's Worker: wrangler resolves them by class name
|
|
19
|
+
* and ctx.exports auto-populates them by export name, so the embedder's entry
|
|
20
|
+
* module re-exports them under exactly these names.
|
|
21
|
+
*
|
|
22
|
+
* Bundle-graph note: these classes must remain reachable from the embedder's
|
|
23
|
+
* entry module for Wrangler to bundle the WorkerEntrypoint exports.
|
|
24
|
+
*/
|
|
25
|
+
import { WorkerEntrypoint } from 'cloudflare:workers';
|
|
26
|
+
import { z } from 'zod/v4';
|
|
27
|
+
import type { WorkerCode } from './vendor/types.js';
|
|
28
|
+
/**
|
|
29
|
+
* The supervisor DO namespace a shim resolves ONE stub from, by the id its
|
|
30
|
+
* props carry. `Stub` is that DO's RPC surface as the calling shim uses it —
|
|
31
|
+
* the supervisor class belongs to the embedder, so each shim names the methods
|
|
32
|
+
* it calls rather than the class.
|
|
33
|
+
*/
|
|
34
|
+
interface SupervisorNamespace<Stub> {
|
|
35
|
+
idFromString(id: string): DurableObjectId;
|
|
36
|
+
get(id: DurableObjectId): Stub;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A dynamic worker's entrypoint, as hop 3 relays to it. `fetch` is the
|
|
40
|
+
* entrypoint contract every loaded worker answers; `handleHttpRequest` is the
|
|
41
|
+
* fabric's own route target, which only a facet that serves ports exposes.
|
|
42
|
+
*/
|
|
43
|
+
interface LoadedEntrypoint {
|
|
44
|
+
fetch(request: Request): Promise<Response>;
|
|
45
|
+
handleHttpRequest?(request: Request): Promise<Response>;
|
|
46
|
+
}
|
|
47
|
+
/** A stub for one dynamically-loaded worker, as the shims hop across it. */
|
|
48
|
+
interface LoadedWorker {
|
|
49
|
+
getEntrypoint(name?: string): LoadedEntrypoint;
|
|
50
|
+
getDurableObjectClass(name: string): DurableObjectClass;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The OUTER `env.LOADER` these shims forward to. `load` is the unkeyed arm the
|
|
54
|
+
* inner Worker asked for; `get` is the keyed arm every later hop re-enters in
|
|
55
|
+
* its own request context. `get`'s callback answers with whatever the caller
|
|
56
|
+
* assembled — a staged artifact's module map is the embedder's, so the fabric
|
|
57
|
+
* does not name it (see {@link ./process-fabric.js} StagedBootAssembler).
|
|
58
|
+
*/
|
|
59
|
+
interface OuterWorkerLoader {
|
|
60
|
+
load(code: WorkerCode): LoadedWorker;
|
|
61
|
+
get(id: string, getCode: () => Promise<object>): LoadedWorker;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `env` for the three Worker-Loader hops. The depth var rides the env rather
|
|
65
|
+
* than props because it is set on the OUTERMOST session and every nested
|
|
66
|
+
* Nimbus inherits it.
|
|
67
|
+
*/
|
|
68
|
+
interface NimbusLoaderShimEnv {
|
|
69
|
+
LOADER?: OuterWorkerLoader;
|
|
70
|
+
NIMBUS_INNER_LOADER_DEPTH?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* What the assets shim reads off the supervisor DO: the VFS bytes of one path,
|
|
74
|
+
* or null when it holds no such file.
|
|
75
|
+
*/
|
|
76
|
+
interface AssetsSupervisorStub {
|
|
77
|
+
_rpcReadFileBytes(path: string): Promise<ArrayBuffer | Uint8Array | null>;
|
|
78
|
+
}
|
|
79
|
+
/** `env` for the assets shim: the supervisor its VFS reads round-trip through. */
|
|
80
|
+
interface NimbusAssetsEnv {
|
|
81
|
+
NIMBUS_SESSION?: SupervisorNamespace<AssetsSupervisorStub>;
|
|
82
|
+
}
|
|
83
|
+
/** Props the assets shim is minted with. */
|
|
84
|
+
interface NimbusAssetsProps {
|
|
85
|
+
/** Project root in VFS (e.g. "home/user/myapp"). */
|
|
86
|
+
vfsRoot?: string;
|
|
87
|
+
/** Directory declared in wrangler.jsonc.assets.directory. */
|
|
88
|
+
assetsDir?: string;
|
|
89
|
+
/** Supervisor DO id whose VFS holds the assets. */
|
|
90
|
+
doId?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Assets binding shim. The inner Worker calls `env.ASSETS.fetch(request)`
|
|
94
|
+
* and we serve the file from VFS under `<vfsRoot>/<assetsDir>/<pathname>`.
|
|
95
|
+
*
|
|
96
|
+
* Props (passed via ctx.props when this binding is constructed):
|
|
97
|
+
* vfsRoot — project root in VFS (e.g. "home/user/myapp")
|
|
98
|
+
* assetsDir — directory declared in wrangler.jsonc.assets.directory
|
|
99
|
+
* (e.g. "./public" → we trim the leading ./)
|
|
100
|
+
*
|
|
101
|
+
* The hostname on the incoming Request is irrelevant (Workers Assets
|
|
102
|
+
* convention); only pathname matters. Path traversal (`..`) is clamped.
|
|
103
|
+
* Directories resolve to their `index.html` child; missing files fall
|
|
104
|
+
* back to the assetsDir root `index.html` (SPA convention), then 404.
|
|
105
|
+
*
|
|
106
|
+
* The VFS is read from the supervisor DO via the class property
|
|
107
|
+
* `_nimbusVfsResolver` set by NimbusSession at construction. WorkerEntrypoint
|
|
108
|
+
* instances don't have direct access to the supervisor's SqliteVFS, so we
|
|
109
|
+
* reach it through the supervisor stub (env.NIMBUS_SESSION.idFromString).
|
|
110
|
+
* For Phase 1, we use a simpler approach: the props carry a supervisor
|
|
111
|
+
* DO id so we can round-trip through an RPC method that reads the file.
|
|
112
|
+
*/
|
|
113
|
+
export declare class NimbusAssetsRPC extends WorkerEntrypoint<NimbusAssetsEnv, NimbusAssetsProps> {
|
|
114
|
+
/**
|
|
115
|
+
* Fetch a static asset. Called by the inner Worker as
|
|
116
|
+
* `env.ASSETS.fetch(request)`. The request URL's pathname is used to
|
|
117
|
+
* resolve a file under the configured assets directory.
|
|
118
|
+
*/
|
|
119
|
+
fetch(request: Request): Promise<Response>;
|
|
120
|
+
}
|
|
121
|
+
declare const NimbusLoadedEntrypointPropsSchema: z.ZodObject<{
|
|
122
|
+
key: z.ZodString;
|
|
123
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
124
|
+
depth: z.ZodOptional<z.ZodNumber>;
|
|
125
|
+
supervisor: z.ZodOptional<z.ZodObject<{
|
|
126
|
+
doId: z.ZodString;
|
|
127
|
+
pid: z.ZodNumber;
|
|
128
|
+
writerId: z.ZodString;
|
|
129
|
+
}, z.core.$strip>>;
|
|
130
|
+
stage: z.ZodOptional<z.ZodUnknown>;
|
|
131
|
+
}, z.core.$loose>;
|
|
132
|
+
type NimbusLoadedEntrypointProps = z.infer<typeof NimbusLoadedEntrypointPropsSchema>;
|
|
133
|
+
/**
|
|
134
|
+
* Diagnostic surface for /api/_diag/memory. Returns a snapshot of
|
|
135
|
+
* the Map state — entry count, configured cap, eviction counter
|
|
136
|
+
* since isolate boot. Pure read; no I/O.
|
|
137
|
+
*/
|
|
138
|
+
export declare function getLoadedCodesStats(): {
|
|
139
|
+
entries: number;
|
|
140
|
+
maxEntries: number;
|
|
141
|
+
evictions: number;
|
|
142
|
+
};
|
|
143
|
+
/** Props every Worker-Loader hop carries: how deep this Nimbus already is. */
|
|
144
|
+
interface NimbusLoaderDepthProps {
|
|
145
|
+
depth?: number;
|
|
146
|
+
}
|
|
147
|
+
/** Hop 1: env.LOADER.{load,get} forwarded to the outer loader. */
|
|
148
|
+
export declare class NimbusLoaderRPC extends WorkerEntrypoint<NimbusLoaderShimEnv, NimbusLoaderDepthProps> {
|
|
149
|
+
private _currentDepth;
|
|
150
|
+
private _maxDepth;
|
|
151
|
+
private _assertDepthOk;
|
|
152
|
+
/**
|
|
153
|
+
* Inner: env.LOADER.load(code). Stashes the CODE (not a stub — stubs
|
|
154
|
+
* are I/O-bound to the calling request context) and returns a
|
|
155
|
+
* NimbusLoadedWorker RPC stub. Each downstream call re-loads the
|
|
156
|
+
* worker in its own request context via LOADER.get(key, cb).
|
|
157
|
+
*/
|
|
158
|
+
load(code: WorkerCode): unknown;
|
|
159
|
+
/**
|
|
160
|
+
* Inner: env.LOADER.get(id, callback). The inner's callback returns
|
|
161
|
+
* a code object; we treat `id` as the outer cache key (prefixed so
|
|
162
|
+
* it doesn't collide with load()-generated keys).
|
|
163
|
+
*/
|
|
164
|
+
get(id: string, callback: () => WorkerCode | Promise<WorkerCode>): Promise<unknown>;
|
|
165
|
+
}
|
|
166
|
+
/** Props hop 2 carries: the stashed code it re-loads, and its inherited depth. */
|
|
167
|
+
interface NimbusLoadedWorkerProps extends NimbusLoaderDepthProps {
|
|
168
|
+
key?: string;
|
|
169
|
+
}
|
|
170
|
+
/** Hop 2: the returned "worker" stub. Exposes .getEntrypoint(). */
|
|
171
|
+
export declare class NimbusLoadedWorker extends WorkerEntrypoint<NimbusLoaderShimEnv, NimbusLoadedWorkerProps> {
|
|
172
|
+
/**
|
|
173
|
+
* Returns a NimbusLoadedEntrypoint stub that carries the code key +
|
|
174
|
+
* entrypoint name forward. The actual outer-side load + fetch happens
|
|
175
|
+
* inside NimbusLoadedEntrypoint.fetch() so all outer hops run in a
|
|
176
|
+
* SINGLE outer request context (the cross-request-I/O limitation is
|
|
177
|
+
* real — stubs created in one outer request can't be used by another).
|
|
178
|
+
*/
|
|
179
|
+
getEntrypoint(name?: string): unknown;
|
|
180
|
+
/**
|
|
181
|
+
* Pass-through to outer worker.getDurableObjectClass(name). The
|
|
182
|
+
* returned stub is tied to THIS method's outer request context; if
|
|
183
|
+
* the caller (the inner worker) uses the class in a later request
|
|
184
|
+
* it will fail the cross-request-I/O check. For Phase 3 DO binding
|
|
185
|
+
* synthesis we resolve classes directly from nimbus-wrangler's own
|
|
186
|
+
* request context (which is the build-time context), not through
|
|
187
|
+
* this method.
|
|
188
|
+
*/
|
|
189
|
+
getDurableObjectClass(name: string): DurableObjectClass;
|
|
190
|
+
}
|
|
191
|
+
/** Hop 3: a named-or-default entrypoint. Exposes .fetch(). */
|
|
192
|
+
export declare class NimbusLoadedEntrypoint extends WorkerEntrypoint<NimbusLoaderShimEnv, NimbusLoadedEntrypointProps> {
|
|
193
|
+
_props(): NimbusLoadedEntrypointProps;
|
|
194
|
+
_supervisorBinding(props: NimbusLoadedEntrypointProps): Promise<unknown>;
|
|
195
|
+
_resolveEntrypoint(): Promise<LoadedEntrypoint>;
|
|
196
|
+
/**
|
|
197
|
+
* Relay the inner entrypoint's Response to the caller with a LIVE body.
|
|
198
|
+
* The body streams through an identity pipe and the entrypoint stub is
|
|
199
|
+
* disposed only once the body finishes — materializing (arrayBuffer) here
|
|
200
|
+
* buffered every routed response to stream-end, which froze SSE/chunked
|
|
201
|
+
* bodies (an agent server's /event live-sync, `curl -N` loopback, external
|
|
202
|
+
* preview) until the facet closed the stream.
|
|
203
|
+
*/
|
|
204
|
+
private _relayNestedRpcResponse;
|
|
205
|
+
/**
|
|
206
|
+
* Invoke the facet's HTTP handler.
|
|
207
|
+
*
|
|
208
|
+
* The call must be written as `ep.method(request)`. An RPC stub's method is a
|
|
209
|
+
* JsRpcProperty, whose every property access is a WILDCARD that extends a
|
|
210
|
+
* pipelined path (`JSG_WILDCARD_PROPERTY`, workerd api/worker-rpc.h) — so
|
|
211
|
+
* `method.call(ep, request)` does NOT reach Function.prototype.call. It builds
|
|
212
|
+
* the path `handleHttpRequest.call` and invokes it remotely with `ep` as its
|
|
213
|
+
* first ARGUMENT. Serializing `ep` — an entrypoint to a dynamically-loaded
|
|
214
|
+
* worker — is what workerd refuses:
|
|
215
|
+
*
|
|
216
|
+
* DataCloneError: Entrypoints to dynamically-loaded workers cannot be
|
|
217
|
+
* transferred to other Workers
|
|
218
|
+
*
|
|
219
|
+
* (server.c++ `requireAllowsTransfer` → `throwDynamicEntrypointTransferError`).
|
|
220
|
+
* The facet is never entered, because the failure is in serializing the
|
|
221
|
+
* arguments, before the call is delivered.
|
|
222
|
+
*/
|
|
223
|
+
private _callHttpHandler;
|
|
224
|
+
handleHttpRequest(request: Request): Promise<Response>;
|
|
225
|
+
/**
|
|
226
|
+
* Forward fetch() to the outer worker's entrypoint. All three outer
|
|
227
|
+
* hops (load → getEntrypoint → fetch) run in the same outer request
|
|
228
|
+
* context (this method's invocation), which sidesteps the
|
|
229
|
+
* cross-request-I/O limitation.
|
|
230
|
+
*/
|
|
231
|
+
fetch(request: Request): Promise<Response>;
|
|
232
|
+
}
|
|
233
|
+
/** Props the synthesized namespace carries: which binding, on which supervisor. */
|
|
234
|
+
interface NimbusDoNamespaceProps {
|
|
235
|
+
bindingName?: string;
|
|
236
|
+
supervisorDoId?: string;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* `env.MY_DO` shim — a DurableObjectNamespace-like WorkerEntrypoint.
|
|
240
|
+
*
|
|
241
|
+
* Usage from inner Worker:
|
|
242
|
+
* const id = await env.MY_DO.idFromName('x'); // AWAIT required
|
|
243
|
+
* const stub = env.MY_DO.get(id);
|
|
244
|
+
* await stub.fetch(request);
|
|
245
|
+
*
|
|
246
|
+
* IMPORTANT: unlike the real DurableObjectNamespace, idFromName /
|
|
247
|
+
* newUniqueId / idFromString here return **Promises**, because they're
|
|
248
|
+
* RPC-backed WorkerEntrypoint methods. The inner caller MUST `await`
|
|
249
|
+
* them before passing the result to `.get()`. Workers RPC pipelining
|
|
250
|
+
* does not currently allow passing an RpcPromise as a method argument
|
|
251
|
+
* — the no-await form fails with:
|
|
252
|
+
* "Could not serialize object of type \"RpcPromise\"."
|
|
253
|
+
*
|
|
254
|
+
* Typical real-Worker code written for Cloudflare's synchronous
|
|
255
|
+
* DurableObjectNamespace needs a one-word change (add `await`).
|
|
256
|
+
*
|
|
257
|
+
* idFromName produces prefix `name:` (deterministic FNV-style hash);
|
|
258
|
+
* newUniqueId uses `uniq:` (random). The prefixes keep the two id
|
|
259
|
+
* spaces distinct so a name-derived id can't collide with a random
|
|
260
|
+
* one.
|
|
261
|
+
*/
|
|
262
|
+
export declare class NimbusDurableObjectNamespace extends WorkerEntrypoint<unknown, NimbusDoNamespaceProps> {
|
|
263
|
+
/** Stable string id derived from a name. Hash is deterministic. */
|
|
264
|
+
idFromName(name: string): string;
|
|
265
|
+
/** Fresh random id (matches DurableObjectNamespace.newUniqueId()). */
|
|
266
|
+
newUniqueId(): string;
|
|
267
|
+
/** Accept-through for an already-formatted id. */
|
|
268
|
+
idFromString(s: string): string;
|
|
269
|
+
/** Return a stub bound to the given id. */
|
|
270
|
+
get(id: string): unknown;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* What the DO shim reads off the supervisor: one inner-DO request, answered
|
|
274
|
+
* from the facet the supervisor resolves in its own request context.
|
|
275
|
+
*/
|
|
276
|
+
interface InnerDoSupervisorStub {
|
|
277
|
+
_rpcInnerDoFetch(request: {
|
|
278
|
+
bindingName: string;
|
|
279
|
+
id: string;
|
|
280
|
+
method: string;
|
|
281
|
+
url: string;
|
|
282
|
+
headers: [string, string][];
|
|
283
|
+
body: ArrayBuffer | null;
|
|
284
|
+
}): Promise<{
|
|
285
|
+
body: ArrayBuffer;
|
|
286
|
+
status: number;
|
|
287
|
+
statusText: string;
|
|
288
|
+
headers: [string, string][];
|
|
289
|
+
}>;
|
|
290
|
+
}
|
|
291
|
+
/** `env` for the DO shim: the supervisor that owns the facet. */
|
|
292
|
+
interface NimbusInnerDoEnv {
|
|
293
|
+
NIMBUS_SESSION?: SupervisorNamespace<InnerDoSupervisorStub>;
|
|
294
|
+
}
|
|
295
|
+
/** Props the DO stub carries: which binding, which supervisor, which id. */
|
|
296
|
+
interface NimbusDoStubProps extends NimbusDoNamespaceProps {
|
|
297
|
+
id?: string;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* A Durable-Object-namespace-stub for a specific id. Exposes fetch()
|
|
301
|
+
* and will, if we later need it, forward RPC method calls through a
|
|
302
|
+
* dispatch helper. The important invariant: EVERY call resolves the
|
|
303
|
+
* inner DO class via getInnerDoClass() (./inner-do-registry.js) and
|
|
304
|
+
* spins up / attaches to a facet via the supervisor's ctx.facets in
|
|
305
|
+
* the SAME outer request context — never reusing stubs across requests.
|
|
306
|
+
*/
|
|
307
|
+
export declare class NimbusDOStub extends WorkerEntrypoint<NimbusInnerDoEnv, NimbusDoStubProps> {
|
|
308
|
+
/**
|
|
309
|
+
* Resolve the supervisor DO from env.NIMBUS_SESSION and route through
|
|
310
|
+
* its _rpcInnerDoFetch RPC method, which runs ctx.facets.get(...) in
|
|
311
|
+
* its own context and forwards the request.
|
|
312
|
+
*/
|
|
313
|
+
fetch(request: Request): Promise<Response>;
|
|
314
|
+
}
|
|
315
|
+
export {};
|
|
316
|
+
//# sourceMappingURL=bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../src/bindings.ts"],"names":[],"mappings":"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"}
|