@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.
Files changed (104) hide show
  1. package/README.md +208 -293
  2. package/dist/bindings.js +5 -5
  3. package/dist/budgets.d.ts +132 -0
  4. package/dist/budgets.d.ts.map +1 -0
  5. package/dist/budgets.js +248 -0
  6. package/dist/composition.d.ts +3 -0
  7. package/dist/composition.d.ts.map +1 -0
  8. package/dist/composition.js +2 -0
  9. package/dist/connections.d.ts +81 -0
  10. package/dist/connections.d.ts.map +1 -0
  11. package/dist/connections.js +114 -0
  12. package/dist/derived.d.ts +65 -0
  13. package/dist/derived.d.ts.map +1 -0
  14. package/dist/derived.js +95 -0
  15. package/dist/do-calls.d.ts +94 -0
  16. package/dist/do-calls.d.ts.map +1 -0
  17. package/dist/do-calls.js +111 -0
  18. package/dist/facet-pool.d.ts +90 -0
  19. package/dist/facet-pool.d.ts.map +1 -0
  20. package/dist/facet-pool.js +113 -0
  21. package/dist/{fanout-pool.d.ts → fanout.d.ts} +20 -20
  22. package/dist/fanout.d.ts.map +1 -0
  23. package/dist/{fanout-pool.js → fanout.js} +20 -20
  24. package/dist/{launch-journal.d.ts → fenced-work.d.ts} +58 -17
  25. package/dist/fenced-work.d.ts.map +1 -0
  26. package/dist/fenced-work.js +241 -0
  27. package/dist/generation.d.ts +69 -0
  28. package/dist/generation.d.ts.map +1 -0
  29. package/dist/generation.js +118 -0
  30. package/dist/{facet-image-store.d.ts → image-store.d.ts} +8 -8
  31. package/dist/image-store.d.ts.map +1 -0
  32. package/dist/{facet-image-store.js → image-store.js} +4 -4
  33. package/dist/index.d.ts +16 -8
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +16 -8
  36. package/dist/{loader-pool.d.ts → isolate-pool.d.ts} +19 -19
  37. package/dist/isolate-pool.d.ts.map +1 -0
  38. package/dist/{loader-pool.js → isolate-pool.js} +20 -20
  39. package/dist/journal.d.ts +111 -0
  40. package/dist/journal.d.ts.map +1 -0
  41. package/dist/journal.js +177 -0
  42. package/dist/outbox.d.ts +249 -0
  43. package/dist/outbox.d.ts.map +1 -0
  44. package/dist/outbox.js +355 -0
  45. package/dist/process-fabric.d.ts +33 -15
  46. package/dist/process-fabric.d.ts.map +1 -1
  47. package/dist/process-fabric.js +25 -15
  48. package/dist/process-host.d.ts +1 -1
  49. package/dist/process-host.d.ts.map +1 -1
  50. package/dist/process-host.js +19 -11
  51. package/dist/sealed.d.ts +78 -0
  52. package/dist/sealed.d.ts.map +1 -0
  53. package/dist/sealed.js +145 -0
  54. package/dist/timers.d.ts +138 -0
  55. package/dist/timers.d.ts.map +1 -0
  56. package/dist/timers.js +231 -0
  57. package/dist/{launch-pacer.d.ts → turn-budget.d.ts} +24 -21
  58. package/dist/turn-budget.d.ts.map +1 -0
  59. package/dist/{launch-pacer.js → turn-budget.js} +24 -12
  60. package/dist/workerd-facet-host.d.ts +67 -70
  61. package/dist/workerd-facet-host.d.ts.map +1 -1
  62. package/dist/workerd-facet-host.js +129 -181
  63. package/examples/agent-core-adapter.ts +191 -0
  64. package/package.json +4 -2
  65. package/src/bindings.ts +6 -6
  66. package/src/budgets.ts +308 -0
  67. package/src/composition.ts +16 -0
  68. package/src/connections.ts +140 -0
  69. package/src/derived.ts +135 -0
  70. package/src/do-calls.ts +156 -0
  71. package/src/facet-pool.ts +157 -0
  72. package/src/{fanout-pool.ts → fanout.ts} +35 -35
  73. package/src/{launch-journal.ts → fenced-work.ts} +129 -42
  74. package/src/generation.ts +144 -0
  75. package/src/{facet-image-store.ts → image-store.ts} +9 -9
  76. package/src/index.ts +16 -8
  77. package/src/{loader-pool.ts → isolate-pool.ts} +34 -34
  78. package/src/journal.ts +242 -0
  79. package/src/node-async-hooks.d.ts +14 -0
  80. package/src/outbox.ts +520 -0
  81. package/src/process-fabric.ts +43 -34
  82. package/src/process-host.ts +22 -20
  83. package/src/sealed.ts +150 -0
  84. package/src/timers.ts +294 -0
  85. package/src/{launch-pacer.ts → turn-budget.ts} +34 -27
  86. package/src/workerd-facet-host.ts +159 -208
  87. package/dist/alarms.d.ts +0 -134
  88. package/dist/alarms.d.ts.map +0 -1
  89. package/dist/alarms.js +0 -214
  90. package/dist/ctx-exports.d.ts +0 -47
  91. package/dist/ctx-exports.d.ts.map +0 -1
  92. package/dist/ctx-exports.js +0 -54
  93. package/dist/facet-image-store.d.ts.map +0 -1
  94. package/dist/fanout-pool.d.ts.map +0 -1
  95. package/dist/launch-journal.d.ts.map +0 -1
  96. package/dist/launch-journal.js +0 -154
  97. package/dist/launch-pacer.d.ts.map +0 -1
  98. package/dist/loader-ledger.d.ts +0 -57
  99. package/dist/loader-ledger.d.ts.map +0 -1
  100. package/dist/loader-ledger.js +0 -91
  101. package/dist/loader-pool.d.ts.map +0 -1
  102. package/src/alarms.ts +0 -275
  103. package/src/ctx-exports.ts +0 -77
  104. 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
@@ -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
- }
@@ -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"}
@@ -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"}
@@ -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"}