@nimbus-sh/fabric 0.1.0 → 0.3.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 +208 -293
- package/dist/bindings.js +5 -5
- package/dist/budgets.d.ts +132 -0
- package/dist/budgets.d.ts.map +1 -0
- package/dist/budgets.js +248 -0
- package/dist/composition.d.ts +3 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +2 -0
- package/dist/connections.d.ts +81 -0
- package/dist/connections.d.ts.map +1 -0
- package/dist/connections.js +114 -0
- package/dist/derived.d.ts +65 -0
- package/dist/derived.d.ts.map +1 -0
- package/dist/derived.js +95 -0
- package/dist/do-calls.d.ts +94 -0
- package/dist/do-calls.d.ts.map +1 -0
- package/dist/do-calls.js +111 -0
- package/dist/facet-pool.d.ts +90 -0
- package/dist/facet-pool.d.ts.map +1 -0
- package/dist/facet-pool.js +113 -0
- package/dist/{fanout-pool.d.ts → fanout.d.ts} +20 -20
- package/dist/fanout.d.ts.map +1 -0
- package/dist/{fanout-pool.js → fanout.js} +20 -20
- package/dist/{launch-journal.d.ts → fenced-work.d.ts} +58 -17
- package/dist/fenced-work.d.ts.map +1 -0
- package/dist/fenced-work.js +241 -0
- package/dist/generation.d.ts +69 -0
- package/dist/generation.d.ts.map +1 -0
- package/dist/generation.js +118 -0
- package/dist/{facet-image-store.d.ts → image-store.d.ts} +8 -8
- package/dist/image-store.d.ts.map +1 -0
- package/dist/{facet-image-store.js → image-store.js} +4 -4
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -8
- package/dist/{loader-pool.d.ts → isolate-pool.d.ts} +19 -19
- package/dist/isolate-pool.d.ts.map +1 -0
- package/dist/{loader-pool.js → isolate-pool.js} +20 -20
- package/dist/journal.d.ts +111 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +177 -0
- package/dist/outbox.d.ts +249 -0
- package/dist/outbox.d.ts.map +1 -0
- package/dist/outbox.js +355 -0
- package/dist/process-fabric.d.ts +33 -15
- package/dist/process-fabric.d.ts.map +1 -1
- package/dist/process-fabric.js +25 -15
- package/dist/process-host.d.ts +1 -1
- package/dist/process-host.d.ts.map +1 -1
- package/dist/process-host.js +19 -11
- package/dist/sealed.d.ts +78 -0
- package/dist/sealed.d.ts.map +1 -0
- package/dist/sealed.js +145 -0
- package/dist/timers.d.ts +138 -0
- package/dist/timers.d.ts.map +1 -0
- package/dist/timers.js +231 -0
- package/dist/{launch-pacer.d.ts → turn-budget.d.ts} +24 -21
- package/dist/turn-budget.d.ts.map +1 -0
- package/dist/{launch-pacer.js → turn-budget.js} +24 -12
- package/dist/workerd-facet-host.d.ts +67 -70
- package/dist/workerd-facet-host.d.ts.map +1 -1
- package/dist/workerd-facet-host.js +129 -181
- package/examples/agent-core-adapter.ts +191 -0
- package/package.json +4 -2
- package/src/bindings.ts +6 -6
- package/src/budgets.ts +308 -0
- package/src/composition.ts +16 -0
- package/src/connections.ts +140 -0
- package/src/derived.ts +135 -0
- package/src/do-calls.ts +156 -0
- package/src/facet-pool.ts +157 -0
- package/src/{fanout-pool.ts → fanout.ts} +35 -35
- package/src/{launch-journal.ts → fenced-work.ts} +129 -42
- package/src/generation.ts +144 -0
- package/src/{facet-image-store.ts → image-store.ts} +9 -9
- package/src/index.ts +16 -8
- package/src/{loader-pool.ts → isolate-pool.ts} +34 -34
- package/src/journal.ts +242 -0
- package/src/node-async-hooks.d.ts +14 -0
- package/src/outbox.ts +520 -0
- package/src/process-fabric.ts +43 -34
- package/src/process-host.ts +22 -20
- package/src/sealed.ts +150 -0
- package/src/timers.ts +294 -0
- package/src/{launch-pacer.ts → turn-budget.ts} +34 -27
- package/src/workerd-facet-host.ts +159 -208
- package/dist/alarms.d.ts +0 -134
- package/dist/alarms.d.ts.map +0 -1
- package/dist/alarms.js +0 -214
- package/dist/ctx-exports.d.ts +0 -47
- package/dist/ctx-exports.d.ts.map +0 -1
- package/dist/ctx-exports.js +0 -54
- package/dist/facet-image-store.d.ts.map +0 -1
- package/dist/fanout-pool.d.ts.map +0 -1
- package/dist/launch-journal.d.ts.map +0 -1
- package/dist/launch-journal.js +0 -154
- package/dist/launch-pacer.d.ts.map +0 -1
- package/dist/loader-ledger.d.ts +0 -57
- package/dist/loader-ledger.d.ts.map +0 -1
- package/dist/loader-ledger.js +0 -91
- package/dist/loader-pool.d.ts.map +0 -1
- package/src/alarms.ts +0 -275
- package/src/ctx-exports.ts +0 -77
- package/src/loader-ledger.ts +0 -112
package/dist/alarms.d.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
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
|
-
/**
|
|
20
|
-
* The storage the alarm map lives in. `setAlarm` is optional because
|
|
21
|
-
* `wrangler dev` serves a storage without it, which is the whole reason
|
|
22
|
-
* scheduling degrades to a no-op instead of throwing.
|
|
23
|
-
*/
|
|
24
|
-
export interface AlarmStorage {
|
|
25
|
-
get(key: string): Promise<unknown>;
|
|
26
|
-
put(key: string, value: unknown): Promise<void>;
|
|
27
|
-
delete(key: string): Promise<boolean>;
|
|
28
|
-
setAlarm?(scheduledTime: number): Promise<void>;
|
|
29
|
-
}
|
|
30
|
-
/** The hosting actor's context, as the alarm coordination reads it. */
|
|
31
|
-
export interface AlarmContext {
|
|
32
|
-
storage: AlarmStorage;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Multi-reason alarm coordination map.
|
|
36
|
-
*
|
|
37
|
-
* JSON-serialised `Record<reason, deadlineMsEpoch>` where keys are the
|
|
38
|
-
* embedder's canonical reason strings (e.g. 'w9-flush', 'log-janitor'). The
|
|
39
|
-
* alarm() dispatcher reads this on fire, dispatches every reason whose
|
|
40
|
-
* deadline has passed, and re-arms `ctx.storage.setAlarm` at the earliest
|
|
41
|
-
* remaining deadline.
|
|
42
|
-
*
|
|
43
|
-
* Why a map (not a single nextAlarmAt + reason): two subsystems can have
|
|
44
|
-
* distinct deadlines. Without the map, the later setAlarm() call would
|
|
45
|
-
* overwrite the earlier reason silently, breaking whichever subsystem
|
|
46
|
-
* expected its deadline.
|
|
47
|
-
*
|
|
48
|
-
* Forward-compat: the dispatcher silently drops unknown reasons so a
|
|
49
|
-
* rollback from a future deploy that added new reasons doesn't leave the
|
|
50
|
-
* alarm stuck.
|
|
51
|
-
*
|
|
52
|
-
* The VALUE is live production DO storage ('w1_next_alarm_reasons', from the
|
|
53
|
-
* workstream that introduced it) and must never change — renaming a storage
|
|
54
|
-
* key is a migration, and orphaned rows are the least of what it breaks.
|
|
55
|
-
*/
|
|
56
|
-
export declare const ALARM_REASONS_KEY = "w1_next_alarm_reasons";
|
|
57
|
-
/**
|
|
58
|
-
* Storage key for the isolate-generation counter (cold-start +
|
|
59
|
-
* post-hibernation wake; one increment per fresh isolate).
|
|
60
|
-
*
|
|
61
|
-
* The VALUE is live production DO storage ('w9_isolate_gen') and must never
|
|
62
|
-
* change, same contract as {@link ALARM_REASONS_KEY}.
|
|
63
|
-
*/
|
|
64
|
-
export declare const ISOLATE_GEN_KEY = "w9_isolate_gen";
|
|
65
|
-
/**
|
|
66
|
-
* The host instance carrying the per-instance alarm chain. The field lives on
|
|
67
|
-
* the embedder's DO instance so one chain serializes every alarm-map
|
|
68
|
-
* read-modify-write for that instance (see {@link scheduleAlarm}).
|
|
69
|
-
*/
|
|
70
|
-
export interface AlarmHost {
|
|
71
|
-
_alarmChain?: Promise<unknown>;
|
|
72
|
-
}
|
|
73
|
-
/** The host instance carrying the isolate-generation state. */
|
|
74
|
-
export interface IsolateGenHost {
|
|
75
|
-
_isolateGen: number;
|
|
76
|
-
_isolateGenPersisted: boolean;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Schedule (or re-schedule) an alarm reason. Coordinated via a single map in
|
|
80
|
-
* DO storage so multiple subsystems don't clobber each other's `setAlarm()`
|
|
81
|
-
* calls.
|
|
82
|
-
*
|
|
83
|
-
* Semantics:
|
|
84
|
-
* - Reads the existing reasons map.
|
|
85
|
-
* - Sets `map[reason] = whenMs` IF `whenMs` is sooner than the
|
|
86
|
-
* currently-pending deadline for that reason (or no entry exists).
|
|
87
|
-
* Later-than-pending requests are silently ignored — the existing
|
|
88
|
-
* alarm will fire and re-arm anyway.
|
|
89
|
-
* - Writes the map back and calls `ctx.storage.setAlarm(min(deadlines))`.
|
|
90
|
-
*
|
|
91
|
-
* Cost: 1 storage read + 1 storage write + 1 setAlarm per call. setAlarm
|
|
92
|
-
* itself is billed as 1 row written per DO pricing. At a 60s janitor
|
|
93
|
-
* cadence, this is ~$0.05/mo/session at scale — dwarfed by the
|
|
94
|
-
* hibernation duration savings.
|
|
95
|
-
*
|
|
96
|
-
* Fail-soft: any throw is swallowed with a warn. On older runtimes /
|
|
97
|
-
* wrangler-dev where setAlarm is unavailable, this is a no-op (the
|
|
98
|
-
* subsystem's in-isolate setTimeout fallback continues to work).
|
|
99
|
-
*/
|
|
100
|
-
export declare function scheduleAlarm(host: AlarmHost, ctx: AlarmContext, reason: string, whenMs: number): Promise<boolean>;
|
|
101
|
-
/**
|
|
102
|
-
* What one alarm handler may return: nothing, or a deadline this reason
|
|
103
|
-
* re-arms itself at. Re-arming through the return value keeps the map's
|
|
104
|
-
* read-modify-write inside the dispatcher, where it is serialized.
|
|
105
|
-
*/
|
|
106
|
-
export type AlarmHandlerResult = void | {
|
|
107
|
-
rearmAt: number;
|
|
108
|
-
};
|
|
109
|
-
/** The embedder's reasons, each with the handler that answers it. */
|
|
110
|
-
export type AlarmHandlers = Record<string, (now: number) => AlarmHandlerResult | Promise<AlarmHandlerResult>>;
|
|
111
|
-
/**
|
|
112
|
-
* Multi-reason alarm dispatcher. Called from the DO's `alarm()` handler with
|
|
113
|
-
* the embedder's handler map.
|
|
114
|
-
*
|
|
115
|
-
* For each pending reason whose deadline has passed, run its handler.
|
|
116
|
-
* Handlers are awaited in place: the alarm invocation is the fresh turn a
|
|
117
|
-
* re-entering subsystem asked for, and it has to stay the one paying for the
|
|
118
|
-
* work it just released.
|
|
119
|
-
*
|
|
120
|
-
* After running fireable reasons, re-arms `ctx.storage.setAlarm` at the
|
|
121
|
-
* earliest remaining deadline. If no reasons remain, deletes the map key and
|
|
122
|
-
* does NOT call setAlarm — the DO becomes hibernation-eligible after the 10s
|
|
123
|
-
* idle window.
|
|
124
|
-
*
|
|
125
|
-
* Forward/back-compat: unknown reasons silently dropped. `onLegacyAlarm`
|
|
126
|
-
* covers an alarm that fires with no map at all — a deploy from before the
|
|
127
|
-
* map existed left a bare `setAlarm` behind, and the embedder decides what
|
|
128
|
-
* that one-time fire means (one dispatch later the map is populated by the
|
|
129
|
-
* next scheduleAlarm call).
|
|
130
|
-
*/
|
|
131
|
-
export declare function dispatchAlarm(host: AlarmHost, ctx: AlarmContext, handlers: AlarmHandlers, onLegacyAlarm?: () => void): Promise<void>;
|
|
132
|
-
/** Increment + persist the isolate-gen counter once per fresh isolate. */
|
|
133
|
-
export declare function maybeBumpIsolateGen(host: IsolateGenHost, ctx: AlarmContext): Promise<void>;
|
|
134
|
-
//# sourceMappingURL=alarms.d.ts.map
|
package/dist/alarms.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"alarms.d.ts","sourceRoot":"","sources":["../src/alarms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAChC;AAED,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA8BlB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D,qEAAqE;AACrE,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,MAAM,EACN,CAAC,GAAG,EAAE,MAAM,KAAK,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAClE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,aAAa,EACvB,aAAa,CAAC,EAAE,MAAM,IAAI,GACzB,OAAO,CAAC,IAAI,CAAC,CASf;AAwDD,0EAA0E;AAC1E,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBhG"}
|
package/dist/alarms.js
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/ctx-exports.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ctx-exports.ts — leaf module holding the ctx.exports reference.
|
|
3
|
-
*
|
|
4
|
-
* Isolated from the embedder's entry module so helpers (notably
|
|
5
|
-
* loader-pool.ts) can read `ctx.exports` without transitively importing the
|
|
6
|
-
* Durable Object classes. Keeping this a leaf (no imports) lets the pool be
|
|
7
|
-
* unit-tested in a plain Node/Bun process.
|
|
8
|
-
*
|
|
9
|
-
* The embedder's fetch handler calls `setCtxExports(ctx.exports)` on the
|
|
10
|
-
* first request; callers like the loader pool read via `getCtxExports()`.
|
|
11
|
-
* If the pool is constructed before the first fetch (unlikely) it just gets
|
|
12
|
-
* null — the caller decides how to degrade.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* One entry of `ctx.exports`: a top-level entrypoint's loopback factory, which
|
|
16
|
-
* mints a Service Binding stub for that entrypoint when called with props.
|
|
17
|
-
*
|
|
18
|
-
* The stub's RPC surface belongs to the entrypoint CLASS, which this leaf
|
|
19
|
-
* cannot see — `Cloudflare.Exports` is derived from the embedder's own main
|
|
20
|
-
* module, so for a library it evaluates to `{}`. A caller that knows the class
|
|
21
|
-
* names the surface it expects (`factory<MySupervisorRpc>({ props })`); one
|
|
22
|
-
* that does not gets `unknown` and has to narrow, same as
|
|
23
|
-
* `DurableObjectNamespace<T>` and `RpcStub<T>` in @cloudflare/workers-types.
|
|
24
|
-
*/
|
|
25
|
-
export type EntrypointLoopbackFactory = <Stub = unknown>(options: {
|
|
26
|
-
props: object;
|
|
27
|
-
}) => Stub;
|
|
28
|
-
/**
|
|
29
|
-
* `ctx.exports` itself — one factory per top-level entrypoint export, keyed by
|
|
30
|
-
* export name. Absent names read as undefined, which is how a caller finds out
|
|
31
|
-
* the embedder's entry module does not re-export the class it needs.
|
|
32
|
-
*/
|
|
33
|
-
export type CtxExports = Record<string, EntrypointLoopbackFactory | undefined>;
|
|
34
|
-
export declare function setCtxExports(value: CtxExports): void;
|
|
35
|
-
export declare function getCtxExports(): CtxExports | null;
|
|
36
|
-
export declare function setSupervisorEntrypointName(name: string): void;
|
|
37
|
-
/**
|
|
38
|
-
* Resolve the registered supervisor entrypoint on an exports object —
|
|
39
|
-
* `exportsObj` when given (a WorkerEntrypoint reads its own ctx.exports),
|
|
40
|
-
* the held ctx.exports otherwise. Calling the result with props mints one
|
|
41
|
-
* supervisor binding (`env.SUPERVISOR`) for one hosted program. Null when
|
|
42
|
-
* either half is missing; the caller decides whether that degrades or throws.
|
|
43
|
-
*/
|
|
44
|
-
export declare function supervisorEntrypoint(exportsObj?: unknown): EntrypointLoopbackFactory | null;
|
|
45
|
-
/** The registered name, for error messages that point at the missing export. */
|
|
46
|
-
export declare function supervisorEntrypointName(): string | null;
|
|
47
|
-
//# sourceMappingURL=ctx-exports.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ctx-exports.d.ts","sourceRoot":"","sources":["../src/ctx-exports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC;AAE7F;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC;AAI/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAGrD;AAED,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD;AAUD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAG9D;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,yBAAyB,GAAG,IAAI,CAM3F;AAED,gFAAgF;AAChF,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAExD"}
|
package/dist/ctx-exports.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ctx-exports.ts — leaf module holding the ctx.exports reference.
|
|
3
|
-
*
|
|
4
|
-
* Isolated from the embedder's entry module so helpers (notably
|
|
5
|
-
* loader-pool.ts) can read `ctx.exports` without transitively importing the
|
|
6
|
-
* Durable Object classes. Keeping this a leaf (no imports) lets the pool be
|
|
7
|
-
* unit-tested in a plain Node/Bun process.
|
|
8
|
-
*
|
|
9
|
-
* The embedder's fetch handler calls `setCtxExports(ctx.exports)` on the
|
|
10
|
-
* first request; callers like the loader pool read via `getCtxExports()`.
|
|
11
|
-
* If the pool is constructed before the first fetch (unlikely) it just gets
|
|
12
|
-
* null — the caller decides how to degrade.
|
|
13
|
-
*/
|
|
14
|
-
let _ctxExports = null;
|
|
15
|
-
export function setCtxExports(value) {
|
|
16
|
-
if (_ctxExports)
|
|
17
|
-
return; // first-write-wins, same as the prior inline impl
|
|
18
|
-
_ctxExports = value;
|
|
19
|
-
}
|
|
20
|
-
export function getCtxExports() {
|
|
21
|
-
return _ctxExports;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* The fabric mints supervisor bindings for the programs it hosts, but the
|
|
25
|
-
* entrypoint class that answers them belongs to the embedder, so its
|
|
26
|
-
* ctx.exports name is registered once at composition time rather than
|
|
27
|
-
* hardcoded here. First-write-wins, same as the ctx.exports holder above.
|
|
28
|
-
*/
|
|
29
|
-
let _supervisorEntrypointName = null;
|
|
30
|
-
export function setSupervisorEntrypointName(name) {
|
|
31
|
-
if (_supervisorEntrypointName)
|
|
32
|
-
return;
|
|
33
|
-
_supervisorEntrypointName = name;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Resolve the registered supervisor entrypoint on an exports object —
|
|
37
|
-
* `exportsObj` when given (a WorkerEntrypoint reads its own ctx.exports),
|
|
38
|
-
* the held ctx.exports otherwise. Calling the result with props mints one
|
|
39
|
-
* supervisor binding (`env.SUPERVISOR`) for one hosted program. Null when
|
|
40
|
-
* either half is missing; the caller decides whether that degrades or throws.
|
|
41
|
-
*/
|
|
42
|
-
export function supervisorEntrypoint(exportsObj) {
|
|
43
|
-
const exports = exportsObj ?? _ctxExports;
|
|
44
|
-
if (!_supervisorEntrypointName)
|
|
45
|
-
return null;
|
|
46
|
-
if ((typeof exports !== 'object' && typeof exports !== 'function') || exports === null)
|
|
47
|
-
return null;
|
|
48
|
-
const factory = exports[_supervisorEntrypointName];
|
|
49
|
-
return typeof factory === 'function' ? factory : null;
|
|
50
|
-
}
|
|
51
|
-
/** The registered name, for error messages that point at the missing export. */
|
|
52
|
-
export function supervisorEntrypointName() {
|
|
53
|
-
return _supervisorEntrypointName;
|
|
54
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"facet-image-store.d.ts","sourceRoot":"","sources":["../src/facet-image-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,QAA0D,CAAC;AAErG;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wEAAwE;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,wEAAwE;IACxE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACjD,kDAAkD;IAClD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAClE,2EAA2E;IAC3E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,qDAAqD;IACrD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe;IAaxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAbzB,8EAA8E;IAC9E,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;OAMG;gBAEgB,KAAK,EAAE,MAAM,mBAAmB,EAChC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO;IAGnD;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,IAAI,IAAI;IAOjB;;;;;;OAMG;IACG,WAAW,CACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAoDlC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,KAAK;CAgBd"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fanout-pool.d.ts","sourceRoot":"","sources":["../src/fanout-pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"launch-journal.d.ts","sourceRoot":"","sources":["../src/launch-journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B,qBAAqB,CAAC;AAE7D,gFAAgF;AAChF,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB;;4DAEwD;IACxD,KAAK,EAAE,UAAU,GAAG,SAAS,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,oBAAoB;IAC/D;;;;;;OAMG;IACH,cAAc,IAAI,MAAM,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3C;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,mDAAmD;IACnD,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAC5B,yEAAyE;IACzE,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAC9B,kCAAkC;IAClC,eAAe,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,qBAAa,qBAAqB,CAAC,CAAC,SAAS,oBAAoB;IAW7D,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXvB;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAqB;IAC3C,gFAAgF;IAChF,OAAO,CAAC,SAAS,CAAS;gBAGP,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAG7C;;;;;;;;;;;;;;OAcG;IACG,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvC,8DAA8D;IAC9D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzC;;;;;;;;;;;OAWG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;CA6B1C"}
|
package/dist/launch-journal.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* launch-journal.ts — durable record of the resident launches a Durable Object
|
|
3
|
-
* owes, and their recovery after an instance reset.
|
|
4
|
-
*
|
|
5
|
-
* The platform resets a session Durable Object over what one turn has
|
|
6
|
-
* outstanding in storage ("Internal error in Durable Object storage caused
|
|
7
|
-
* object to be reset"), and a resident launch is the largest writer a session
|
|
8
|
-
* has. Everything a launch holds is in memory, so the process it is building
|
|
9
|
-
* and the terminal watching it both go with the instance — the journal is what
|
|
10
|
-
* a LATER instance reads to know that happened, and this module is the whole
|
|
11
|
-
* of that mechanism: the put→sync durability barrier on the way in, the
|
|
12
|
-
* delete→sync release on the way out, and the once-per-instance recovery pump
|
|
13
|
-
* that re-drives what a previous generation left behind.
|
|
14
|
-
*
|
|
15
|
-
* What a launch IS stays the embedder's: the journal stores the record it is
|
|
16
|
-
* given and hands it back on recovery. The mechanism reads only the fields in
|
|
17
|
-
* {@link ResidentLaunchRecord}; everything else in the record rides through
|
|
18
|
-
* opaquely.
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* Prefix for the resident-process journal: one row per resident this session
|
|
22
|
-
* owes the user, keyed by the pid it was built for.
|
|
23
|
-
*
|
|
24
|
-
* A resident holds its state in memory — the process table entry, the facet
|
|
25
|
-
* handle, the terminal — so an instance reset destroys it silently. The row
|
|
26
|
-
* is what a LATER instance reads to know a resident ended that way rather
|
|
27
|
-
* than on purpose: a pid at or below the reader's own pid base was allocated
|
|
28
|
-
* by a previous generation (PID_GEN_STRIDE, core's process-table). Written
|
|
29
|
-
* (and synced) before the launch's first byte of work, rewritten as `running`
|
|
30
|
-
* when the launch settles, and released only when the PROCESS ends — because
|
|
31
|
-
* the resets this row survives strike after the launch as often as during it
|
|
32
|
-
* (measured live, staging 2026-08-13: every observed reset landed seconds
|
|
33
|
-
* AFTER settle).
|
|
34
|
-
*
|
|
35
|
-
* The VALUE is live production DO storage and must never change — renaming a
|
|
36
|
-
* storage key is a migration, and orphaned rows are the least of what it
|
|
37
|
-
* breaks.
|
|
38
|
-
*/
|
|
39
|
-
export const RESIDENT_LAUNCH_KEY_PREFIX = 'resident-launch:';
|
|
40
|
-
/** A launch is re-driven once. A reset that recurs is not the transient one. */
|
|
41
|
-
export const RESIDENT_LAUNCH_MAX_ATTEMPT = 1;
|
|
42
|
-
/**
|
|
43
|
-
* The resident-launch journal of one Durable Object instance.
|
|
44
|
-
*
|
|
45
|
-
* In-memory state here is per-instance on purpose: `journalledPids` tracks the
|
|
46
|
-
* rows THIS instance wrote, and `recovered` whether this instance has already
|
|
47
|
-
* read the journal a reset leaves behind. Rows from a previous instance are
|
|
48
|
-
* recovery's to consume, never the release path's.
|
|
49
|
-
*/
|
|
50
|
-
export class ResidentLaunchJournal {
|
|
51
|
-
storage;
|
|
52
|
-
host;
|
|
53
|
-
/**
|
|
54
|
-
* Pids THIS instance holds journal rows for. What keeps the terminal hook —
|
|
55
|
-
* which fires for every process, shells and one-shots included — from
|
|
56
|
-
* paying a storage delete for pids that never had a row.
|
|
57
|
-
*/
|
|
58
|
-
journalledPids = new Set();
|
|
59
|
-
/** Whether this instance has already read the journal a reset leaves behind. */
|
|
60
|
-
recovered = false;
|
|
61
|
-
constructor(storage, host) {
|
|
62
|
-
this.storage = storage;
|
|
63
|
-
this.host = host;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Record a launch as in flight, so an instance that replaces this one knows
|
|
67
|
-
* it never finished. Best-effort: a launch that cannot be journalled still
|
|
68
|
-
* runs, and a reset then costs exactly what it cost before the journal.
|
|
69
|
-
*
|
|
70
|
-
* Synced, not merely put: `await put()` resolves before durability, and the
|
|
71
|
-
* reset this journal exists for destroys every write its turn still had
|
|
72
|
-
* outstanding — measured live, a launch killed in its first chunks left NO
|
|
73
|
-
* row for the replacement instance to find, which is how the recovery this
|
|
74
|
-
* feeds sat inert while its own test stayed green. `sync()` is the storage
|
|
75
|
-
* layer's durability barrier: the row is on disk before the launch performs
|
|
76
|
-
* its first byte of real work. What remains is a reset between the put and
|
|
77
|
-
* the sync's completion — and a launch that dies there has not started, so
|
|
78
|
-
* losing its row costs a retype, not a recovery.
|
|
79
|
-
*/
|
|
80
|
-
async journal(record) {
|
|
81
|
-
try {
|
|
82
|
-
this.journalledPids.add(record.pid);
|
|
83
|
-
await this.storage.put(`${RESIDENT_LAUNCH_KEY_PREFIX}${record.pid}`, record);
|
|
84
|
-
await this.storage.sync();
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
console.warn('[nimbus] resident launch journal write failed:', errorMessage(e));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/** True while this instance holds a journal row for `pid`. */
|
|
91
|
-
has(pid) {
|
|
92
|
-
return this.journalledPids.has(pid);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* The journal row's one release: the process is over, nothing is owed.
|
|
96
|
-
* Synced so an instance reset moments later cannot roll the delete back and
|
|
97
|
-
* resurrect a process the user watched end.
|
|
98
|
-
*/
|
|
99
|
-
async release(pid) {
|
|
100
|
-
if (!this.journalledPids.delete(pid))
|
|
101
|
-
return;
|
|
102
|
-
try {
|
|
103
|
-
await this.storage.delete(`${RESIDENT_LAUNCH_KEY_PREFIX}${pid}`);
|
|
104
|
-
await this.storage.sync();
|
|
105
|
-
}
|
|
106
|
-
catch (e) {
|
|
107
|
-
console.warn('[nimbus] resident launch journal delete failed:', errorMessage(e));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Re-drive the launches a previous instance was building when it was reset.
|
|
112
|
-
*
|
|
113
|
-
* Sited on the launch-turn pump because the pump is what an alarm calls, and
|
|
114
|
-
* a launch that was suspended has an alarm armed for it — a reset during a
|
|
115
|
-
* chunk fails that alarm, and the platform re-delivers it to the instance
|
|
116
|
-
* that replaces this one. So the first turn after a reset is already this
|
|
117
|
-
* one.
|
|
118
|
-
*
|
|
119
|
-
* Runs once per instance: the journal only changes when a launch of THIS
|
|
120
|
-
* instance starts or settles, and those are rows this instance wrote.
|
|
121
|
-
*/
|
|
122
|
-
async recoverInterrupted() {
|
|
123
|
-
if (this.recovered)
|
|
124
|
-
return;
|
|
125
|
-
this.recovered = true;
|
|
126
|
-
const journal = await this.storage.list({ prefix: RESIDENT_LAUNCH_KEY_PREFIX });
|
|
127
|
-
const base = this.host.generationBase();
|
|
128
|
-
for (const [key, record] of journal) {
|
|
129
|
-
// A pid at or below this instance's base was allocated by a PREVIOUS one
|
|
130
|
-
// (process-table.ts, PID_GEN_STRIDE), so its launch never finished; above
|
|
131
|
-
// the base is this instance's own, still running. Same predicate as
|
|
132
|
-
// `session/rpc.ts` uses to attribute a prior generation's pid.
|
|
133
|
-
if (!(record.pid > 0 && record.pid <= base))
|
|
134
|
-
continue;
|
|
135
|
-
await this.storage.delete(key);
|
|
136
|
-
if (record.attempt >= RESIDENT_LAUNCH_MAX_ATTEMPT) {
|
|
137
|
-
this.host.onAbandoned?.(record);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
this.host.onRedrive?.(record);
|
|
141
|
-
// Not awaited: this call is running inside the alarm that granted the
|
|
142
|
-
// turn, and the launch it starts asks for turns of its own through that
|
|
143
|
-
// same alarm — awaiting it here would be waiting on an alarm that cannot
|
|
144
|
-
// be scheduled until this one returns.
|
|
145
|
-
this.host.waitUntil(this.host.redrive(record, record.attempt + 1)
|
|
146
|
-
.catch((e) => {
|
|
147
|
-
this.host.onRedriveFailed?.(record, e);
|
|
148
|
-
}));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
function errorMessage(error) {
|
|
153
|
-
return error instanceof Error ? error.message : String(error);
|
|
154
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"launch-pacer.d.ts","sourceRoot":"","sources":["../src/launch-pacer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,UAAY,CAAC;AAEhD;;;;;;;;;GASG;AACH,qBAAa,WAAW;IAkBpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAnB/B,qEAAqE;IACrE,MAAM,SAAK;IACX,iDAAiD;IACjD,KAAK,SAAK;IAEV,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,UAAU,CAA8D;IAEhF;;;;;;;OAOG;gBAEgB,SAAS,EAAE,mBAAmB,EAC9B,aAAa,GAAE,MAA+B,EAC9C,WAAW,CAAC,GAAE,MAAM,IAAI,aAAA;IAG3C;;;;;;OAMG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAczC;;;;OAIG;IACH,MAAM,IAAI,IAAI;CAIf;AAQD,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,mBAAmB;IAa5C,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZjC;;;;;;;;;OASG;IACH,OAAO,CAAC,OAAO,CAAgE;gBAElD,IAAI,EAAE,kBAAkB;IAErD;;;;;;;;;OASG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,0DAA0D;IAC1D,IAAI,UAAU,IAAI,OAAO,CAExB;CACF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAMxD"}
|