@graphrefly/ts 0.0.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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/index.cjs +9015 -0
  3. package/dist/adapters/index.cjs.map +1 -0
  4. package/dist/adapters/index.d.cts +678 -0
  5. package/dist/adapters/index.d.ts +678 -0
  6. package/dist/adapters/index.js +2894 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/adapters/observe-storage.cjs +260 -0
  9. package/dist/adapters/observe-storage.cjs.map +1 -0
  10. package/dist/adapters/observe-storage.d.cts +77 -0
  11. package/dist/adapters/observe-storage.d.ts +77 -0
  12. package/dist/adapters/observe-storage.js +161 -0
  13. package/dist/adapters/observe-storage.js.map +1 -0
  14. package/dist/append-log-DisvEReb.d.cts +189 -0
  15. package/dist/append-log-DzKMmjxx.d.ts +189 -0
  16. package/dist/chunk-2ILL7HQI.js +1189 -0
  17. package/dist/chunk-2ILL7HQI.js.map +1 -0
  18. package/dist/chunk-4VLAQHZI.js +330 -0
  19. package/dist/chunk-4VLAQHZI.js.map +1 -0
  20. package/dist/chunk-6PHB5CJS.js +229 -0
  21. package/dist/chunk-6PHB5CJS.js.map +1 -0
  22. package/dist/chunk-COHO25CZ.js +2449 -0
  23. package/dist/chunk-COHO25CZ.js.map +1 -0
  24. package/dist/chunk-D6BG7U6A.js +161 -0
  25. package/dist/chunk-D6BG7U6A.js.map +1 -0
  26. package/dist/chunk-DJC4CHLQ.js +818 -0
  27. package/dist/chunk-DJC4CHLQ.js.map +1 -0
  28. package/dist/chunk-DK554R7P.js +67 -0
  29. package/dist/chunk-DK554R7P.js.map +1 -0
  30. package/dist/chunk-DKP56CSW.js +322 -0
  31. package/dist/chunk-DKP56CSW.js.map +1 -0
  32. package/dist/chunk-G4M3NHRJ.js +688 -0
  33. package/dist/chunk-G4M3NHRJ.js.map +1 -0
  34. package/dist/chunk-GAJW5I5P.js +1 -0
  35. package/dist/chunk-GAJW5I5P.js.map +1 -0
  36. package/dist/chunk-GDL26N2T.js +282 -0
  37. package/dist/chunk-GDL26N2T.js.map +1 -0
  38. package/dist/chunk-GTPCOEOZ.js +741 -0
  39. package/dist/chunk-GTPCOEOZ.js.map +1 -0
  40. package/dist/chunk-H2HTHFVN.js +1 -0
  41. package/dist/chunk-H2HTHFVN.js.map +1 -0
  42. package/dist/chunk-HDRL4VQQ.js +218 -0
  43. package/dist/chunk-HDRL4VQQ.js.map +1 -0
  44. package/dist/chunk-I3IHOHCJ.js +2267 -0
  45. package/dist/chunk-I3IHOHCJ.js.map +1 -0
  46. package/dist/chunk-JIMUDQN3.js +965 -0
  47. package/dist/chunk-JIMUDQN3.js.map +1 -0
  48. package/dist/chunk-JR4IKCR2.js +462 -0
  49. package/dist/chunk-JR4IKCR2.js.map +1 -0
  50. package/dist/chunk-K3FXTC3I.js +165 -0
  51. package/dist/chunk-K3FXTC3I.js.map +1 -0
  52. package/dist/chunk-OOEN33FB.js +65 -0
  53. package/dist/chunk-OOEN33FB.js.map +1 -0
  54. package/dist/chunk-PKVBHGBM.js +554 -0
  55. package/dist/chunk-PKVBHGBM.js.map +1 -0
  56. package/dist/chunk-RIIOUTGM.js +1857 -0
  57. package/dist/chunk-RIIOUTGM.js.map +1 -0
  58. package/dist/chunk-SCRFRCGR.js +33 -0
  59. package/dist/chunk-SCRFRCGR.js.map +1 -0
  60. package/dist/chunk-SWTLEQL6.js +53 -0
  61. package/dist/chunk-SWTLEQL6.js.map +1 -0
  62. package/dist/chunk-SZAETNNA.js +1 -0
  63. package/dist/chunk-SZAETNNA.js.map +1 -0
  64. package/dist/chunk-TEW47IVR.js +49 -0
  65. package/dist/chunk-TEW47IVR.js.map +1 -0
  66. package/dist/chunk-Y3ZLRGHC.js +1482 -0
  67. package/dist/chunk-Y3ZLRGHC.js.map +1 -0
  68. package/dist/chunk-Z2ANQ6JW.js +149 -0
  69. package/dist/chunk-Z2ANQ6JW.js.map +1 -0
  70. package/dist/codec-BB7p8fJc.d.cts +38 -0
  71. package/dist/codec-BB7p8fJc.d.ts +38 -0
  72. package/dist/composition/index.cjs +2505 -0
  73. package/dist/composition/index.cjs.map +1 -0
  74. package/dist/composition/index.d.cts +95 -0
  75. package/dist/composition/index.d.ts +95 -0
  76. package/dist/composition/index.js +19 -0
  77. package/dist/composition/index.js.map +1 -0
  78. package/dist/core/index.cjs +2475 -0
  79. package/dist/core/index.cjs.map +1 -0
  80. package/dist/core/index.d.cts +25 -0
  81. package/dist/core/index.d.ts +25 -0
  82. package/dist/core/index.js +74 -0
  83. package/dist/core/index.js.map +1 -0
  84. package/dist/cqrs/index.cjs +595 -0
  85. package/dist/cqrs/index.cjs.map +1 -0
  86. package/dist/cqrs/index.d.cts +200 -0
  87. package/dist/cqrs/index.d.ts +200 -0
  88. package/dist/cqrs/index.js +557 -0
  89. package/dist/cqrs/index.js.map +1 -0
  90. package/dist/data-structures/index.cjs +4277 -0
  91. package/dist/data-structures/index.cjs.map +1 -0
  92. package/dist/data-structures/index.d.cts +22 -0
  93. package/dist/data-structures/index.d.ts +22 -0
  94. package/dist/data-structures/index.js +31 -0
  95. package/dist/data-structures/index.js.map +1 -0
  96. package/dist/describe-BZb7AS2y.d.ts +57 -0
  97. package/dist/describe-Ciu8SUnN.d.cts +57 -0
  98. package/dist/graph/index.cjs +7832 -0
  99. package/dist/graph/index.cjs.map +1 -0
  100. package/dist/graph/index.d.cts +251 -0
  101. package/dist/graph/index.d.ts +251 -0
  102. package/dist/graph/index.js +131 -0
  103. package/dist/graph/index.js.map +1 -0
  104. package/dist/graph-DfdF9zy4.d.ts +666 -0
  105. package/dist/graph-s23RbbFO.d.cts +666 -0
  106. package/dist/index-CAEmLCYF.d.cts +166 -0
  107. package/dist/index-SnzZu3x7.d.ts +166 -0
  108. package/dist/index.cjs +15409 -0
  109. package/dist/index.cjs.map +1 -0
  110. package/dist/index.d.cts +23 -0
  111. package/dist/index.d.ts +23 -0
  112. package/dist/index.js +582 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/messages-BZqrGGm1.d.cts +55 -0
  115. package/dist/messages-BZqrGGm1.d.ts +55 -0
  116. package/dist/messaging/index.cjs +818 -0
  117. package/dist/messaging/index.cjs.map +1 -0
  118. package/dist/messaging/index.d.cts +235 -0
  119. package/dist/messaging/index.d.ts +235 -0
  120. package/dist/messaging/index.js +767 -0
  121. package/dist/messaging/index.js.map +1 -0
  122. package/dist/observe-event-log-0SVsx7JG.d.cts +88 -0
  123. package/dist/observe-event-log-Dnm76owp.d.ts +88 -0
  124. package/dist/operators/index.cjs +3774 -0
  125. package/dist/operators/index.cjs.map +1 -0
  126. package/dist/operators/index.d.cts +308 -0
  127. package/dist/operators/index.d.ts +308 -0
  128. package/dist/operators/index.js +112 -0
  129. package/dist/operators/index.js.map +1 -0
  130. package/dist/orchestration/index.cjs +3541 -0
  131. package/dist/orchestration/index.cjs.map +1 -0
  132. package/dist/orchestration/index.d.cts +302 -0
  133. package/dist/orchestration/index.d.ts +302 -0
  134. package/dist/orchestration/index.js +1004 -0
  135. package/dist/orchestration/index.js.map +1 -0
  136. package/dist/patterns/index.cjs +1045 -0
  137. package/dist/patterns/index.cjs.map +1 -0
  138. package/dist/patterns/index.d.cts +39 -0
  139. package/dist/patterns/index.d.ts +39 -0
  140. package/dist/patterns/index.js +33 -0
  141. package/dist/patterns/index.js.map +1 -0
  142. package/dist/reactive-collection-BxBqtIOE.d.cts +344 -0
  143. package/dist/reactive-collection-Cb1II1Fi.d.ts +344 -0
  144. package/dist/reactive-collection-storage-BNiEc-Gd.d.ts +217 -0
  145. package/dist/reactive-collection-storage-D-KmjE0J.d.cts +217 -0
  146. package/dist/reactive-map-BcB4KsQR.d.ts +256 -0
  147. package/dist/reactive-map-D-g3dv8I.d.cts +256 -0
  148. package/dist/read-through-B1CYPfq_.d.cts +82 -0
  149. package/dist/read-through-DwyJhGs1.d.ts +82 -0
  150. package/dist/render/index.cjs +250 -0
  151. package/dist/render/index.cjs.map +1 -0
  152. package/dist/render/index.d.cts +58 -0
  153. package/dist/render/index.d.ts +58 -0
  154. package/dist/render/index.js +20 -0
  155. package/dist/render/index.js.map +1 -0
  156. package/dist/resilience-Dig3Q7n0.d.cts +45 -0
  157. package/dist/resilience-Dig3Q7n0.d.ts +45 -0
  158. package/dist/semantic-memory-graph-BYoIlP35.d.cts +356 -0
  159. package/dist/semantic-memory-graph-CxyXLms3.d.ts +356 -0
  160. package/dist/solutions/index.cjs +3249 -0
  161. package/dist/solutions/index.cjs.map +1 -0
  162. package/dist/solutions/index.d.cts +520 -0
  163. package/dist/solutions/index.d.ts +520 -0
  164. package/dist/solutions/index.js +31 -0
  165. package/dist/solutions/index.js.map +1 -0
  166. package/dist/sources/browser.cjs +3095 -0
  167. package/dist/sources/browser.cjs.map +1 -0
  168. package/dist/sources/browser.d.cts +39 -0
  169. package/dist/sources/browser.d.ts +39 -0
  170. package/dist/sources/browser.js +137 -0
  171. package/dist/sources/browser.js.map +1 -0
  172. package/dist/sources/index.cjs +3021 -0
  173. package/dist/sources/index.cjs.map +1 -0
  174. package/dist/sources/index.d.cts +7 -0
  175. package/dist/sources/index.d.ts +7 -0
  176. package/dist/sources/index.js +63 -0
  177. package/dist/sources/index.js.map +1 -0
  178. package/dist/sources/node.cjs +675 -0
  179. package/dist/sources/node.cjs.map +1 -0
  180. package/dist/sources/node.d.cts +109 -0
  181. package/dist/sources/node.d.ts +109 -0
  182. package/dist/sources/node.js +639 -0
  183. package/dist/sources/node.js.map +1 -0
  184. package/dist/storage/browser.cjs +777 -0
  185. package/dist/storage/browser.cjs.map +1 -0
  186. package/dist/storage/browser.d.cts +40 -0
  187. package/dist/storage/browser.d.ts +40 -0
  188. package/dist/storage/browser.js +275 -0
  189. package/dist/storage/browser.js.map +1 -0
  190. package/dist/storage/index.cjs +1989 -0
  191. package/dist/storage/index.cjs.map +1 -0
  192. package/dist/storage/index.d.cts +92 -0
  193. package/dist/storage/index.d.ts +92 -0
  194. package/dist/storage/index.js +161 -0
  195. package/dist/storage/index.js.map +1 -0
  196. package/dist/storage/node.cjs +898 -0
  197. package/dist/storage/node.cjs.map +1 -0
  198. package/dist/storage/node.d.cts +66 -0
  199. package/dist/storage/node.d.ts +66 -0
  200. package/dist/storage/node.js +402 -0
  201. package/dist/storage/node.js.map +1 -0
  202. package/dist/testing/index.cjs +95 -0
  203. package/dist/testing/index.cjs.map +1 -0
  204. package/dist/testing/index.d.cts +20 -0
  205. package/dist/testing/index.d.ts +20 -0
  206. package/dist/testing/index.js +8 -0
  207. package/dist/testing/index.js.map +1 -0
  208. package/dist/types-bNMSWKqF.d.cts +732 -0
  209. package/dist/types-h12mNYbq.d.ts +732 -0
  210. package/package.json +250 -0
@@ -0,0 +1,4277 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/data-structures/index.ts
21
+ var data_structures_exports = {};
22
+ __export(data_structures_exports, {
23
+ mergeReactiveLogs: () => mergeReactiveLogs,
24
+ reactiveIndex: () => reactiveIndex,
25
+ reactiveList: () => reactiveList,
26
+ reactiveLog: () => reactiveLog,
27
+ reactiveMap: () => reactiveMap,
28
+ restoreReactiveIndex: () => restoreReactiveIndex,
29
+ restoreReactiveList: () => restoreReactiveList,
30
+ restoreReactiveLog: () => restoreReactiveLog,
31
+ restoreReactiveMap: () => restoreReactiveMap,
32
+ scanLog: () => scanLog
33
+ });
34
+ module.exports = __toCommonJS(data_structures_exports);
35
+
36
+ // src/protocol/messages.ts
37
+ var SENTINEL = void 0;
38
+ function isInvalidErrorPayload(v) {
39
+ return v === SENTINEL || typeof v === "boolean";
40
+ }
41
+ function errorPayload(reason, fallback = "error without a valid payload") {
42
+ return isInvalidErrorPayload(reason) ? new Error(fallback) : reason;
43
+ }
44
+ var TIER_START = 0;
45
+ var TIER_CONTROL = 1;
46
+ var TIER_NOTIFICATION = 2;
47
+ var TIER_VALUE = 3;
48
+ var TIER_SETTLE = 4;
49
+ var TIER_TERMINAL = 5;
50
+ var TIER_TEARDOWN = 6;
51
+ var TIER = {
52
+ START: TIER_START,
53
+ PAUSE: TIER_CONTROL,
54
+ RESUME: TIER_CONTROL,
55
+ DIRTY: TIER_NOTIFICATION,
56
+ DATA: TIER_VALUE,
57
+ RESOLVED: TIER_VALUE,
58
+ INVALIDATE: TIER_SETTLE,
59
+ COMPLETE: TIER_TERMINAL,
60
+ ERROR: TIER_TERMINAL,
61
+ TEARDOWN: TIER_TEARDOWN
62
+ };
63
+ function messageTier(t) {
64
+ return TIER[t];
65
+ }
66
+ function isDeferredTier(t) {
67
+ return TIER[t] >= TIER_VALUE;
68
+ }
69
+ function isValueTier(t) {
70
+ return TIER[t] === TIER_VALUE;
71
+ }
72
+ function isPauseBufferedTier(t) {
73
+ const tier = TIER[t];
74
+ return tier === TIER_VALUE || tier === TIER_SETTLE;
75
+ }
76
+ function isTerminal(t) {
77
+ return TIER[t] === TIER_TERMINAL;
78
+ }
79
+ function isUpAllowed(t) {
80
+ const tier = TIER[t];
81
+ return tier !== TIER_VALUE && tier !== TIER_TERMINAL;
82
+ }
83
+
84
+ // src/ctx/types.ts
85
+ var CTX_DEP_CACHE = /* @__PURE__ */ Symbol("graphrefly.ctx.depCache");
86
+ var CTX_NODE_BINDING = /* @__PURE__ */ Symbol("graphrefly.ctx.nodeBinding");
87
+ function isTerminalComplete(t) {
88
+ return t === true;
89
+ }
90
+ function depCount(ctx) {
91
+ return ctx.waveData.length;
92
+ }
93
+ function depWaves(ctx, depIndex) {
94
+ return ctx.waveData[depIndex] ?? [];
95
+ }
96
+ function depBatch(ctx, depIndex) {
97
+ const waves = depWaves(ctx, depIndex);
98
+ if (waves.length === 0) return null;
99
+ const flattened = waves.flat().filter((v) => v !== SENTINEL);
100
+ return flattened.length === 0 ? [] : flattened;
101
+ }
102
+ function depLatest(ctx, depIndex) {
103
+ return ctx[CTX_DEP_CACHE]?.latest[depIndex];
104
+ }
105
+ function depTerminal(ctx, depIndex) {
106
+ const t = ctx.terminal[depIndex];
107
+ return t === void 0 ? false : t;
108
+ }
109
+
110
+ // src/batch/boundary.ts
111
+ var depth = 0;
112
+ var pendingCores = [];
113
+ var pendingHead = 0;
114
+ function enterWave() {
115
+ depth++;
116
+ }
117
+ function exitWave() {
118
+ depth--;
119
+ if (depth === 0 && pendingHead < pendingCores.length) drain();
120
+ }
121
+ function deferRewire(core, apply, options = {}) {
122
+ core.enqueueBoundaryTask({ apply, batchToken: options.batchToken, isReady: options.isReady });
123
+ pendingCores.push(core);
124
+ }
125
+ function scheduleBoundaryDrain(core) {
126
+ for (let i = 0; i < core.boundaryTaskCount(); i++) pendingCores.push(core);
127
+ if (depth === 0 && pendingHead < pendingCores.length) drain();
128
+ }
129
+ function drain() {
130
+ let escaped = null;
131
+ while (pendingHead < pendingCores.length) {
132
+ const core = pendingCores[pendingHead++];
133
+ const task = core.shiftBoundaryTask();
134
+ if (task === void 0) continue;
135
+ if (task.batchToken !== void 0) {
136
+ const committed = task.batchToken.committed === true;
137
+ if (!committed) continue;
138
+ }
139
+ if (task.isReady !== void 0 && !task.isReady()) {
140
+ core.unshiftBoundaryTask(task);
141
+ continue;
142
+ }
143
+ depth++;
144
+ try {
145
+ task.apply();
146
+ } catch (e) {
147
+ if (escaped === null) escaped = { e };
148
+ } finally {
149
+ depth--;
150
+ }
151
+ }
152
+ pendingCores.length = 0;
153
+ pendingHead = 0;
154
+ if (escaped !== null) throw escaped.e;
155
+ }
156
+
157
+ // src/batch/batch.ts
158
+ var active = null;
159
+ var boundaryOwner = null;
160
+ function currentBatch() {
161
+ return active !== null;
162
+ }
163
+ function currentBoundaryBatchToken() {
164
+ return active ?? boundaryOwner ?? void 0;
165
+ }
166
+ function deferToBatch(target, tier3Wave) {
167
+ if (active === null) return false;
168
+ if (!active.deferred.has(target)) active.order.push(target);
169
+ active.deferred.set(target, tier3Wave);
170
+ return true;
171
+ }
172
+ function deferAfterBatchForTarget(target, fn) {
173
+ if (active === null || !active.deferred.has(target)) return false;
174
+ const owner = active;
175
+ target.__deferBoundary(() => {
176
+ if (owner.committed) fn();
177
+ }, owner);
178
+ return true;
179
+ }
180
+
181
+ // src/dispatcher/index.ts
182
+ var PoolTable = class {
183
+ constructor(kind) {
184
+ this.kind = kind;
185
+ }
186
+ fns = [];
187
+ free = [];
188
+ register(fn) {
189
+ const reused = this.free.pop();
190
+ if (reused !== void 0) {
191
+ this.fns[reused] = fn;
192
+ return reused;
193
+ }
194
+ const id = this.fns.length;
195
+ this.fns.push(fn);
196
+ return id;
197
+ }
198
+ unregister(handleId) {
199
+ if (this.fns[handleId] === void 0) return;
200
+ this.fns[handleId] = void 0;
201
+ this.free.push(handleId);
202
+ }
203
+ invoke(handleId, ctx) {
204
+ this.fns[handleId](ctx);
205
+ }
206
+ };
207
+ var statKey = (h) => `${h.poolId}:${h.handleId}`;
208
+ var Dispatcher = class {
209
+ pools = [];
210
+ syncPoolId;
211
+ asyncPoolId;
212
+ // opt-in profile recorder (default OFF → zero overhead, F-PERF).
213
+ _recording = false;
214
+ _stats = /* @__PURE__ */ new Map();
215
+ _totalInvokes = 0;
216
+ constructor() {
217
+ this.syncPoolId = this.addPool(new PoolTable("sync"));
218
+ this.asyncPoolId = this.addPool(new PoolTable("async"));
219
+ }
220
+ /** Turn the profile recorder on/off (D39). Off = zero overhead on invoke. */
221
+ setRecording(on) {
222
+ this._recording = on;
223
+ }
224
+ /** Reset accumulated profiling counters. */
225
+ clearStats() {
226
+ this._stats.clear();
227
+ this._totalInvokes = 0;
228
+ }
229
+ /** Read a handle's accumulated counters (undefined if it never ran while recording). */
230
+ statFor(handle) {
231
+ return this._stats.get(statKey(handle));
232
+ }
233
+ /** Total fn invocations recorded across the dispatcher. */
234
+ get totalInvokes() {
235
+ return this._totalInvokes;
236
+ }
237
+ addPool(pool) {
238
+ const id = this.pools.length;
239
+ this.pools.push(pool);
240
+ return id;
241
+ }
242
+ /** Register a fn in a pool, returning its Handle. Default pool = sync (R-sync-core). */
243
+ register(fn, pool = "sync") {
244
+ const poolId = pool === "sync" ? this.syncPoolId : pool === "async" ? this.asyncPoolId : pool;
245
+ const handleId = this.pools[poolId].register(fn);
246
+ return { poolId, handleId };
247
+ }
248
+ /**
249
+ * Release a handle (B15): frees the pool slot (closure GC'd, id reusable) and drops any
250
+ * accumulated profile stat so a reused id never inherits the previous tenant's counters.
251
+ * Called on rewire fn-swap (node._rewire) — the old handle is dropped before the node
252
+ * adopts the new one. Idempotent. NOT called on deactivate (a node's handle survives
253
+ * activate↔deactivate and is reused on reactivation; only a rewire swaps it).
254
+ */
255
+ unregister(handle) {
256
+ this.pools[handle.poolId].unregister(handle.handleId);
257
+ this._stats.delete(statKey(handle));
258
+ }
259
+ /** Uniform sync-void invoke (R-sync-core / R-dispatch-all). */
260
+ invoke(handle, ctx) {
261
+ if (!this._recording) {
262
+ this.pools[handle.poolId].invoke(handle.handleId, ctx);
263
+ return;
264
+ }
265
+ this._totalInvokes++;
266
+ const t0 = performance.now();
267
+ try {
268
+ this.pools[handle.poolId].invoke(handle.handleId, ctx);
269
+ } finally {
270
+ const dur = (performance.now() - t0) * 1e6;
271
+ const key = statKey(handle);
272
+ const s = this._stats.get(key) ?? {
273
+ invokes: 0,
274
+ totalDurationNs: 0,
275
+ lastDurationNs: 0
276
+ };
277
+ s.invokes++;
278
+ s.lastDurationNs = dur;
279
+ s.totalDurationNs += dur;
280
+ this._stats.set(key, s);
281
+ }
282
+ }
283
+ poolKind(poolId) {
284
+ return this.pools[poolId].kind;
285
+ }
286
+ };
287
+ var defaultDispatcher = new Dispatcher();
288
+
289
+ // src/graph/environment.ts
290
+ var EnvironmentDrivers = class _EnvironmentDrivers {
291
+ process;
292
+ http;
293
+ sse;
294
+ websocket;
295
+ webhook;
296
+ constructor(init = {}) {
297
+ this.process = init.process;
298
+ this.http = init.http;
299
+ this.sse = init.sse;
300
+ this.websocket = init.websocket;
301
+ this.webhook = init.webhook;
302
+ Object.freeze(this);
303
+ }
304
+ static empty() {
305
+ return EMPTY_ENVIRONMENT;
306
+ }
307
+ withProcess(driver) {
308
+ return new _EnvironmentDrivers({ ...this, process: driver });
309
+ }
310
+ withHttp(driver) {
311
+ return new _EnvironmentDrivers({ ...this, http: driver });
312
+ }
313
+ withSse(driver) {
314
+ return new _EnvironmentDrivers({ ...this, sse: driver });
315
+ }
316
+ withWebSocket(driver) {
317
+ return new _EnvironmentDrivers({ ...this, websocket: driver });
318
+ }
319
+ withWebhook(driver) {
320
+ return new _EnvironmentDrivers({ ...this, webhook: driver });
321
+ }
322
+ processDriver() {
323
+ return this.process;
324
+ }
325
+ httpDriver() {
326
+ return this.http;
327
+ }
328
+ sseDriver() {
329
+ return this.sse;
330
+ }
331
+ webSocketDriver() {
332
+ return this.websocket;
333
+ }
334
+ webhookDriver() {
335
+ return this.webhook;
336
+ }
337
+ };
338
+ var EMPTY_ENVIRONMENT = new EnvironmentDrivers();
339
+
340
+ // src/node/core.ts
341
+ var NodeCore = class {
342
+ nextId = 0;
343
+ slots = [];
344
+ values = [];
345
+ waves = [];
346
+ controls = [];
347
+ lifecycles = [];
348
+ depStates = [];
349
+ privateStates = [];
350
+ hooks = [];
351
+ syncCtxs = [];
352
+ versionStates = [];
353
+ boundary = { queue: [], head: 0 };
354
+ createSlot(slot, state) {
355
+ const id = this.nextId++;
356
+ const full = { ...slot, id };
357
+ this.slots[id] = full;
358
+ this.depStates[id] = state.dep;
359
+ this.lifecycles[id] = state.lifecycle;
360
+ this.values[id] = state.value;
361
+ this.waves[id] = state.wave;
362
+ this.controls[id] = state.control;
363
+ this.privateStates[id] = state.privateState;
364
+ this.hooks[id] = state.hooks;
365
+ this.syncCtxs[id] = state.syncCtx;
366
+ this.versionStates[id] = state.version;
367
+ return { id, slot: full };
368
+ }
369
+ get(id) {
370
+ const slot = this.slots[id];
371
+ if (slot === void 0) throw new Error("NodeCore: unknown node slot");
372
+ return slot;
373
+ }
374
+ getValue(id) {
375
+ const value = this.values[id];
376
+ if (value === void 0) throw new Error("NodeCore: unknown node value state");
377
+ return value;
378
+ }
379
+ getWave(id) {
380
+ const wave = this.waves[id];
381
+ if (wave === void 0) throw new Error("NodeCore: unknown node wave state");
382
+ return wave;
383
+ }
384
+ getControl(id) {
385
+ const control = this.controls[id];
386
+ if (control === void 0) throw new Error("NodeCore: unknown node control state");
387
+ return control;
388
+ }
389
+ getLifecycle(id) {
390
+ const lifecycle = this.lifecycles[id];
391
+ if (lifecycle === void 0) throw new Error("NodeCore: unknown node lifecycle state");
392
+ return lifecycle;
393
+ }
394
+ getDep(id) {
395
+ const dep = this.depStates[id];
396
+ if (dep === void 0) throw new Error("NodeCore: unknown node dep state");
397
+ return dep;
398
+ }
399
+ getPrivateState(id) {
400
+ const state = this.privateStates[id];
401
+ if (state === void 0) throw new Error("NodeCore: unknown node private state");
402
+ return state;
403
+ }
404
+ getHooks(id) {
405
+ const hooks = this.hooks[id];
406
+ if (hooks === void 0) throw new Error("NodeCore: unknown node cleanup hooks");
407
+ return hooks;
408
+ }
409
+ getSyncCtx(id) {
410
+ const state = this.syncCtxs[id];
411
+ if (state === void 0) throw new Error("NodeCore: unknown node ctx state");
412
+ return state;
413
+ }
414
+ getVersion(id) {
415
+ const state = this.versionStates[id];
416
+ if (state === void 0) throw new Error("NodeCore: unknown node version state");
417
+ return state;
418
+ }
419
+ /** @internal D122: release graph-owned ephemeral node runtime state from core retention. */
420
+ releaseSlot(id) {
421
+ this.slots[id] = void 0;
422
+ this.depStates[id] = void 0;
423
+ this.lifecycles[id] = void 0;
424
+ this.values[id] = void 0;
425
+ this.waves[id] = void 0;
426
+ this.controls[id] = void 0;
427
+ this.privateStates[id] = void 0;
428
+ this.hooks[id] = void 0;
429
+ this.syncCtxs[id] = void 0;
430
+ this.versionStates[id] = void 0;
431
+ }
432
+ /** @internal B49: graph-local deferred-boundary queue (rewireNext/upNext/batch-after-commit). */
433
+ enqueueBoundaryTask(task) {
434
+ this.boundary.queue.push(task);
435
+ }
436
+ /** @internal */
437
+ hasBoundaryTasks() {
438
+ return this.boundary.head < this.boundary.queue.length;
439
+ }
440
+ /** @internal */
441
+ boundaryTaskCount() {
442
+ return this.boundary.queue.length - this.boundary.head;
443
+ }
444
+ /** @internal */
445
+ shiftBoundaryTask() {
446
+ if (!this.hasBoundaryTasks()) {
447
+ this.boundary.queue = [];
448
+ this.boundary.head = 0;
449
+ return void 0;
450
+ }
451
+ const task = this.boundary.queue[this.boundary.head++];
452
+ if (!this.hasBoundaryTasks()) {
453
+ this.boundary.queue = [];
454
+ this.boundary.head = 0;
455
+ }
456
+ return task;
457
+ }
458
+ /** @internal Put a not-yet-ready task back at this core's FIFO head. */
459
+ unshiftBoundaryTask(task) {
460
+ const remaining = this.boundary.queue.slice(this.boundary.head);
461
+ this.boundary.queue = [task, ...remaining];
462
+ this.boundary.head = 0;
463
+ }
464
+ /** @internal D110: discard all pending tasks caused by an uncommitted batch. */
465
+ dropBoundaryTasksForBatch(batchToken) {
466
+ const remaining = this.boundary.queue.slice(this.boundary.head).filter((task) => task.batchToken !== batchToken);
467
+ this.boundary.queue = remaining;
468
+ this.boundary.head = 0;
469
+ }
470
+ };
471
+ function makeDepBookkeeping(depCount2) {
472
+ return {
473
+ batch: new Array(depCount2).fill(null),
474
+ waveData: Array.from({ length: depCount2 }, () => []),
475
+ waveTokens: new Array(depCount2).fill(void 0),
476
+ prev: new Array(depCount2).fill(SENTINEL),
477
+ hasData: new Array(depCount2).fill(false),
478
+ dirty: new Array(depCount2).fill(false),
479
+ tier: new Array(depCount2).fill(0),
480
+ terminal: new Array(depCount2).fill(void 0),
481
+ terminalInput: new Array(depCount2).fill(void 0),
482
+ unsubs: [],
483
+ idxBoxes: []
484
+ };
485
+ }
486
+
487
+ // src/json/codec.ts
488
+ var JS_MIN_NORMAL_NUMBER = 2 ** -1022;
489
+ function assertStableJsonNumber(value, path) {
490
+ if (!Number.isFinite(value)) {
491
+ throw new TypeError(`stableJsonString: non-finite number at ${path}`);
492
+ }
493
+ }
494
+ function assertStrictJsonNumber(value, path) {
495
+ assertStableJsonNumber(value, path);
496
+ if (Object.is(value, -0)) {
497
+ throw new TypeError(`stableJsonString: non-canonical number at ${path}`);
498
+ }
499
+ const abs = Math.abs(value);
500
+ if (abs > 0 && abs < JS_MIN_NORMAL_NUMBER) {
501
+ throw new TypeError(`stableJsonString: subnormal number at ${path}`);
502
+ }
503
+ if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
504
+ throw new TypeError(`stableJsonString: integer outside safe range at ${path}`);
505
+ }
506
+ }
507
+ function sortedJsonValue(value, seen = /* @__PURE__ */ new Set(), path = "$", strictNumbers = false) {
508
+ if (value === null) return null;
509
+ if (typeof value === "string" || typeof value === "boolean") return value;
510
+ if (typeof value === "number") {
511
+ if (strictNumbers) assertStrictJsonNumber(value, path);
512
+ else assertStableJsonNumber(value, path);
513
+ return value;
514
+ }
515
+ if (typeof value !== "object") {
516
+ throw new TypeError(`stableJsonString: value at ${path} is not JSON-encodable`);
517
+ }
518
+ if (seen.has(value)) throw new TypeError(`stableJsonString: circular reference at ${path}`);
519
+ const proto = Object.getPrototypeOf(value);
520
+ if (!Array.isArray(value) && proto !== Object.prototype && proto !== null) {
521
+ throw new TypeError(`stableJsonString: non-plain object at ${path}`);
522
+ }
523
+ seen.add(value);
524
+ try {
525
+ if (Array.isArray(value)) {
526
+ if (Object.getOwnPropertySymbols(value).length > 0) {
527
+ throw new TypeError(`stableJsonString: symbol-keyed properties at ${path}`);
528
+ }
529
+ for (const key of Object.getOwnPropertyNames(value)) {
530
+ const isIndex = /^(0|[1-9]\d*)$/.test(key) && Number.isSafeInteger(Number(key)) && Number(key) < value.length;
531
+ if (key !== "length" && !isIndex) {
532
+ throw new TypeError(`stableJsonString: non-index array property at ${path}.${key}`);
533
+ }
534
+ }
535
+ const out2 = [];
536
+ for (let i = 0; i < value.length; i += 1) {
537
+ if (!(i in value)) {
538
+ throw new TypeError(`stableJsonString: sparse array hole at ${path}[${i}]`);
539
+ }
540
+ out2.push(sortedJsonValue(value[i], seen, `${path}[${i}]`, strictNumbers));
541
+ }
542
+ return out2;
543
+ }
544
+ if (Object.getOwnPropertySymbols(value).length > 0) {
545
+ throw new TypeError(`stableJsonString: symbol-keyed properties at ${path}`);
546
+ }
547
+ const out = /* @__PURE__ */ Object.create(null);
548
+ for (const key of Object.keys(value).sort()) {
549
+ out[key] = sortedJsonValue(
550
+ value[key],
551
+ seen,
552
+ `${path}.${key}`,
553
+ strictNumbers
554
+ );
555
+ }
556
+ return out;
557
+ } finally {
558
+ seen.delete(value);
559
+ }
560
+ }
561
+ function stableJsonString(value) {
562
+ return JSON.stringify(sortedJsonValue(value));
563
+ }
564
+ function strictStableJsonString(value) {
565
+ return JSON.stringify(sortedJsonValue(value, /* @__PURE__ */ new Set(), "$", true));
566
+ }
567
+ function jsonCodecFor() {
568
+ const encoder = new TextEncoder();
569
+ const decoder = new TextDecoder();
570
+ return {
571
+ encode(value) {
572
+ return encoder.encode(stableJsonString(value));
573
+ },
574
+ decode(bytes) {
575
+ return JSON.parse(decoder.decode(bytes));
576
+ }
577
+ };
578
+ }
579
+ var jsonCodec = jsonCodecFor();
580
+ function bytesEqual(a, b) {
581
+ if (a.byteLength !== b.byteLength) return false;
582
+ for (let i = 0; i < a.byteLength; i += 1) {
583
+ if (a[i] !== b[i]) return false;
584
+ }
585
+ return true;
586
+ }
587
+ function hasUnpairedSurrogate(value) {
588
+ for (let i = 0; i < value.length; i += 1) {
589
+ const code = value.charCodeAt(i);
590
+ if (code >= 55296 && code <= 56319) {
591
+ const next = value.charCodeAt(i + 1);
592
+ if (next >= 56320 && next <= 57343) {
593
+ i += 1;
594
+ continue;
595
+ }
596
+ return true;
597
+ }
598
+ if (code >= 56320 && code <= 57343) return true;
599
+ }
600
+ return false;
601
+ }
602
+ function assertNoUnpairedSurrogates(value, seen = /* @__PURE__ */ new Set(), path = "$") {
603
+ if (typeof value === "string") {
604
+ if (hasUnpairedSurrogate(value)) {
605
+ throw new TypeError(`strictJsonCodec: unpaired surrogate at ${path}`);
606
+ }
607
+ return;
608
+ }
609
+ if (value === null || typeof value !== "object") return;
610
+ if (seen.has(value)) return;
611
+ seen.add(value);
612
+ try {
613
+ if (Array.isArray(value)) {
614
+ for (let i = 0; i < value.length; i += 1) {
615
+ assertNoUnpairedSurrogates(value[i], seen, `${path}[${i}]`);
616
+ }
617
+ return;
618
+ }
619
+ for (const key of Object.keys(value)) {
620
+ if (hasUnpairedSurrogate(key)) {
621
+ throw new TypeError(`strictJsonCodec: unpaired surrogate at ${path}.${key}`);
622
+ }
623
+ assertNoUnpairedSurrogates(value[key], seen, `${path}.${key}`);
624
+ }
625
+ } finally {
626
+ seen.delete(value);
627
+ }
628
+ }
629
+ function assertNoDuplicateJsonObjectKeys(text) {
630
+ let index = 0;
631
+ function fail(message) {
632
+ throw new TypeError(`strictJsonCodec: ${message}`);
633
+ }
634
+ function skipWhitespace() {
635
+ while (/\s/.test(text[index] ?? "")) index += 1;
636
+ }
637
+ function readJsonString() {
638
+ const start = index;
639
+ index += 1;
640
+ while (index < text.length) {
641
+ const ch = text[index];
642
+ if (ch === '"') {
643
+ index += 1;
644
+ try {
645
+ return JSON.parse(text.slice(start, index));
646
+ } catch {
647
+ fail("malformed JSON string");
648
+ }
649
+ }
650
+ if (ch === "\\") {
651
+ index += 2;
652
+ continue;
653
+ }
654
+ index += 1;
655
+ }
656
+ fail("unterminated JSON string");
657
+ }
658
+ function consumeLiteral(literal) {
659
+ if (text.slice(index, index + literal.length) !== literal) {
660
+ fail(`malformed JSON near byte ${index}`);
661
+ }
662
+ index += literal.length;
663
+ }
664
+ function consumeNumber() {
665
+ const match = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text.slice(index));
666
+ if (!match) fail(`malformed JSON number near byte ${index}`);
667
+ index += match[0].length;
668
+ }
669
+ function parseValue(path) {
670
+ skipWhitespace();
671
+ const ch = text[index];
672
+ if (ch === "{") {
673
+ parseObject(path);
674
+ return;
675
+ }
676
+ if (ch === "[") {
677
+ parseArray(path);
678
+ return;
679
+ }
680
+ if (ch === '"') {
681
+ readJsonString();
682
+ return;
683
+ }
684
+ if (ch === "t") {
685
+ consumeLiteral("true");
686
+ return;
687
+ }
688
+ if (ch === "f") {
689
+ consumeLiteral("false");
690
+ return;
691
+ }
692
+ if (ch === "n") {
693
+ consumeLiteral("null");
694
+ return;
695
+ }
696
+ if (ch === "-" || ch !== void 0 && ch >= "0" && ch <= "9") {
697
+ consumeNumber();
698
+ return;
699
+ }
700
+ fail(`malformed JSON near byte ${index}`);
701
+ }
702
+ function parseObject(path) {
703
+ const keys = /* @__PURE__ */ new Set();
704
+ index += 1;
705
+ skipWhitespace();
706
+ if (text[index] === "}") {
707
+ index += 1;
708
+ return;
709
+ }
710
+ while (index < text.length) {
711
+ skipWhitespace();
712
+ if (text[index] !== '"') fail(`expected object key near byte ${index}`);
713
+ const key = readJsonString();
714
+ if (keys.has(key)) {
715
+ throw new TypeError(
716
+ `strictJsonCodec: duplicate object key ${JSON.stringify(key)} at ${path}`
717
+ );
718
+ }
719
+ keys.add(key);
720
+ skipWhitespace();
721
+ if (text[index] !== ":") fail(`expected ':' after object key near byte ${index}`);
722
+ index += 1;
723
+ parseValue(`${path}.${key}`);
724
+ skipWhitespace();
725
+ if (text[index] === ",") {
726
+ index += 1;
727
+ continue;
728
+ }
729
+ if (text[index] === "}") {
730
+ index += 1;
731
+ return;
732
+ }
733
+ fail(`expected ',' or '}' near byte ${index}`);
734
+ }
735
+ fail("unterminated JSON object");
736
+ }
737
+ function parseArray(path) {
738
+ index += 1;
739
+ skipWhitespace();
740
+ if (text[index] === "]") {
741
+ index += 1;
742
+ return;
743
+ }
744
+ let item = 0;
745
+ while (index < text.length) {
746
+ parseValue(`${path}[${item}]`);
747
+ item += 1;
748
+ skipWhitespace();
749
+ if (text[index] === ",") {
750
+ index += 1;
751
+ continue;
752
+ }
753
+ if (text[index] === "]") {
754
+ index += 1;
755
+ return;
756
+ }
757
+ fail(`expected ',' or ']' near byte ${index}`);
758
+ }
759
+ fail("unterminated JSON array");
760
+ }
761
+ parseValue("$");
762
+ skipWhitespace();
763
+ if (index !== text.length) fail(`trailing JSON token near byte ${index}`);
764
+ }
765
+ function strictJsonCodecFor() {
766
+ const encoder = new TextEncoder();
767
+ const decoder = new TextDecoder("utf-8", { fatal: true });
768
+ return {
769
+ encode(value) {
770
+ assertNoUnpairedSurrogates(value);
771
+ return encoder.encode(strictStableJsonString(value));
772
+ },
773
+ decode(bytes) {
774
+ const text = decoder.decode(bytes);
775
+ assertNoDuplicateJsonObjectKeys(text);
776
+ const decoded = JSON.parse(text);
777
+ assertNoUnpairedSurrogates(decoded);
778
+ const canonical = encoder.encode(strictStableJsonString(decoded));
779
+ if (!bytesEqual(bytes, canonical)) {
780
+ throw new TypeError("strictJsonCodec: bytes are not canonical stable JSON");
781
+ }
782
+ return decoded;
783
+ }
784
+ };
785
+ }
786
+ var strictJsonCodec = strictJsonCodecFor();
787
+ function strictCanonicalJsonBytes(value) {
788
+ return strictJsonCodec.encode(value);
789
+ }
790
+
791
+ // src/node/versioning.ts
792
+ var ABSENT_V1_SEED = Object.freeze({
793
+ "@graphrefly/node-version": "v1-absent"
794
+ });
795
+ function fnv1a64(input) {
796
+ let hash = 0xcbf29ce484222325n;
797
+ const prime = 0x100000001b3n;
798
+ for (const byte of input) {
799
+ hash ^= BigInt(byte);
800
+ hash = BigInt.asUintN(64, hash * prime);
801
+ }
802
+ return hash.toString(16).padStart(16, "0");
803
+ }
804
+ function defaultNodeVersionHash(bytes) {
805
+ return `fnv1a64:${fnv1a64(bytes)}`;
806
+ }
807
+ function computeV1Cid(policy, value) {
808
+ return policy.hash(strictCanonicalJsonBytes(value));
809
+ }
810
+ function assertNodeVersionDataCompatible(policy, value) {
811
+ if (!policy.enabled || policy.level === 0) return;
812
+ strictCanonicalJsonBytes(value);
813
+ }
814
+ function snapshotNodeVersionData(policy, value) {
815
+ if (!policy.enabled || policy.level === 0) return value;
816
+ const bytes = strictCanonicalJsonBytes(value);
817
+ return strictJsonCodec.decode(bytes);
818
+ }
819
+ function resolveNodeVersioningPolicy(policy) {
820
+ if (policy === false) return { enabled: false };
821
+ if (policy === void 0 || policy === 0) return { enabled: true, level: 0 };
822
+ if (policy === 1) return { enabled: true, level: 1, hash: defaultNodeVersionHash };
823
+ if (typeof policy === "object" && policy !== null) {
824
+ if (policy.level === 0) return { enabled: true, level: 0 };
825
+ if (policy.level === 1) {
826
+ return { enabled: true, level: 1, hash: policy.hash ?? defaultNodeVersionHash };
827
+ }
828
+ }
829
+ throw new Error("node: versioning level must be 0 or 1; V2/V3 are not locked yet (D109)");
830
+ }
831
+ function createNodeVersion(policy, initialValue = ABSENT_V1_SEED) {
832
+ if (!policy.enabled) return void 0;
833
+ if (policy.level === 0) return Object.freeze({ level: 0, counter: 0 });
834
+ return Object.freeze({
835
+ level: 1,
836
+ counter: 0,
837
+ cid: computeV1Cid(policy, initialValue),
838
+ prev: null
839
+ });
840
+ }
841
+ function advanceNodeVersion(current, policy, value) {
842
+ if (!policy.enabled) return void 0;
843
+ if (current === void 0) return createNodeVersion(policy, value);
844
+ if (policy.level === 0) {
845
+ return Object.freeze({ level: 0, counter: current.counter + 1 });
846
+ }
847
+ const previous = current.level === 1 ? current.cid : null;
848
+ return Object.freeze({
849
+ level: 1,
850
+ counter: current.counter + 1,
851
+ cid: computeV1Cid(policy, value),
852
+ prev: previous
853
+ });
854
+ }
855
+ function cloneNodeVersion(version) {
856
+ if (version === void 0) return void 0;
857
+ if (version.level === 0) return Object.freeze({ level: 0, counter: version.counter });
858
+ return Object.freeze({
859
+ level: 1,
860
+ counter: version.counter,
861
+ cid: version.cid,
862
+ prev: version.prev
863
+ });
864
+ }
865
+ function restoredV1Cid(policy, hasData, cache) {
866
+ return computeV1Cid(policy, hasData ? cache : ABSENT_V1_SEED);
867
+ }
868
+
869
+ // src/node/node.ts
870
+ var constructingCore;
871
+ var constructingEnvironment;
872
+ var ownerTokens = /* @__PURE__ */ new WeakMap();
873
+ var topologyDepsChangedObservers = /* @__PURE__ */ new WeakMap();
874
+ var checkpointReaders = /* @__PURE__ */ new WeakMap();
875
+ var restoreWriters = /* @__PURE__ */ new WeakMap();
876
+ var runtimeReleasers = /* @__PURE__ */ new WeakMap();
877
+ var runtimeQuiescenceReaders = /* @__PURE__ */ new WeakMap();
878
+ var subscriberCountReaders = /* @__PURE__ */ new WeakMap();
879
+ var activationReaders = /* @__PURE__ */ new WeakMap();
880
+ var releasedNodes = /* @__PURE__ */ new WeakSet();
881
+ function withNodeCore(core, create) {
882
+ const prev = constructingCore;
883
+ constructingCore = core;
884
+ try {
885
+ return create();
886
+ } finally {
887
+ constructingCore = prev;
888
+ }
889
+ }
890
+ function withEnvironmentDrivers(environment, create) {
891
+ const prev = constructingEnvironment;
892
+ constructingEnvironment = environment;
893
+ try {
894
+ return create();
895
+ } finally {
896
+ constructingEnvironment = prev;
897
+ }
898
+ }
899
+ function getNodeOwner(n) {
900
+ return ownerTokens.get(n);
901
+ }
902
+ function notifyTopologyDepsChanged(node2, prevDeps, deps) {
903
+ topologyDepsChangedObservers.get(node2)?.(node2, prevDeps, deps);
904
+ }
905
+ function releaseRuntimeOfNode(n) {
906
+ runtimeReleasers.get(n)?.();
907
+ }
908
+ function terminalView(t) {
909
+ return t === void 0 ? false : t;
910
+ }
911
+ function validateDownPayloads(msgs) {
912
+ for (const m of msgs) {
913
+ if (m[0] === "DATA" && m[1] === void 0) {
914
+ throw new Error("down: DATA requires a non-SENTINEL payload (R-data-payload)");
915
+ }
916
+ if (m[0] === "ERROR" && isInvalidErrorPayload(m[1])) {
917
+ throw new Error("down: ERROR requires a non-SENTINEL, non-boolean payload (R-data-payload)");
918
+ }
919
+ }
920
+ }
921
+ var Node = class {
922
+ _core;
923
+ _id;
924
+ _slot;
925
+ _dep;
926
+ _value;
927
+ _wave;
928
+ _control;
929
+ _lifecycle;
930
+ _privateState;
931
+ _hooks;
932
+ _syncCtxState;
933
+ _version;
934
+ _restoredActivationPending = false;
935
+ _released = false;
936
+ get _syncCtx() {
937
+ return this._syncCtxState.value;
938
+ }
939
+ set _syncCtx(ctx) {
940
+ this._syncCtxState.value = ctx;
941
+ }
942
+ constructor(deps, handleOrFn, opts = {}) {
943
+ const core = constructingCore;
944
+ constructingCore = void 0;
945
+ const dispatcher = opts.dispatcher ?? defaultDispatcher;
946
+ const environment = constructingEnvironment ?? EnvironmentDrivers.empty();
947
+ constructingEnvironment = void 0;
948
+ const pool = opts.pool ?? "sync";
949
+ const pausable = opts.pausable ?? true;
950
+ const pullLock = opts.pullId;
951
+ const pull = opts.pullId !== void 0;
952
+ if (pull && pausable === false)
953
+ throw new Error(
954
+ "node: pullId is incompatible with pausable:false \u2014 a pull node is demanded via RESUME, which pausable:false ignores (R-pull / R-pause-modes / D55,D59)"
955
+ );
956
+ let handle;
957
+ if (handleOrFn === null) handle = null;
958
+ else if (typeof handleOrFn === "function") handle = dispatcher.register(handleOrFn, pool);
959
+ else handle = handleOrFn;
960
+ const n = deps.length;
961
+ const dep = makeDepBookkeeping(n);
962
+ const versioning = resolveNodeVersioningPolicy(opts.versioning);
963
+ const value = {
964
+ cache: SENTINEL,
965
+ hasData: false,
966
+ status: "sentinel",
967
+ terminal: void 0,
968
+ hasTorndown: false,
969
+ replayRing: []
970
+ };
971
+ if (opts.initial !== void 0) {
972
+ value.cache = opts.initial;
973
+ value.hasData = true;
974
+ value.status = "settled";
975
+ }
976
+ const pauseLockset = /* @__PURE__ */ new Set();
977
+ if (pull) pauseLockset.add(pullLock);
978
+ this._core = core ?? new NodeCore();
979
+ const created = this._core.createSlot(
980
+ {
981
+ deps,
982
+ handle,
983
+ pool,
984
+ dispatcher,
985
+ environment,
986
+ partial: opts.partial ?? false,
987
+ terminalAsRealInput: opts.terminalAsRealInput ?? false,
988
+ completeWhenDepsComplete: opts.completeWhenDepsComplete ?? true,
989
+ errorWhenDepsError: opts.errorWhenDepsError ?? true,
990
+ resubscribable: opts.resubscribable ?? false,
991
+ resetOnTeardown: opts.resetOnTeardown ?? false,
992
+ pausable,
993
+ pull,
994
+ pullLock,
995
+ replayN: opts.replayBuffer ?? 0,
996
+ dynamic: opts.dynamic ?? false,
997
+ name: opts.name,
998
+ factory: opts.factory
999
+ },
1000
+ {
1001
+ dep,
1002
+ lifecycle: { subscribers: /* @__PURE__ */ new Set(), activated: false },
1003
+ value,
1004
+ wave: {
1005
+ pending: 0,
1006
+ hasCalledFnOnce: false,
1007
+ emittedDirtyThisWave: false,
1008
+ emittedSettleThisWave: false,
1009
+ insideRunWave: false,
1010
+ inDepMutation: false,
1011
+ rewireRunPending: false,
1012
+ batchDirtyOwed: false
1013
+ },
1014
+ control: {
1015
+ pauseLockset,
1016
+ pausedDepWaveOccurred: false,
1017
+ pauseBuffer: [],
1018
+ demandOwed: false,
1019
+ inDeliverDemand: false
1020
+ },
1021
+ privateState: { value: SENTINEL, persist: false },
1022
+ hooks: { onDeactivation: [], onInvalidate: [] },
1023
+ syncCtx: { value: null },
1024
+ version: {
1025
+ policy: versioning,
1026
+ value: createNodeVersion(
1027
+ versioning,
1028
+ opts.initial !== void 0 ? opts.initial : void 0
1029
+ )
1030
+ }
1031
+ }
1032
+ );
1033
+ this._id = created.id;
1034
+ this._slot = this._core.get(this._id);
1035
+ this._dep = this._core.getDep(this._id);
1036
+ this._value = this._core.getValue(this._id);
1037
+ this._wave = this._core.getWave(this._id);
1038
+ this._control = this._core.getControl(this._id);
1039
+ this._lifecycle = this._core.getLifecycle(this._id);
1040
+ this._privateState = this._core.getPrivateState(this._id);
1041
+ this._hooks = this._core.getHooks(this._id);
1042
+ this._syncCtxState = this._core.getSyncCtx(this._id);
1043
+ this._version = this._core.getVersion(this._id);
1044
+ checkpointReaders.set(this, () => ({
1045
+ cache: this._value.cache,
1046
+ hasData: this._value.hasData,
1047
+ terminal: this._value.terminal,
1048
+ activated: this._lifecycle.activated,
1049
+ hasCalledFnOnce: this._wave.hasCalledFnOnce,
1050
+ ctxState: {
1051
+ value: this._privateState.value,
1052
+ persist: this._privateState.persist
1053
+ },
1054
+ version: cloneNodeVersion(this._version.value),
1055
+ handle: this._slot.handle
1056
+ }));
1057
+ restoreWriters.set(this, (state) => {
1058
+ this._assertNotReleased("restoreGraph");
1059
+ this._value.cache = state.cache;
1060
+ this._value.hasData = state.hasData;
1061
+ this._value.status = state.status;
1062
+ this._value.terminal = state.terminal;
1063
+ this._value.hasTorndown = false;
1064
+ this._value.replayRing = [];
1065
+ this._wave.hasCalledFnOnce = state.hasCalledFnOnce;
1066
+ this._wave.emittedDirtyThisWave = false;
1067
+ this._wave.emittedSettleThisWave = false;
1068
+ this._wave.pending = 0;
1069
+ this._wave.insideRunWave = false;
1070
+ this._wave.inDepMutation = false;
1071
+ this._wave.rewireRunPending = false;
1072
+ this._wave.batchDirtyOwed = false;
1073
+ this._control.pauseBuffer = [];
1074
+ this._control.pausedDepWaveOccurred = false;
1075
+ this._control.demandOwed = false;
1076
+ this._control.inDeliverDemand = false;
1077
+ this._control.pauseLockset.clear();
1078
+ if (this._slot.pull) this._control.pauseLockset.add(this._slot.pullLock);
1079
+ this._privateState.value = state.ctxState.value;
1080
+ this._privateState.persist = state.ctxState.persist;
1081
+ if (state.version === false) {
1082
+ this._version.policy = { enabled: false };
1083
+ this._version.value = void 0;
1084
+ } else if (state.version.level === 0) {
1085
+ this._version.policy = { enabled: true, level: 0 };
1086
+ this._version.value = cloneNodeVersion(state.version);
1087
+ } else {
1088
+ if (!this._version.policy.enabled || this._version.policy.level !== 1) {
1089
+ throw new Error(
1090
+ `restoreGraph: checkpoint node version level ${state.version.level} requires matching node versioning policy`
1091
+ );
1092
+ }
1093
+ if (!state.hasData && state.version.counter > 0) {
1094
+ throw new Error(
1095
+ "restoreGraph: checkpoint node version cid cannot be verified without current DATA under V1 versioning (D109)"
1096
+ );
1097
+ }
1098
+ const expectedCid = restoredV1Cid(this._version.policy, state.hasData, state.cache);
1099
+ if (expectedCid !== state.version.cid) {
1100
+ throw new Error(
1101
+ "restoreGraph: checkpoint node version cid does not match the selected node versioning hash policy (D109)"
1102
+ );
1103
+ }
1104
+ this._version.value = cloneNodeVersion(state.version);
1105
+ }
1106
+ this._syncCtx = null;
1107
+ this._resetDepState();
1108
+ this._lifecycle.activated = false;
1109
+ this._lifecycle.subscribers.clear();
1110
+ this._restoredActivationPending = true;
1111
+ });
1112
+ runtimeReleasers.set(this, () => this._releaseRuntime());
1113
+ runtimeQuiescenceReaders.set(this, () => this._isRuntimeQuiescentForRelease());
1114
+ subscriberCountReaders.set(this, () => this._subscriberCount());
1115
+ activationReaders.set(this, () => this._lifecycle.activated);
1116
+ }
1117
+ /** R-pull (D55): true while a pull node is quiet (holds its own pullId/demand lock). */
1118
+ _isPullQuiet() {
1119
+ return this._slot.pull && this._control.pauseLockset.has(this._slot.pullLock);
1120
+ }
1121
+ /**
1122
+ * R-pull (D55/D59): this pull node's pullId (pure data, like {@link cache}/{@link handle} — never
1123
+ * triggers computation). A consumer demands one delivery by cone-routing a RESUME of it (no node
1124
+ * reference): `ctx.up([["RESUME", pullId]])` (immediate; loops back → D37 for a self-read dep) or
1125
+ * `ctx.upNext([["RESUME", pullId]])` (boundary-deferred self-demand, R-up-routing/R-rewire-deferred).
1126
+ * Undefined for a non-pull node. The author writes the pullId verbatim, so this getter is mainly
1127
+ * for inspection/describe; routing matches by the pullId value carried in the RESUME, by identity.
1128
+ */
1129
+ get pullId() {
1130
+ return this._slot.pullLock;
1131
+ }
1132
+ get cache() {
1133
+ return this._value.cache;
1134
+ }
1135
+ get status() {
1136
+ return this._value.status;
1137
+ }
1138
+ get version() {
1139
+ return cloneNodeVersion(this._version.value);
1140
+ }
1141
+ get name() {
1142
+ return this._slot.name;
1143
+ }
1144
+ /** R-describe/D51: real factory name for a standalone graph-less node (a runtime *Map inner). */
1145
+ get factory() {
1146
+ return this._slot.factory;
1147
+ }
1148
+ /**
1149
+ * The node's CURRENT/LIVE deps (R-describe / R-edges-derived / D51) — readonly view of the
1150
+ * live `_deps`, which a rewire (C-8 / C-11) mutates. The graph's describe() reads this (NOT a
1151
+ * construction-time snapshot) so every edge corresponds to a real current subscription (D3).
1152
+ * Inspection-only, like cache/status; never triggers computation.
1153
+ */
1154
+ get deps() {
1155
+ return this._slot.deps;
1156
+ }
1157
+ /**
1158
+ * The fn handle (pure data `(poolId, handleId)`, D7) or null for state/passthrough
1159
+ * nodes. Inspection-only (L1.6 handle is referenceable/inspectable) — lets the graph
1160
+ * layer key a dispatcher-backed profile recorder WITHOUT putting counters on the node
1161
+ * (R-node-thin / D39).
1162
+ */
1163
+ get handle() {
1164
+ return this._slot.handle;
1165
+ }
1166
+ /** R-push-subscribe: a new sink receives START, then cached DATA (or DIRTY if dirty). */
1167
+ subscribe(sink) {
1168
+ this._assertNotReleased("subscribe");
1169
+ enterWave();
1170
+ try {
1171
+ if (this._value.terminal !== void 0) {
1172
+ if (this._slot.resubscribable) {
1173
+ this._restoredActivationPending = false;
1174
+ this._resetLifecycle();
1175
+ } else
1176
+ throw new Error(
1177
+ "subscribe: node is non-resubscribable and has terminated; the stream is permanently over (R-terminal / R2.2.7.b)"
1178
+ );
1179
+ }
1180
+ this._lifecycle.subscribers.add(sink);
1181
+ sink(["START"]);
1182
+ if (this._slot.replayN > 0 && this._value.replayRing.length > 0) {
1183
+ for (const v of this._value.replayRing) sink(["DATA", v]);
1184
+ } else if (this._value.hasData && !this._slot.pull) {
1185
+ sink(["DATA", this._value.cache]);
1186
+ } else if (this._value.status === "dirty" && !this._slot.pull) {
1187
+ sink(["DIRTY"]);
1188
+ }
1189
+ if (!this._lifecycle.activated) this._activate();
1190
+ return () => {
1191
+ if (!this._lifecycle.subscribers.delete(sink)) return;
1192
+ if (this._lifecycle.subscribers.size === 0) this._deactivate();
1193
+ };
1194
+ } finally {
1195
+ exitWave();
1196
+ }
1197
+ }
1198
+ /** External emission toward sinks (state-node push, or async late-emit). One call = one wave. */
1199
+ down(msgs) {
1200
+ this._assertNotReleased("down");
1201
+ enterWave();
1202
+ try {
1203
+ this._down(msgs);
1204
+ } finally {
1205
+ exitWave();
1206
+ }
1207
+ }
1208
+ /**
1209
+ * Emit upstream toward deps — control tiers only (R-ctx-up). `towardDep` (a dep index) routes up
1210
+ * ONE declared edge (R-up-routing directed-up); omitted = broadcast up all deps.
1211
+ */
1212
+ up(msgs, towardDep) {
1213
+ this._assertNotReleased("up");
1214
+ enterWave();
1215
+ try {
1216
+ this._up(msgs, towardDep);
1217
+ } finally {
1218
+ exitWave();
1219
+ }
1220
+ }
1221
+ // ── deferred self-rewire (R-rewire-deferred / D47): ctx.rewireNext drain support ──
1222
+ /**
1223
+ * Enqueue a deferred self-rewire op (issued from this node's fn via `ctx.rewireNext`).
1224
+ * Applied at the committed wave boundary (boundary.ts drain), never in place — the in-fn
1225
+ * immediate path (`subscribeDep`/`replaceDeps`/`unsubscribeDep`) still throws mid-run (D37/R-reentrancy).
1226
+ */
1227
+ _requestRewireNext(op) {
1228
+ deferRewire(this._core, () => this._applyRewireNext(op), {
1229
+ batchToken: currentBoundaryBatchToken(),
1230
+ isReady: () => !this._hasBoundaryPauseLock()
1231
+ });
1232
+ }
1233
+ /**
1234
+ * Enqueue a deferred up-going control wave (R-up-routing / R-pull / D59): at the committed wave
1235
+ * boundary, route `msgs` up from THIS node (broadcast, or up the single `towardDep` edge). The
1236
+ * deferred form of `ctx.up` — the SELF-demand path: a consumer issues `ctx.upNext([[RESUME,
1237
+ * pullId]])` to demand a dep it ALSO reads; the demand applies at the boundary (not mid-fn), so
1238
+ * the pull node's delivery loops back as a FRESH wave rather than re-entering the consumer (D37 /
1239
+ * R-reentrancy). Rides the same R-rewire-deferred (D47) drain as ctx.rewireNext.
1240
+ */
1241
+ _requestUpNext(msgs, towardDep) {
1242
+ deferRewire(
1243
+ this._core,
1244
+ () => {
1245
+ if (!this._released) this._up(msgs, towardDep);
1246
+ },
1247
+ {
1248
+ batchToken: currentBoundaryBatchToken(),
1249
+ isReady: () => !this._hasBoundaryPauseLock()
1250
+ }
1251
+ );
1252
+ }
1253
+ /** Apply one queued self-rewire at the boundary (drain thunk). */
1254
+ _applyRewireNext(op) {
1255
+ if (this._released) return;
1256
+ try {
1257
+ if (op.kind === "add") {
1258
+ const next = this._slot.deps.includes(op.dep) ? [...this._slot.deps] : [...this._slot.deps, op.dep];
1259
+ this._rewire(next, op.fn, { allowTerminalOwner: true });
1260
+ } else if (op.kind === "remove") {
1261
+ this._rewire(
1262
+ this._slot.deps.filter((d) => d !== op.dep),
1263
+ op.fn,
1264
+ { allowTerminalOwner: true }
1265
+ );
1266
+ } else {
1267
+ this._rewire(this._dedupDeps(op.deps), op.fn, { allowTerminalOwner: true });
1268
+ }
1269
+ } catch (e) {
1270
+ this._down([["ERROR", errorPayload(e, "rewireNext op failed")]]);
1271
+ }
1272
+ }
1273
+ // ── rewire (R-rewire / D42): intra-graph runtime topology mutation ──
1274
+ /**
1275
+ * Replace this node's deps atomically (surgical, Option-C). Requires an explicit
1276
+ * `fn` (SD-1 fn-deps pairing — user fns read dep input positionally). Kept deps
1277
+ * keep their subscription + per-dep state; only removed deps unsubscribe and only
1278
+ * added deps fresh-subscribe (push-on-subscribe for an added cached dep). The
1279
+ * first-run gate and cache are PRESERVED (R-rewire Q2/Q7). Intra-graph only (D22).
1280
+ */
1281
+ replaceDeps(newDeps, fn) {
1282
+ this._assertNotReleased("replaceDeps");
1283
+ this._rewire(this._dedupDeps(newDeps), fn);
1284
+ }
1285
+ /** Subscribe to one dep (special case of replaceDeps); returns its index. fn required (SD-1). */
1286
+ subscribeDep(depNode, fn) {
1287
+ this._assertNotReleased("subscribeDep");
1288
+ const next = this._slot.deps.includes(depNode) ? [...this._slot.deps] : [...this._slot.deps, depNode];
1289
+ const deferred = this._rewire(next, fn);
1290
+ return deferred ? next.indexOf(depNode) : this._slot.deps.indexOf(depNode);
1291
+ }
1292
+ /** Unsubscribe from one dep (special case of replaceDeps); idempotent if absent (fn swap still applies). */
1293
+ unsubscribeDep(depNode, fn) {
1294
+ this._assertNotReleased("unsubscribeDep");
1295
+ this._rewire(
1296
+ this._slot.deps.filter((d) => d !== depNode),
1297
+ fn
1298
+ );
1299
+ }
1300
+ _dedupDeps(deps) {
1301
+ const seen = /* @__PURE__ */ new Set();
1302
+ const out = [];
1303
+ for (const d of deps)
1304
+ if (!seen.has(d)) {
1305
+ seen.add(d);
1306
+ out.push(d);
1307
+ }
1308
+ return out;
1309
+ }
1310
+ /** Is `target` reachable upstream from `from` (following deps)? Cycle-prevention DFS. */
1311
+ _reachableUpstream(from, target) {
1312
+ const seen = /* @__PURE__ */ new Set();
1313
+ const stack = [from];
1314
+ while (stack.length > 0) {
1315
+ const n = stack.pop();
1316
+ if (n === void 0) continue;
1317
+ if (n === target) return true;
1318
+ if (seen.has(n)) continue;
1319
+ seen.add(n);
1320
+ for (const d of n._slot.deps) stack.push(d);
1321
+ }
1322
+ return false;
1323
+ }
1324
+ _assertRewireDepOwner(dep) {
1325
+ const selfOwner = getNodeOwner(this);
1326
+ const depOwner = getNodeOwner(dep);
1327
+ if (selfOwner !== void 0 && depOwner !== void 0 && selfOwner !== depOwner) {
1328
+ throw new Error(
1329
+ "rewire: dep belongs to a different graph; cross-graph deps require a wire bridge (D22 / R-graph-domain)"
1330
+ );
1331
+ }
1332
+ }
1333
+ _rewire(newDeps, fn, opts = {}) {
1334
+ if (this._value.terminal !== void 0 && !opts.allowTerminalOwner)
1335
+ throw new Error(
1336
+ "rewire: node is terminal (completed/errored) \u2014 cannot rewire (R-rewire / D42)"
1337
+ );
1338
+ if (this._wave.insideRunWave)
1339
+ throw new Error(
1340
+ "rewire: mid-fn topology mutation \u2014 a fn mutating its own deps mid-wave is the feedback cycle (R-rewire / D37)"
1341
+ );
1342
+ if (this._wave.inDepMutation)
1343
+ throw new Error(
1344
+ "rewire: reentrant dep mutation \u2014 another replaceDeps/subscribeDep/unsubscribeDep is in flight (R-rewire)"
1345
+ );
1346
+ if (newDeps.includes(this))
1347
+ throw new Error("rewire: self-dependency rejected (R-rewire / D42)");
1348
+ const oldDeps = this._slot.deps;
1349
+ const added = newDeps.filter((d) => !oldDeps.includes(d));
1350
+ for (const d of added) {
1351
+ if (this._reachableUpstream(d, this))
1352
+ throw new Error(
1353
+ "rewire: would create a cycle \u2014 dep already transitively depends on this node (R-rewire / D42)"
1354
+ );
1355
+ if (d._value.terminal !== void 0 && !d._slot.resubscribable)
1356
+ throw new Error(
1357
+ "rewire: cannot add a non-resubscribable terminal dep \u2014 would wedge (R-rewire / D42)"
1358
+ );
1359
+ this._assertRewireDepOwner(d);
1360
+ }
1361
+ if (deferAfterBatchForTarget(this, () => {
1362
+ this._rewire(newDeps, fn, { ...opts, allowTerminalOwner: true });
1363
+ })) {
1364
+ return true;
1365
+ }
1366
+ if (!this._lifecycle.activated) this._restoredActivationPending = false;
1367
+ this._wave.inDepMutation = true;
1368
+ this._wave.rewireRunPending = false;
1369
+ let zeroDepUnDirty = false;
1370
+ try {
1371
+ const oldHandle = this._slot.handle;
1372
+ this._slot.handle = this._slot.dispatcher.register(fn, this._slot.pool);
1373
+ if (oldHandle !== null) this._slot.dispatcher.unregister(oldHandle);
1374
+ const removed = oldDeps.filter((d) => !newDeps.includes(d));
1375
+ let removedDirtyContributor = false;
1376
+ for (const d of removed) {
1377
+ const oldIdx = oldDeps.indexOf(d);
1378
+ if (this._dep.dirty[oldIdx]) {
1379
+ removedDirtyContributor = true;
1380
+ this._wave.pending--;
1381
+ }
1382
+ if (this._lifecycle.activated) {
1383
+ const box = this._dep.idxBoxes[oldIdx];
1384
+ if (box) box.v = -1;
1385
+ const unsub = this._dep.unsubs[oldIdx];
1386
+ if (unsub) unsub();
1387
+ }
1388
+ }
1389
+ const n = newDeps.length;
1390
+ const newBatch = new Array(n).fill(null);
1391
+ const newPrev = new Array(n).fill(SENTINEL);
1392
+ const newHasData = new Array(n).fill(false);
1393
+ const newDirty = new Array(n).fill(false);
1394
+ const newTier = new Array(n).fill(0);
1395
+ const newTerminal = new Array(n).fill(void 0);
1396
+ const newTerminalInput = new Array(n).fill(void 0);
1397
+ const newUnsubs = new Array(n);
1398
+ const newBoxes = new Array(n);
1399
+ for (let j = 0; j < n; j++) {
1400
+ const oldIdx = oldDeps.indexOf(newDeps[j]);
1401
+ if (oldIdx !== -1) {
1402
+ newBatch[j] = this._dep.batch[oldIdx];
1403
+ newPrev[j] = this._dep.prev[oldIdx];
1404
+ newHasData[j] = this._dep.hasData[oldIdx];
1405
+ newDirty[j] = this._dep.dirty[oldIdx];
1406
+ newTier[j] = this._dep.tier[oldIdx];
1407
+ newTerminal[j] = this._dep.terminal[oldIdx];
1408
+ newUnsubs[j] = this._dep.unsubs[oldIdx];
1409
+ const box = this._dep.idxBoxes[oldIdx];
1410
+ if (box) box.v = j;
1411
+ newBoxes[j] = box;
1412
+ }
1413
+ }
1414
+ this._slot.deps = newDeps;
1415
+ this._dep.batch = newBatch;
1416
+ this._dep.prev = newPrev;
1417
+ this._dep.hasData = newHasData;
1418
+ this._dep.dirty = newDirty;
1419
+ this._dep.tier = newTier;
1420
+ this._dep.terminal = newTerminal;
1421
+ this._dep.terminalInput = newTerminalInput;
1422
+ this._dep.unsubs = newUnsubs;
1423
+ this._dep.idxBoxes = newBoxes;
1424
+ this._dep.waveData = newDeps.map(() => []);
1425
+ this._dep.waveTokens = new Array(newDeps.length).fill(void 0);
1426
+ this._syncCtx = null;
1427
+ if (this._lifecycle.activated) {
1428
+ for (const d of added) this._subscribeDepAt(d);
1429
+ }
1430
+ notifyTopologyDepsChanged(this, oldDeps, newDeps);
1431
+ if (removedDirtyContributor && this._wave.pending === 0 && this._value.status === "dirty") {
1432
+ if (newDeps.length > 0) this._wave.rewireRunPending = true;
1433
+ else zeroDepUnDirty = true;
1434
+ }
1435
+ } finally {
1436
+ this._wave.inDepMutation = false;
1437
+ }
1438
+ if (this._wave.rewireRunPending) {
1439
+ this._wave.rewireRunPending = false;
1440
+ this._settleRewire();
1441
+ } else if (zeroDepUnDirty) {
1442
+ if (this._wave.emittedDirtyThisWave) this._down([["RESOLVED"]]);
1443
+ else this._value.status = this._value.hasData ? "settled" : "sentinel";
1444
+ }
1445
+ return false;
1446
+ }
1447
+ // ── activation / deactivation (lazy; R-rom-ram) ──
1448
+ _activate() {
1449
+ this._lifecycle.activated = true;
1450
+ const seedRestoredDeps = this._restoredActivationPending;
1451
+ this._restoredActivationPending = false;
1452
+ if (this._slot.pull) this._control.pauseLockset.add(this._slot.pullLock);
1453
+ this._dep.unsubs = new Array(this._slot.deps.length);
1454
+ this._dep.idxBoxes = new Array(this._slot.deps.length);
1455
+ for (const dep of this._slot.deps)
1456
+ this._subscribeDepAt(dep, { seedRestored: seedRestoredDeps });
1457
+ if (this._slot.deps.length === 0 && this._slot.handle !== null && !this._wave.hasCalledFnOnce) {
1458
+ this._runWave();
1459
+ }
1460
+ }
1461
+ /**
1462
+ * Subscribe to a dep. The dispatch callback reads the dep's CURRENT index from a
1463
+ * mutable box (O(1)); a surgical rewire that reorders kept deps just updates the box
1464
+ * (R-rewire Option-C / D42) — no re-subscribe, no per-message indexOf scan. A removed
1465
+ * dep's box is set to -1 so any stale in-flight callback drops (drain).
1466
+ */
1467
+ _subscribeDepAt(depNode, opts = {}) {
1468
+ const idx0 = this._slot.deps.indexOf(depNode);
1469
+ const box = { v: idx0 };
1470
+ let ignoreInitialPush = opts.seedRestored === true;
1471
+ if (ignoreInitialPush && idx0 !== -1) {
1472
+ this._seedRestoredDepAt(idx0, depNode);
1473
+ if (depNode._value.terminal !== void 0 && !depNode._slot.resubscribable) {
1474
+ this._dep.unsubs[idx0] = () => {
1475
+ };
1476
+ this._dep.idxBoxes[idx0] = box;
1477
+ return;
1478
+ }
1479
+ }
1480
+ const unsub = depNode.subscribe((msg, delivery) => {
1481
+ if (ignoreInitialPush && delivery === void 0) return;
1482
+ if (ignoreInitialPush) ignoreInitialPush = false;
1483
+ if (box.v === -1) return;
1484
+ this._receiveFromDep(box.v, msg, delivery);
1485
+ });
1486
+ if (ignoreInitialPush && idx0 !== -1 && box.v !== -1) this._seedRestoredDepAt(idx0, depNode);
1487
+ ignoreInitialPush = false;
1488
+ if (idx0 !== -1) {
1489
+ this._dep.unsubs[idx0] = unsub;
1490
+ this._dep.idxBoxes[idx0] = box;
1491
+ }
1492
+ }
1493
+ _seedRestoredDepAt(idx, depNode) {
1494
+ const seedData = depNode._value.hasData && !depNode._slot.pull;
1495
+ this._dep.batch[idx] = null;
1496
+ this._dep.waveData[idx] = [];
1497
+ this._dep.waveTokens[idx] = void 0;
1498
+ this._dep.prev[idx] = seedData ? depNode._value.cache : SENTINEL;
1499
+ this._dep.hasData[idx] = seedData;
1500
+ this._dep.dirty[idx] = false;
1501
+ this._dep.tier[idx] = seedData ? 3 : 0;
1502
+ this._dep.terminal[idx] = depNode._value.terminal;
1503
+ this._dep.terminalInput[idx] = void 0;
1504
+ }
1505
+ _deactivate() {
1506
+ this._lifecycle.activated = false;
1507
+ for (const u of this._dep.unsubs) if (u) u();
1508
+ this._dep.unsubs = [];
1509
+ this._dep.idxBoxes = [];
1510
+ for (const fn of this._hooks.onDeactivation) fn();
1511
+ this._hooks.onDeactivation = [];
1512
+ this._hooks.onInvalidate = [];
1513
+ const isCompute = this._slot.handle !== null || this._slot.deps.length > 0;
1514
+ if (isCompute) {
1515
+ this._value.cache = SENTINEL;
1516
+ this._value.hasData = false;
1517
+ this._value.status = "sentinel";
1518
+ }
1519
+ this._resetDepState();
1520
+ this._wave.hasCalledFnOnce = false;
1521
+ this._control.pauseLockset.clear();
1522
+ this._control.pauseBuffer = [];
1523
+ this._control.pausedDepWaveOccurred = false;
1524
+ this._control.demandOwed = false;
1525
+ this._value.replayRing = [];
1526
+ if (!this._privateState.persist) this._privateState.value = SENTINEL;
1527
+ }
1528
+ _assertNotReleased(op) {
1529
+ if (this._released)
1530
+ throw new Error(`${op}: node has been released from its graph lifecycle (D122)`);
1531
+ }
1532
+ _subscriberCount() {
1533
+ return this._lifecycle.subscribers.size;
1534
+ }
1535
+ _isRuntimeQuiescentForRelease() {
1536
+ const allowedPullQuietLock = this._slot.pull && this._control.pauseLockset.size === 1 && this._control.pauseLockset.has(this._slot.pullLock);
1537
+ return !this._released && this._value.status !== "dirty" && this._value.status !== "pending" && this._wave.pending === 0 && !this._wave.insideRunWave && !this._wave.inDepMutation && !this._wave.rewireRunPending && !this._wave.batchDirtyOwed && this._dep.dirty.every((dirty) => !dirty) && this._control.pauseBuffer.length === 0 && !this._control.pausedDepWaveOccurred && !this._control.demandOwed && !this._control.inDeliverDemand && (this._control.pauseLockset.size === 0 || allowedPullQuietLock);
1538
+ }
1539
+ _releaseRuntime() {
1540
+ if (this._released) return;
1541
+ this._released = true;
1542
+ releasedNodes.add(this);
1543
+ let releaseError;
1544
+ const recordReleaseError = (error) => {
1545
+ if (releaseError === void 0) releaseError = error;
1546
+ };
1547
+ this._lifecycle.activated = false;
1548
+ for (const u of this._dep.unsubs) {
1549
+ try {
1550
+ u();
1551
+ } catch (error) {
1552
+ recordReleaseError(error);
1553
+ }
1554
+ }
1555
+ for (const fn of this._hooks.onDeactivation) {
1556
+ try {
1557
+ fn();
1558
+ } catch (error) {
1559
+ recordReleaseError(error);
1560
+ }
1561
+ }
1562
+ this._dep.unsubs = [];
1563
+ this._dep.idxBoxes = [];
1564
+ this._lifecycle.subscribers.clear();
1565
+ if (this._slot.handle !== null) {
1566
+ this._slot.dispatcher.unregister(this._slot.handle);
1567
+ this._slot.handle = null;
1568
+ }
1569
+ this._slot.deps = [];
1570
+ this._dep.batch = [];
1571
+ this._dep.waveData = [];
1572
+ this._dep.waveTokens = [];
1573
+ this._dep.prev = [];
1574
+ this._dep.hasData = [];
1575
+ this._dep.dirty = [];
1576
+ this._dep.tier = [];
1577
+ this._dep.terminal = [];
1578
+ this._dep.terminalInput = [];
1579
+ this._value.cache = SENTINEL;
1580
+ this._value.hasData = false;
1581
+ this._value.status = "sentinel";
1582
+ this._value.terminal = void 0;
1583
+ this._value.replayRing = [];
1584
+ this._privateState.value = SENTINEL;
1585
+ this._privateState.persist = false;
1586
+ this._syncCtx = null;
1587
+ this._resetDepState();
1588
+ this._hooks.onDeactivation = [];
1589
+ this._hooks.onInvalidate = [];
1590
+ this._control.pauseLockset.clear();
1591
+ this._control.pauseBuffer = [];
1592
+ this._control.pausedDepWaveOccurred = false;
1593
+ this._control.demandOwed = false;
1594
+ this._restoredActivationPending = false;
1595
+ checkpointReaders.delete(this);
1596
+ restoreWriters.delete(this);
1597
+ runtimeReleasers.delete(this);
1598
+ runtimeQuiescenceReaders.delete(this);
1599
+ subscriberCountReaders.delete(this);
1600
+ activationReaders.delete(this);
1601
+ ownerTokens.delete(this);
1602
+ topologyDepsChangedObservers.delete(this);
1603
+ this._core.releaseSlot(this._id);
1604
+ if (releaseError !== void 0) throw releaseError;
1605
+ }
1606
+ _resetDepState() {
1607
+ const n = this._slot.deps.length;
1608
+ for (let i = 0; i < n; i++) {
1609
+ this._dep.batch[i] = null;
1610
+ this._dep.waveData[i] = [];
1611
+ this._dep.waveTokens[i] = void 0;
1612
+ this._dep.prev[i] = SENTINEL;
1613
+ this._dep.hasData[i] = false;
1614
+ this._dep.dirty[i] = false;
1615
+ this._dep.tier[i] = 0;
1616
+ this._dep.terminal[i] = void 0;
1617
+ this._dep.terminalInput[i] = void 0;
1618
+ }
1619
+ this._wave.pending = 0;
1620
+ this._wave.emittedDirtyThisWave = false;
1621
+ }
1622
+ // ── upstream wave receive (two-phase + diamond) ──
1623
+ _recordDepProjection(idx, delivery) {
1624
+ const token = delivery?.wave ?? {};
1625
+ if (this._dep.waveTokens[idx] !== token) {
1626
+ this._dep.waveData[idx].push([]);
1627
+ this._dep.waveTokens[idx] = token;
1628
+ }
1629
+ return this._dep.waveData[idx][this._dep.waveData[idx].length - 1];
1630
+ }
1631
+ _depProjectionHasData(idx) {
1632
+ const projection = this._dep.waveData[idx][this._dep.waveData[idx].length - 1];
1633
+ return projection?.some((v) => v !== SENTINEL) ?? false;
1634
+ }
1635
+ _receiveFromDep(idx, msg, delivery) {
1636
+ if (this._released) return;
1637
+ const t = msg[0];
1638
+ if (t === "START") return;
1639
+ const isLastInDeliveredWave = delivery?.last ?? true;
1640
+ if (this._value.terminal !== void 0) {
1641
+ if (t === "TEARDOWN") this._down([["TEARDOWN"]]);
1642
+ return;
1643
+ }
1644
+ if (t === "INVALIDATE") {
1645
+ const projection = this._recordDepProjection(idx, delivery);
1646
+ projection.push(SENTINEL);
1647
+ if (projection.some((v) => v !== SENTINEL) && isLastInDeliveredWave) this._maybeRun();
1648
+ this._dep.prev[idx] = SENTINEL;
1649
+ this._dep.hasData[idx] = false;
1650
+ this._dep.batch[idx] = null;
1651
+ if (this._dep.dirty[idx]) {
1652
+ this._dep.dirty[idx] = false;
1653
+ this._wave.pending--;
1654
+ }
1655
+ if (this._control.pausedDepWaveOccurred && this._dep.batch.every((b) => b === null)) {
1656
+ this._control.pausedDepWaveOccurred = false;
1657
+ }
1658
+ const hadData = this._value.hasData;
1659
+ this._invalidate();
1660
+ if (this._wave.pending === 0 && this._wave.emittedDirtyThisWave) {
1661
+ if (!hadData) this._down([["RESOLVED"]]);
1662
+ else this._wave.emittedDirtyThisWave = false;
1663
+ }
1664
+ this._fireOwedDemandIfReady();
1665
+ return;
1666
+ }
1667
+ if (isTerminal(t)) {
1668
+ const isError = t === "ERROR";
1669
+ const errPayload = isError ? msg[1] : void 0;
1670
+ this._dep.terminal[idx] = isError ? errPayload : true;
1671
+ this._dep.terminalInput[idx] = isError ? errPayload : true;
1672
+ this._releaseDepDirty(idx);
1673
+ const ranValueBeforeTerminal = this._depProjectionHasData(idx) && isLastInDeliveredWave;
1674
+ if (ranValueBeforeTerminal) this._maybeRun();
1675
+ if (isError && this._slot.errorWhenDepsError) {
1676
+ this._down([["ERROR", errPayload]]);
1677
+ } else if (this._slot.terminalAsRealInput) {
1678
+ if (ranValueBeforeTerminal) {
1679
+ this._fireOwedDemandIfReady();
1680
+ return;
1681
+ }
1682
+ this._maybeRun();
1683
+ } else if (this._slot.completeWhenDepsComplete && this._allDepsTerminal()) {
1684
+ this._down([["COMPLETE"]]);
1685
+ } else {
1686
+ this._settleAfterAbsorbedTerminal();
1687
+ }
1688
+ this._fireOwedDemandIfReady();
1689
+ return;
1690
+ }
1691
+ if (t === "TEARDOWN") {
1692
+ this._down([["TEARDOWN"]]);
1693
+ return;
1694
+ }
1695
+ if (t === "DIRTY") {
1696
+ if (!this._dep.dirty[idx]) {
1697
+ this._dep.dirty[idx] = true;
1698
+ this._wave.pending++;
1699
+ this._dep.tier[idx] = 2;
1700
+ this._markDirty();
1701
+ }
1702
+ return;
1703
+ }
1704
+ if (t === "DATA") {
1705
+ const v = msg[1];
1706
+ this._recordDepProjection(idx, delivery).push(v);
1707
+ const b = this._dep.batch[idx];
1708
+ if (b === null) this._dep.batch[idx] = [v];
1709
+ else b.push(v);
1710
+ this._dep.prev[idx] = v;
1711
+ this._dep.hasData[idx] = true;
1712
+ this._dep.tier[idx] = 3;
1713
+ if (this._dep.dirty[idx]) {
1714
+ this._dep.dirty[idx] = false;
1715
+ this._wave.pending--;
1716
+ }
1717
+ if (isLastInDeliveredWave) this._maybeRun();
1718
+ this._fireOwedDemandIfReady();
1719
+ return;
1720
+ }
1721
+ if (t === "RESOLVED") {
1722
+ this._recordDepProjection(idx, delivery);
1723
+ this._dep.tier[idx] = 3;
1724
+ if (this._dep.dirty[idx]) {
1725
+ this._dep.dirty[idx] = false;
1726
+ this._wave.pending--;
1727
+ }
1728
+ if (isLastInDeliveredWave) this._maybeRun();
1729
+ this._fireOwedDemandIfReady();
1730
+ return;
1731
+ }
1732
+ }
1733
+ /**
1734
+ * R-terminal-settles-dirty (B35): release a dep's outstanding in-wave DIRTY contribution.
1735
+ * A settle-class event for that dep (DATA/RESOLVED inline above, INVALIDATE, and now
1736
+ * COMPLETE/ERROR) clears its dirty flag + decrements _pending. No-op if the dep already
1737
+ * settled this wave (DATA/RESOLVED ran first) — so the normal DATA-then-COMPLETE flow is
1738
+ * unaffected. This makes the exactly-one-settle invariant a single, shared step.
1739
+ */
1740
+ _releaseDepDirty(idx) {
1741
+ if (this._dep.dirty[idx]) {
1742
+ this._dep.dirty[idx] = false;
1743
+ this._wave.pending--;
1744
+ }
1745
+ }
1746
+ /**
1747
+ * R-terminal-settles-dirty (B35): settle a node whose dirtied dep was released by an ABSORBED
1748
+ * terminal that is NOT a real input (a plain derived/effect — the common default-node case when
1749
+ * one of several deps completes while others stay live). Runs only when the release drained
1750
+ * _pending while the node still owes a downstream settle (it broadcast DIRTY this wave):
1751
+ * - if some OTHER dep delivered real DATA this wave (a value occurred) → recompute (→ DATA);
1752
+ * - else the terminal's signalled change did not materialise (no value) → one undirty RESOLVED
1753
+ * (R-resolved-undirty), keeping the cache (a terminal, unlike INVALIDATE, leaves the value).
1754
+ * A terminalAsRealInput node instead recomputes unconditionally (its fn reads the terminal).
1755
+ */
1756
+ _settleAfterAbsorbedTerminal() {
1757
+ if (this._wave.pending !== 0 || !this._wave.emittedDirtyThisWave) return;
1758
+ const sawData = this._dep.batch.some((b) => b !== null && b.length > 0);
1759
+ if (sawData) this._maybeRun();
1760
+ if (this._wave.emittedDirtyThisWave) this._down([["RESOLVED"]]);
1761
+ }
1762
+ _markDirty() {
1763
+ this._value.status = "dirty";
1764
+ if (this._isPullQuiet()) return;
1765
+ if (!this._wave.emittedDirtyThisWave) {
1766
+ this._wave.emittedDirtyThisWave = true;
1767
+ this._emitToSubs(["DIRTY"]);
1768
+ }
1769
+ }
1770
+ _maybeRun() {
1771
+ if (this._wave.inDepMutation) {
1772
+ this._wave.rewireRunPending = true;
1773
+ return;
1774
+ }
1775
+ if (this._slot.pausable === true && this._isPaused()) {
1776
+ this._control.pausedDepWaveOccurred = true;
1777
+ return;
1778
+ }
1779
+ this._tryRun();
1780
+ }
1781
+ /**
1782
+ * R-rewire atomic settle (after a rewire that warrants a recompute): emit a proper
1783
+ * two-phase DIRTY→DATA wave (R-dirty-before-data — a rewire-triggered settle is a wave),
1784
+ * once, with every added dep already wired. Mirrors _maybeRun/_tryRun's pause + gate +
1785
+ * pending guards, then injects the phase-1 DIRTY the added dep's [START,DATA] handshake
1786
+ * did not carry.
1787
+ */
1788
+ _settleRewire() {
1789
+ if (this._slot.pausable === true && this._isPaused()) {
1790
+ this._control.pausedDepWaveOccurred = true;
1791
+ return;
1792
+ }
1793
+ if (this._wave.pending > 0) return;
1794
+ if (this._slot.handle === null) {
1795
+ this._passthroughEmit();
1796
+ return;
1797
+ }
1798
+ if (!this._wave.hasCalledFnOnce && !(this._slot.partial || this._allDepsSettled())) return;
1799
+ this._markDirty();
1800
+ this._runWave();
1801
+ }
1802
+ _tryRun() {
1803
+ if (this._wave.pending > 0) return;
1804
+ if (this._slot.handle === null) {
1805
+ this._passthroughEmit();
1806
+ return;
1807
+ }
1808
+ if (!this._wave.hasCalledFnOnce) {
1809
+ if (this._slot.partial || this._allDepsSettled()) this._runWave();
1810
+ return;
1811
+ }
1812
+ this._runWave();
1813
+ }
1814
+ _allDepsSettled() {
1815
+ for (let i = 0; i < this._slot.deps.length; i++) {
1816
+ if (this._dep.hasData[i]) continue;
1817
+ if (this._slot.terminalAsRealInput && this._dep.terminal[i] !== void 0) continue;
1818
+ return false;
1819
+ }
1820
+ return true;
1821
+ }
1822
+ _passthroughEmit() {
1823
+ const b = this._dep.batch[0];
1824
+ if (b !== null && b.length > 0) {
1825
+ this._down([["DATA", b[b.length - 1]]]);
1826
+ } else if (this._wave.emittedDirtyThisWave) {
1827
+ this._down([["RESOLVED"]]);
1828
+ }
1829
+ this._dep.batch[0] = null;
1830
+ this._wave.emittedDirtyThisWave = false;
1831
+ }
1832
+ _runWave() {
1833
+ if (this._wave.insideRunWave)
1834
+ throw new Error(
1835
+ "synchronous feedback cycle: node fn re-entered its own wave (R-reentrancy / D37)"
1836
+ );
1837
+ this._wave.hasCalledFnOnce = true;
1838
+ this._hooks.onInvalidate = [];
1839
+ this._hooks.onDeactivation = [];
1840
+ const ctx = this._buildCtx();
1841
+ const wasDirty = this._wave.emittedDirtyThisWave;
1842
+ this._wave.emittedSettleThisWave = false;
1843
+ this._wave.insideRunWave = true;
1844
+ try {
1845
+ this._slot.dispatcher.invoke(this._slot.handle, ctx);
1846
+ } finally {
1847
+ this._wave.insideRunWave = false;
1848
+ }
1849
+ if (wasDirty && !this._wave.emittedSettleThisWave && this._value.terminal === void 0 && !this._isAsyncPool()) {
1850
+ this._down([["RESOLVED"]]);
1851
+ }
1852
+ for (let i = 0; i < this._dep.batch.length; i++) {
1853
+ this._dep.batch[i] = null;
1854
+ this._dep.waveData[i] = [];
1855
+ this._dep.waveTokens[i] = void 0;
1856
+ this._dep.terminalInput[i] = void 0;
1857
+ }
1858
+ this._wave.emittedDirtyThisWave = false;
1859
+ }
1860
+ // ── ctx construction (L3-Q5: sync = node-stable reused ctx; async = per-invocation) ──
1861
+ _buildCtx() {
1862
+ const kind = this._slot.handle ? this._slot.dispatcher.poolKind(this._slot.handle.poolId) : "sync";
1863
+ if (kind === "sync") {
1864
+ if (this._syncCtx === null) this._syncCtx = this._makeCtx();
1865
+ this._refreshCtx(this._syncCtx);
1866
+ return this._syncCtx;
1867
+ }
1868
+ return this._makeCtx({
1869
+ waveData: this._dep.waveData.map((waves) => waves.map((w) => [...w])),
1870
+ terminal: this._dep.terminalInput.map(terminalView),
1871
+ latest: [...this._dep.prev]
1872
+ });
1873
+ }
1874
+ _makeCtx(snapshot) {
1875
+ const ctx = {
1876
+ // Wave-owner boundary (D47): a SYNC fn's emit nests under the public entry that drove
1877
+ // it (cheap inc/dec, no early drain); an ASYNC-pool fn re-enters here from its stashed
1878
+ // ctx at depth 0, so this is the boundary that drains any rewireNext it issued.
1879
+ up: (msgs, towardDep) => {
1880
+ if (this._released) return;
1881
+ enterWave();
1882
+ try {
1883
+ this._up(msgs, towardDep);
1884
+ } finally {
1885
+ exitWave();
1886
+ }
1887
+ },
1888
+ down: (msgs) => {
1889
+ if (this._released) return;
1890
+ enterWave();
1891
+ try {
1892
+ this._down(msgs);
1893
+ } finally {
1894
+ exitWave();
1895
+ }
1896
+ },
1897
+ waveData: snapshot?.waveData ?? this._dep.waveData,
1898
+ terminal: snapshot?.terminal ?? this._dep.terminalInput.map(terminalView),
1899
+ state: this._makeState(),
1900
+ onDeactivation: (fn) => {
1901
+ if (this._released) return;
1902
+ this._hooks.onDeactivation.push(fn);
1903
+ },
1904
+ onInvalidate: (fn) => {
1905
+ if (this._released) return;
1906
+ this._hooks.onInvalidate.push(fn);
1907
+ },
1908
+ environment: () => this._slot.environment,
1909
+ // R-rewire-deferred (D47): defer a self-dep-set mutation to the committed boundary.
1910
+ rewireNext: {
1911
+ subscribeDep: (dep, fn) => this._requestRewireNext({ kind: "add", dep, fn }),
1912
+ unsubscribeDep: (dep, fn) => this._requestRewireNext({ kind: "remove", dep, fn }),
1913
+ replaceDeps: (deps, fn) => this._requestRewireNext({ kind: "set", deps, fn })
1914
+ },
1915
+ // R-up-routing / R-pull (D59): deferred up — route a control wave (e.g. a RESUME pull
1916
+ // DEMAND) up the declared cone at the committed boundary. The SELF-demand path: an
1917
+ // immediate ctx.up whose delivery loops back re-enters this fn (D37 / R-reentrancy).
1918
+ upNext: (msgs, towardDep) => this._requestUpNext(msgs, towardDep),
1919
+ [CTX_DEP_CACHE]: { latest: snapshot?.latest ?? this._dep.prev },
1920
+ [CTX_NODE_BINDING]: {
1921
+ dispatcher: this._slot.dispatcher,
1922
+ create: (factory) => withEnvironmentDrivers(this._slot.environment, () => withNodeCore(this._core, factory))
1923
+ }
1924
+ };
1925
+ if (this._slot.dynamic) {
1926
+ ctx.track = (i) => ctx[CTX_DEP_CACHE]?.latest[i];
1927
+ }
1928
+ return ctx;
1929
+ }
1930
+ _refreshCtx(ctx) {
1931
+ ctx.waveData = this._dep.waveData;
1932
+ ctx.terminal = this._dep.terminalInput.map(terminalView);
1933
+ ctx[CTX_DEP_CACHE] = { latest: this._dep.prev };
1934
+ }
1935
+ _makeState() {
1936
+ return {
1937
+ get: () => this._privateState.value,
1938
+ set: (v) => {
1939
+ this._privateState.value = v;
1940
+ },
1941
+ persist: (on = true) => {
1942
+ this._privateState.persist = on;
1943
+ }
1944
+ };
1945
+ }
1946
+ // ── downstream emission pipeline (the unified waist) ──
1947
+ _down(msgs) {
1948
+ if (this._released) return;
1949
+ validateDownPayloads(msgs);
1950
+ const deliveryWave = {};
1951
+ const assertVersionDataCompatible = (wave) => {
1952
+ for (const m of wave) {
1953
+ if (m[0] === "DATA") assertNodeVersionDataCompatible(this._version.policy, m[1]);
1954
+ }
1955
+ };
1956
+ const snapshotVersionData = (wave) => wave.map(
1957
+ (m) => m[0] === "DATA" ? ["DATA", snapshotNodeVersionData(this._version.policy, m[1])] : m
1958
+ );
1959
+ assertVersionDataCompatible(msgs);
1960
+ if (this._value.terminal !== void 0) {
1961
+ if (!msgs.some((m) => m[0] === "TEARDOWN")) return;
1962
+ this._value.hasTorndown = true;
1963
+ if (this._slot.resetOnTeardown) {
1964
+ this._value.cache = SENTINEL;
1965
+ this._value.hasData = false;
1966
+ }
1967
+ this._emitToSubs(["TEARDOWN"], { wave: deliveryWave, last: true });
1968
+ return;
1969
+ }
1970
+ let sorted = [...msgs].sort((a, b) => messageTier(a[0]) - messageTier(b[0]));
1971
+ const firstInvalidate = sorted.findIndex((m) => m[0] === "INVALIDATE");
1972
+ if (firstInvalidate !== -1) {
1973
+ sorted = sorted.filter((m, i) => m[0] !== "INVALIDATE" || i === firstInvalidate);
1974
+ }
1975
+ const hasTeardown = sorted.some((m) => m[0] === "TEARDOWN");
1976
+ const hasTerminal = sorted.some((m) => m[0] === "COMPLETE" || m[0] === "ERROR");
1977
+ if (hasTeardown && !hasTerminal && this._value.terminal === void 0 && !this._value.hasTorndown) {
1978
+ sorted = [["COMPLETE"], ...sorted];
1979
+ }
1980
+ if (!this._wave.insideRunWave && currentBatch()) {
1981
+ const deferred = snapshotVersionData(sorted.filter((m) => isDeferredTier(m[0])));
1982
+ if (deferred.length > 0) {
1983
+ if (!this._wave.emittedDirtyThisWave) {
1984
+ this._wave.emittedDirtyThisWave = true;
1985
+ this._value.status = "dirty";
1986
+ this._emitToSubs(["DIRTY"], { wave: deliveryWave, last: false });
1987
+ }
1988
+ this._wave.batchDirtyOwed = true;
1989
+ deferToBatch(this, deferred);
1990
+ return;
1991
+ }
1992
+ }
1993
+ if (this._shouldBufferOnPause()) {
1994
+ const buffered = snapshotVersionData(sorted.filter((m) => isPauseBufferedTier(m[0])));
1995
+ if (buffered.length > 0) {
1996
+ this._wave.emittedSettleThisWave = true;
1997
+ this._control.pauseBuffer.push(buffered);
1998
+ }
1999
+ sorted = sorted.filter((m) => !isPauseBufferedTier(m[0]));
2000
+ if (sorted.length === 0) return;
2001
+ }
2002
+ let dataCount = 0;
2003
+ let hasTier3 = false;
2004
+ let hasResolved = false;
2005
+ for (const m of sorted) {
2006
+ if (m[0] === "DATA") dataCount++;
2007
+ if (m[0] === "RESOLVED") hasResolved = true;
2008
+ if (isValueTier(m[0])) hasTier3 = true;
2009
+ }
2010
+ if (dataCount >= 1 && hasResolved) {
2011
+ throw new Error(
2012
+ "down: a wave cannot mix DATA and RESOLVED (tier-3 exclusivity, R-resolved-undirty)"
2013
+ );
2014
+ }
2015
+ const plannedVersions = new Array(sorted.length);
2016
+ if (dataCount > 0) {
2017
+ let plannedVersion = this._version.value;
2018
+ for (let i = 0; i < sorted.length; i++) {
2019
+ const m = sorted[i];
2020
+ if (m[0] !== "DATA") continue;
2021
+ plannedVersion = advanceNodeVersion(plannedVersion, this._version.policy, m[1]);
2022
+ plannedVersions[i] = plannedVersion;
2023
+ }
2024
+ }
2025
+ if (hasTier3 && !this._wave.insideRunWave && !this._wave.emittedDirtyThisWave) {
2026
+ this._wave.emittedDirtyThisWave = true;
2027
+ this._value.status = "dirty";
2028
+ this._emitToSubs(["DIRTY"], { wave: deliveryWave, last: false });
2029
+ }
2030
+ for (let i = 0; i < sorted.length; i++) {
2031
+ const m = sorted[i];
2032
+ const delivery = { wave: deliveryWave, last: i === sorted.length - 1 };
2033
+ if (isDeferredTier(m[0])) this._wave.emittedSettleThisWave = true;
2034
+ if (m[0] === "DIRTY") {
2035
+ if (!this._wave.emittedDirtyThisWave) {
2036
+ this._wave.emittedDirtyThisWave = true;
2037
+ this._value.status = "dirty";
2038
+ this._emitToSubs(["DIRTY"], delivery);
2039
+ }
2040
+ continue;
2041
+ }
2042
+ if (m[0] === "DATA") {
2043
+ const v = m[1];
2044
+ this._value.cache = v;
2045
+ this._value.hasData = true;
2046
+ this._value.status = "settled";
2047
+ this._version.value = plannedVersions[i];
2048
+ if (this._slot.replayN > 0) {
2049
+ this._value.replayRing.push(v);
2050
+ if (this._value.replayRing.length > this._slot.replayN) this._value.replayRing.shift();
2051
+ }
2052
+ this._emitToSubs(["DATA", v], delivery);
2053
+ continue;
2054
+ }
2055
+ if (m[0] === "RESOLVED") {
2056
+ this._value.status = this._value.hasData ? "resolved" : "sentinel";
2057
+ this._emitToSubs(["RESOLVED"], delivery);
2058
+ continue;
2059
+ }
2060
+ if (m[0] === "INVALIDATE") {
2061
+ this._invalidate(delivery);
2062
+ continue;
2063
+ }
2064
+ if (m[0] === "COMPLETE") {
2065
+ if (this._value.terminal !== void 0) continue;
2066
+ this._value.terminal = true;
2067
+ this._control.pauseBuffer = [];
2068
+ this._value.status = "completed";
2069
+ this._emitToSubs(["COMPLETE"], delivery);
2070
+ continue;
2071
+ }
2072
+ if (m[0] === "ERROR") {
2073
+ if (this._value.terminal !== void 0) continue;
2074
+ this._value.terminal = m[1];
2075
+ this._control.pauseBuffer = [];
2076
+ this._value.status = "errored";
2077
+ this._emitToSubs(["ERROR", m[1]], delivery);
2078
+ continue;
2079
+ }
2080
+ if (m[0] === "TEARDOWN") {
2081
+ this._value.hasTorndown = true;
2082
+ if (this._slot.resetOnTeardown) {
2083
+ this._value.cache = SENTINEL;
2084
+ this._value.hasData = false;
2085
+ }
2086
+ this._emitToSubs(["TEARDOWN"], delivery);
2087
+ }
2088
+ }
2089
+ if (!this._wave.insideRunWave) this._wave.emittedDirtyThisWave = false;
2090
+ }
2091
+ _up(msgs, towardDep, route) {
2092
+ if (this._released) return;
2093
+ const routeState = route ?? { demandFired: /* @__PURE__ */ new Map() };
2094
+ for (const m of msgs) {
2095
+ if (!isUpAllowed(m[0])) {
2096
+ throw new Error(
2097
+ `ctx.up: ${m[0]} is down-only (tier ${messageTier(m[0])}); up carries control tiers only (R-ctx-up)`
2098
+ );
2099
+ }
2100
+ }
2101
+ for (const m of msgs) {
2102
+ if (m[0] === "PAUSE") {
2103
+ this._pauseAcquire(m[1]);
2104
+ } else if (m[0] === "RESUME") {
2105
+ if (this._slot.pull && m[1] === this._slot.pullLock) {
2106
+ if (!this._markDemandRouted(m[1], routeState)) this._onDemand();
2107
+ } else if (this._control.pauseLockset.has(m[1])) {
2108
+ this._pauseRelease(m[1]);
2109
+ } else {
2110
+ this._forwardUp(m, towardDep, routeState);
2111
+ }
2112
+ } else if (this._slot.deps.length === 0) {
2113
+ if (m[0] === "INVALIDATE") this._down([["INVALIDATE"]]);
2114
+ } else {
2115
+ this._forwardUp(m, towardDep, routeState);
2116
+ }
2117
+ }
2118
+ }
2119
+ _markDemandRouted(lockId, route) {
2120
+ let holders = route.demandFired.get(lockId);
2121
+ if (holders === void 0) {
2122
+ holders = /* @__PURE__ */ new Set();
2123
+ route.demandFired.set(lockId, holders);
2124
+ }
2125
+ if (holders.has(this)) return true;
2126
+ holders.add(this);
2127
+ return false;
2128
+ }
2129
+ /**
2130
+ * Forward one up-going control message toward deps (R-up-routing): up the single declared edge
2131
+ * `towardDep` (directed — prunes other branches; only meaningful on the issuer's FIRST hop) or
2132
+ * broadcast up ALL deps. A depless source has no deps → the message DROPS here (the terminus —
2133
+ * e.g. a cone-routed RESUME whose pullId no node up this cone holds, R-up-at-source). Recursive
2134
+ * forwarding carries the same per-wave route state and no `towardDep` → broadcast beyond the
2135
+ * first hop.
2136
+ */
2137
+ _forwardUp(m, towardDep, route) {
2138
+ if (this._slot.deps.length === 0) return;
2139
+ if (towardDep !== void 0) {
2140
+ const d = this._slot.deps[towardDep];
2141
+ if (d !== void 0) d._up([m], void 0, route);
2142
+ } else {
2143
+ for (const dep of this._slot.deps) dep._up([m], void 0, route);
2144
+ }
2145
+ }
2146
+ // ── PAUSE/RESUME lockset (R-pause-lockset) + modes (R-pause-modes) ──
2147
+ _isPaused() {
2148
+ return this._control.pauseLockset.size > 0;
2149
+ }
2150
+ _hasBoundaryPauseLock() {
2151
+ if (this._slot.pausable === false) return false;
2152
+ let locks = this._control.pauseLockset.size;
2153
+ if (this._slot.pull && this._control.pauseLockset.has(this._slot.pullLock)) locks--;
2154
+ return locks > 0;
2155
+ }
2156
+ _isAsyncPool() {
2157
+ return this._slot.handle !== null && this._slot.dispatcher.poolKind(this._slot.handle.poolId) === "async";
2158
+ }
2159
+ _pauseAcquire(lockId) {
2160
+ this._control.pauseLockset.add(lockId);
2161
+ }
2162
+ _pauseRelease(lockId) {
2163
+ if (!this._control.pauseLockset.has(lockId)) return;
2164
+ this._control.pauseLockset.delete(lockId);
2165
+ if (this._slot.pull && this._control.demandOwed) this._fireOwedDemandIfReady();
2166
+ if (this._hasBoundaryPauseLock()) return;
2167
+ scheduleBoundaryDrain(this._core);
2168
+ if (this._control.pauseLockset.size > 0) return;
2169
+ this._onResume();
2170
+ }
2171
+ _onResume() {
2172
+ if (this._value.terminal !== void 0) {
2173
+ this._control.pauseBuffer = [];
2174
+ this._control.pausedDepWaveOccurred = false;
2175
+ this._control.demandOwed = false;
2176
+ return;
2177
+ }
2178
+ if (this._control.pauseBuffer.length > 0) {
2179
+ const buf = this._control.pauseBuffer;
2180
+ this._control.pauseBuffer = [];
2181
+ for (const wave of buf) this._down(wave);
2182
+ }
2183
+ if (this._control.pausedDepWaveOccurred) {
2184
+ this._control.pausedDepWaveOccurred = false;
2185
+ this._tryRun();
2186
+ }
2187
+ }
2188
+ /**
2189
+ * R-pull (D59): can this pull node fire a demand NOW? — not terminal, settle-ready (_pending===0),
2190
+ * and no OTHER (non-pullId) lock holds it paused. The gate that decides fire-now vs OWE.
2191
+ */
2192
+ _canFireDemand() {
2193
+ if (this._value.terminal !== void 0 || this._wave.pending > 0) return false;
2194
+ const own = this._control.pauseLockset.has(this._slot.pullLock) ? 1 : 0;
2195
+ return this._control.pauseLockset.size <= own;
2196
+ }
2197
+ /**
2198
+ * R-pull (D59): deliver ONE demand pulse — release the pullId so the demand wave RELAYS (not
2199
+ * absorbed), fire it, then RE-QUIET (1:1). The pullId re-add + the re-entrancy flag are in a
2200
+ * `finally` so a throwing pull fn (graph-less node, no D30 catch) cannot leave the node
2201
+ * permanently non-quiet (the wedge-fix silently disabled) — QA-found robustness fix.
2202
+ */
2203
+ _deliverPullDemand() {
2204
+ this._control.demandOwed = false;
2205
+ this._control.inDeliverDemand = true;
2206
+ this._control.pauseLockset.delete(this._slot.pullLock);
2207
+ try {
2208
+ this._firePullDemand();
2209
+ } finally {
2210
+ this._control.pauseLockset.add(this._slot.pullLock);
2211
+ this._control.inDeliverDemand = false;
2212
+ }
2213
+ }
2214
+ /**
2215
+ * R-pull (D59): a cone-routed RESUME of THIS node's pullId arrived (R-up-routing) = a DEMAND.
2216
+ * Fire immediately if able; else OWE it — pin-5 (a dep DIRTY in flight, _pending>0) or F4/F5 (an
2217
+ * external PAUSE lock co-holds the node) — and fire when the node next becomes able. A demand
2218
+ * arriving WHILE a delivery is in flight (synchronous re-entry) is DROPPED (1:1, QA guard).
2219
+ */
2220
+ _onDemand() {
2221
+ if (this._control.inDeliverDemand) return;
2222
+ if (this._canFireDemand()) this._deliverPullDemand();
2223
+ else this._control.demandOwed = true;
2224
+ }
2225
+ /**
2226
+ * R-pull (D55): service ONE demand. Delivery content = the orthogonal pausable mode:
2227
+ * - 'resumeAll' → drain the buffered BACKLOG (each `_down` re-synthesizes a leading DIRTY, so
2228
+ * the per-entry replay is DIRTY-before-DATA — R-pause-modes per-entry arrival order);
2229
+ * - true → deliver the coalesced LATEST as ONE DIRTY-before-DATA wave (pin 2: the quiet absorb
2230
+ * suppressed the dep's original DIRTY, so the demand wave restores it before the value).
2231
+ * Neither branch fires when nothing changed since the last demand → the demand is SILENT.
2232
+ * Called by `_deliverPullDemand` only when settle-ready; the caller has released the pullId, so
2233
+ * `_markDirty` relays the leading DIRTY (not absorbed).
2234
+ */
2235
+ _firePullDemand() {
2236
+ if (this._control.pauseBuffer.length > 0) {
2237
+ const buf = this._control.pauseBuffer;
2238
+ this._control.pauseBuffer = [];
2239
+ for (const wave of buf) this._down(wave);
2240
+ }
2241
+ if (this._control.pausedDepWaveOccurred) {
2242
+ const gated = this._slot.handle !== null && !this._wave.hasCalledFnOnce && !(this._slot.partial || this._allDepsSettled());
2243
+ if (this._wave.pending > 0 || gated) return;
2244
+ this._control.pausedDepWaveOccurred = false;
2245
+ this._markDirty();
2246
+ this._tryRun();
2247
+ }
2248
+ }
2249
+ /**
2250
+ * R-pull (D55/D59): fire a demand that was OWED (deferred at demand time) once the node becomes
2251
+ * able — called from the dep-settle arms (DATA/RESOLVED/INVALIDATE/terminal drain _pending, pin-5
2252
+ * + B1/F6) and from `_pauseRelease` (an external PAUSE lock releases, F4/F5).
2253
+ */
2254
+ _fireOwedDemandIfReady() {
2255
+ if (this._control.inDeliverDemand) return;
2256
+ if (this._slot.pull && this._control.demandOwed && this._canFireDemand())
2257
+ this._deliverPullDemand();
2258
+ }
2259
+ /** Should an outgoing settle slice be deferred into the pause buffer? */
2260
+ _shouldBufferOnPause() {
2261
+ if (this._slot.pausable === false) return false;
2262
+ if (!this._isPaused()) return false;
2263
+ if (this._slot.pausable === "resumeAll") return true;
2264
+ if (!this._wave.insideRunWave && this._isAsyncPool() && this._slot.deps.length > 0) return true;
2265
+ return false;
2266
+ }
2267
+ /** R-invalidate-idempotent: clear cache + flush + cascade; no-op if nothing cached. */
2268
+ _invalidate(delivery) {
2269
+ if (!this._value.hasData) return;
2270
+ this._value.cache = SENTINEL;
2271
+ this._value.hasData = false;
2272
+ this._value.status = "sentinel";
2273
+ this._value.replayRing = [];
2274
+ for (const fn of this._hooks.onInvalidate) fn();
2275
+ this._emitToSubs(["INVALIDATE"], delivery);
2276
+ }
2277
+ // B42 (R-deps-terminal): ALL deps TERMINAL = every dep has reached COMPLETE *or* an ABSORBED
2278
+ // ERROR. Block only on a LIVE dep (_depTerminal[i] === undefined); an errored dep (terminal = the
2279
+ // error payload, which R-data-payload guarantees is !== undefined and non-boolean) COUNTS as
2280
+ // terminal-done. Was
2281
+ // `tm !== true`, which wedged a node whose errorWhenDepsError:false dep ERRORed (it never
2282
+ // auto-completed even after every other dep completed). Drives the completeWhenDepsComplete cascade.
2283
+ _allDepsTerminal() {
2284
+ if (this._slot.deps.length === 0) return false;
2285
+ for (const tm of this._dep.terminal) if (tm === void 0) return false;
2286
+ return true;
2287
+ }
2288
+ /** R-terminal: resubscribable reset clears terminal + dep state + re-arms the gate. */
2289
+ _resetLifecycle() {
2290
+ for (const u of this._dep.unsubs) if (u) u();
2291
+ this._dep.unsubs = [];
2292
+ this._dep.idxBoxes = [];
2293
+ this._lifecycle.subscribers.clear();
2294
+ this._lifecycle.activated = false;
2295
+ this._value.terminal = void 0;
2296
+ this._value.hasTorndown = false;
2297
+ this._wave.hasCalledFnOnce = false;
2298
+ this._resetDepState();
2299
+ this._control.pauseLockset.clear();
2300
+ this._control.pauseBuffer = [];
2301
+ this._control.pausedDepWaveOccurred = false;
2302
+ this._control.demandOwed = false;
2303
+ this._value.replayRing = [];
2304
+ const isCompute = this._slot.handle !== null || this._slot.deps.length > 0;
2305
+ if (isCompute) {
2306
+ this._value.cache = SENTINEL;
2307
+ this._value.hasData = false;
2308
+ this._value.status = "sentinel";
2309
+ } else {
2310
+ this._value.status = this._value.hasData ? "settled" : "sentinel";
2311
+ }
2312
+ if (!this._privateState.persist) this._privateState.value = SENTINEL;
2313
+ }
2314
+ _emitToSubs(msg, delivery) {
2315
+ if (this._released) return;
2316
+ const subs = [...this._lifecycle.subscribers];
2317
+ for (const sink of subs) sink(msg, delivery);
2318
+ }
2319
+ /** Batch commit (R-batch-coalesce): deliver the deferred tier-3 wave now. */
2320
+ __commitBatchedWave(wave) {
2321
+ this._wave.batchDirtyOwed = false;
2322
+ this._down(wave);
2323
+ }
2324
+ /** Batch rollback: balance the immediate DIRTY with a RESOLVED so downstream un-dirties. */
2325
+ __rollbackBatched() {
2326
+ if (this._wave.batchDirtyOwed) {
2327
+ this._wave.batchDirtyOwed = false;
2328
+ this._wave.emittedDirtyThisWave = false;
2329
+ this._value.status = this._value.hasData ? "settled" : "sentinel";
2330
+ this._emitToSubs(["RESOLVED"]);
2331
+ }
2332
+ }
2333
+ /** B49: enqueue a committed-boundary task on this node's graph-local core. */
2334
+ __deferBoundary(fn, batchToken) {
2335
+ deferRewire(this._core, fn, {
2336
+ batchToken,
2337
+ isReady: () => !this._hasBoundaryPauseLock()
2338
+ });
2339
+ }
2340
+ };
2341
+ function node(deps = [], handleOrFn = null, opts = {}) {
2342
+ return new Node(deps, handleOrFn, opts);
2343
+ }
2344
+
2345
+ // src/graph/checkpoint.ts
2346
+ function toCheckpointJson(value, path = "$") {
2347
+ try {
2348
+ return strictJsonCodec.decode(strictJsonCodec.encode(value));
2349
+ } catch (cause) {
2350
+ throw new TypeError(`checkpoint: value at ${path} is not strict JSON compatible`, {
2351
+ cause
2352
+ });
2353
+ }
2354
+ }
2355
+ var backendStateContributors = /* @__PURE__ */ new WeakMap();
2356
+ function registerBackendStateContributor(node2, contributor) {
2357
+ backendStateContributors.set(node2, contributor);
2358
+ }
2359
+
2360
+ // src/graph/data-structures/core.ts
2361
+ function validateViewCacheMaxEntries(policy) {
2362
+ const maxEntries = policy?.maxEntries;
2363
+ if (maxEntries === void 0) return void 0;
2364
+ if (!Number.isInteger(maxEntries) || maxEntries < 0) {
2365
+ throw new RangeError(`viewCache.maxEntries must be a non-negative integer (got ${maxEntries})`);
2366
+ }
2367
+ return maxEntries;
2368
+ }
2369
+ var ViewMemoCache = class {
2370
+ maxEntries;
2371
+ equals;
2372
+ memo = [];
2373
+ live = /* @__PURE__ */ new Set();
2374
+ constructor(policy, equals = Object.is) {
2375
+ this.maxEntries = validateViewCacheMaxEntries(policy);
2376
+ this.equals = equals;
2377
+ }
2378
+ get(key) {
2379
+ const index = this.memo.findIndex((entry2) => this.equals(entry2.key, key));
2380
+ if (index < 0) return void 0;
2381
+ const [entry] = this.memo.splice(index, 1);
2382
+ if (entry === void 0) return void 0;
2383
+ this.memo.push(entry);
2384
+ return entry.value;
2385
+ }
2386
+ set(key, value) {
2387
+ this.deleteValue(value);
2388
+ this.live.add(value);
2389
+ if (this.maxEntries === 0) return;
2390
+ this.memo.push({ key, value });
2391
+ while (this.maxEntries !== void 0 && this.memo.length > this.maxEntries) {
2392
+ this.memo.shift();
2393
+ }
2394
+ }
2395
+ deleteValue(value) {
2396
+ for (let i = this.memo.length - 1; i >= 0; i -= 1) {
2397
+ if (this.memo[i]?.value === value) this.memo.splice(i, 1);
2398
+ }
2399
+ this.live.delete(value);
2400
+ }
2401
+ disposeAll() {
2402
+ for (const view of [...this.live]) view.dispose();
2403
+ this.memo.length = 0;
2404
+ this.live.clear();
2405
+ }
2406
+ };
2407
+ function lightReactiveView(parentDelta, opts) {
2408
+ const { dispatcher, factory, graph, materializeSnapshot, name, onDispose } = opts;
2409
+ const base = dispatcher ? { dispatcher } : {};
2410
+ const group = graph?.topologyGroup({ name: name ?? factory });
2411
+ const deltaBody = (ctx) => {
2412
+ for (const change of depBatch(ctx, 0) ?? []) {
2413
+ ctx.down([["DATA", change]]);
2414
+ }
2415
+ };
2416
+ const delta = group ? group.node([parentDelta], deltaBody, {
2417
+ factory: `${factory}.delta`,
2418
+ name: name ? `${name}.delta` : void 0,
2419
+ meta: { kind: "collection_view_delta", factory },
2420
+ partial: true
2421
+ }) : new Node([parentDelta], deltaBody, {
2422
+ ...base,
2423
+ factory: `${factory}.delta`,
2424
+ name: name ? `${name}.delta` : void 0,
2425
+ partial: true
2426
+ });
2427
+ const pullId = /* @__PURE__ */ Symbol(name ? `${name}.snapshot` : `${factory}.snapshot`);
2428
+ const snapshotBody = (ctx) => {
2429
+ try {
2430
+ ctx.down([["DATA", materializeSnapshot()]]);
2431
+ } catch (e) {
2432
+ ctx.down([["ERROR", errorPayload(e, `${factory}.snapshot materializer failed`)]]);
2433
+ }
2434
+ };
2435
+ const snapshot = group ? group.node([delta], snapshotBody, {
2436
+ factory: `${factory}.snapshot`,
2437
+ name: name ? `${name}.snapshot` : void 0,
2438
+ meta: { kind: "collection_view_snapshot", factory },
2439
+ partial: true,
2440
+ pullId
2441
+ }) : new Node([delta], snapshotBody, {
2442
+ ...base,
2443
+ factory: `${factory}.snapshot`,
2444
+ name: name ? `${name}.snapshot` : void 0,
2445
+ partial: true,
2446
+ pullId
2447
+ });
2448
+ let disposed = false;
2449
+ return {
2450
+ delta,
2451
+ snapshot,
2452
+ pullId,
2453
+ dispose() {
2454
+ if (disposed) return;
2455
+ if (group) {
2456
+ group.release({ reason: factory });
2457
+ } else {
2458
+ releaseRuntimeOfNode(delta);
2459
+ releaseRuntimeOfNode(snapshot);
2460
+ }
2461
+ disposed = true;
2462
+ onDispose?.();
2463
+ }
2464
+ };
2465
+ }
2466
+ function collectionCore(backend, factory, opts = {}, restore) {
2467
+ const { name, dispatcher, graph } = opts;
2468
+ const base = dispatcher ? { dispatcher } : {};
2469
+ let bindSeq = 0;
2470
+ const delta = graph ? graph.node([], null, {
2471
+ factory: `${factory}.delta`,
2472
+ name: name ? `${name}.delta` : void 0,
2473
+ meta: { kind: "collection_delta", collection: factory },
2474
+ ...restore ? { restore: { ref: restore.deltaRef, config: restore.config } } : {}
2475
+ }) : new Node([], null, {
2476
+ ...base,
2477
+ factory: `${factory}.delta`,
2478
+ name: name ? `${name}.delta` : void 0
2479
+ });
2480
+ const pullId = /* @__PURE__ */ Symbol(name ? `${name}.snapshot` : `${factory}.snapshot`);
2481
+ const snapFn = (ctx) => {
2482
+ ctx.down([["DATA", backend.snapshot()]]);
2483
+ };
2484
+ const snapshot = graph ? graph.node([delta], snapFn, {
2485
+ pullId,
2486
+ partial: true,
2487
+ factory: `${factory}.snapshot`,
2488
+ name: name ? `${name}.snapshot` : void 0,
2489
+ meta: { kind: "collection_snapshot", collection: factory },
2490
+ ...restore ? { restore: { ref: restore.snapshotRef, config: restore.config } } : {}
2491
+ }) : new Node([delta], snapFn, {
2492
+ ...base,
2493
+ pullId,
2494
+ partial: true,
2495
+ factory: `${factory}.snapshot`,
2496
+ name: name ? `${name}.snapshot` : void 0
2497
+ });
2498
+ if (restore !== void 0) {
2499
+ registerBackendStateContributor(delta, restore.backendState);
2500
+ }
2501
+ function emit(change) {
2502
+ delta.down([["DATA", change]]);
2503
+ }
2504
+ function bindSource(src, apply) {
2505
+ if (graph === void 0)
2506
+ throw new Error(
2507
+ "collectionCore.bindSource requires options.graph so the input fold is describe-visible (D61)"
2508
+ );
2509
+ const op = {
2510
+ factory: `${factory}.bindSource`,
2511
+ body: (ctx) => {
2512
+ const b = depBatch(ctx, 0);
2513
+ try {
2514
+ if (b) for (const v of b) apply(v);
2515
+ } catch (e) {
2516
+ ctx.down([["ERROR", errorPayload(e, "collectionCore.bindSource failed")]]);
2517
+ }
2518
+ }
2519
+ };
2520
+ const folder = graph.initNode(op, [src], {
2521
+ name: name ? `${name}.bind#${bindSeq++}` : void 0,
2522
+ meta: { kind: "collection_bind_source", collection: factory }
2523
+ });
2524
+ return graph.retain(folder, { reason: `${factory}.bindSource` });
2525
+ }
2526
+ return { delta, snapshot, pullId, emit, bindSource };
2527
+ }
2528
+
2529
+ // src/graph/data-structures/reactive-index.ts
2530
+ function cmpOrd(a, b) {
2531
+ if (a === b) return 0;
2532
+ const ta = typeof a;
2533
+ if (ta === typeof b && (ta === "number" || ta === "string" || ta === "boolean" || ta === "bigint")) {
2534
+ const ax = a;
2535
+ const bx = b;
2536
+ return ax < bx ? -1 : ax > bx ? 1 : 0;
2537
+ }
2538
+ return String(a).localeCompare(String(b));
2539
+ }
2540
+ function compareRows(a, b) {
2541
+ const c = cmpOrd(a.secondary, b.secondary);
2542
+ return c !== 0 ? c : cmpOrd(a.primary, b.primary);
2543
+ }
2544
+ function bisectLeft(rows, row) {
2545
+ let lo = 0;
2546
+ let hi = rows.length;
2547
+ while (lo < hi) {
2548
+ const mid = lo + hi >> 1;
2549
+ if (compareRows(rows[mid], row) < 0) lo = mid + 1;
2550
+ else hi = mid;
2551
+ }
2552
+ return lo;
2553
+ }
2554
+ var IndexBackend = class {
2555
+ _version = 0;
2556
+ buf = [];
2557
+ byKey = /* @__PURE__ */ new Map();
2558
+ trackLru;
2559
+ constructor(trackLru, initialRows = []) {
2560
+ this.trackLru = trackLru;
2561
+ for (const row of initialRows) {
2562
+ this.upsert(row.primary, row.secondary, row.value);
2563
+ }
2564
+ this._version = 0;
2565
+ }
2566
+ get version() {
2567
+ return this._version;
2568
+ }
2569
+ get size() {
2570
+ return this.buf.length;
2571
+ }
2572
+ has(primary) {
2573
+ const row = this.byKey.get(primary);
2574
+ if (row === void 0) return false;
2575
+ this.touchLru(row);
2576
+ return true;
2577
+ }
2578
+ get(primary) {
2579
+ const row = this.byKey.get(primary);
2580
+ if (row === void 0) return void 0;
2581
+ this.touchLru(row);
2582
+ return row.value;
2583
+ }
2584
+ touchLru(row) {
2585
+ if (!this.trackLru) return;
2586
+ this.byKey.delete(row.primary);
2587
+ this.byKey.set(row.primary, row);
2588
+ }
2589
+ removeRow(row) {
2590
+ const start = bisectLeft(this.buf, row);
2591
+ for (let i = start; i < this.buf.length && compareRows(this.buf[i], row) === 0; i++) {
2592
+ if (this.buf[i] === row) {
2593
+ this.buf.splice(i, 1);
2594
+ return;
2595
+ }
2596
+ }
2597
+ const fallback = this.buf.indexOf(row);
2598
+ if (fallback >= 0) {
2599
+ this.buf.splice(fallback, 1);
2600
+ return;
2601
+ }
2602
+ throw new Error("reactiveIndex: internal row index desynchronized");
2603
+ }
2604
+ upsert(primary, secondary, value) {
2605
+ const existing = this.byKey.get(primary);
2606
+ const row = { primary, secondary, value };
2607
+ if (existing !== void 0) {
2608
+ this.removeRow(existing);
2609
+ this.byKey.delete(primary);
2610
+ }
2611
+ this.buf.splice(bisectLeft(this.buf, row), 0, row);
2612
+ this.byKey.set(primary, row);
2613
+ this._version += 1;
2614
+ return existing === void 0;
2615
+ }
2616
+ delete(primary) {
2617
+ const existing = this.byKey.get(primary);
2618
+ if (existing === void 0) return false;
2619
+ this.removeRow(existing);
2620
+ this.byKey.delete(primary);
2621
+ this._version += 1;
2622
+ return true;
2623
+ }
2624
+ clear() {
2625
+ const n = this.buf.length;
2626
+ if (n === 0) return 0;
2627
+ this.buf.length = 0;
2628
+ this.byKey.clear();
2629
+ this._version += 1;
2630
+ return n;
2631
+ }
2632
+ rangeByPrimary(start, end) {
2633
+ if (cmpOrd(start, end) >= 0) return [];
2634
+ const rows = [...this.byKey.entries()].filter(
2635
+ ([p]) => cmpOrd(p, start) >= 0 && cmpOrd(p, end) < 0
2636
+ );
2637
+ rows.sort(([a], [b]) => cmpOrd(a, b));
2638
+ return rows.map(([, r]) => r.value);
2639
+ }
2640
+ evictOneForCapacity(order, maxSize) {
2641
+ if (maxSize === void 0) return void 0;
2642
+ if (!Number.isInteger(maxSize) || maxSize < 1)
2643
+ throw new RangeError(`reactiveIndex: maxSize must be a positive integer (got ${maxSize})`);
2644
+ const overflow = this.buf.length - maxSize;
2645
+ if (overflow <= 0) return void 0;
2646
+ switch (order) {
2647
+ case "secondary": {
2648
+ const row = this.buf[0];
2649
+ if (row === void 0) return void 0;
2650
+ this.buf.shift();
2651
+ this.byKey.delete(row.primary);
2652
+ this._version += 1;
2653
+ return row.primary;
2654
+ }
2655
+ case "primary": {
2656
+ let victim;
2657
+ for (const key of this.byKey.keys()) {
2658
+ if (victim === void 0 || cmpOrd(key, victim) < 0) victim = key;
2659
+ }
2660
+ if (victim === void 0) return void 0;
2661
+ const row = this.byKey.get(victim);
2662
+ if (row !== void 0) this.removeRow(row);
2663
+ this.byKey.delete(victim);
2664
+ this._version += 1;
2665
+ return victim;
2666
+ }
2667
+ case "lru": {
2668
+ const oldest = this.byKey.keys().next().value;
2669
+ if (oldest === void 0) return void 0;
2670
+ const row = this.byKey.get(oldest);
2671
+ if (row !== void 0) this.removeRow(row);
2672
+ this.byKey.delete(oldest);
2673
+ this._version += 1;
2674
+ return oldest;
2675
+ }
2676
+ default: {
2677
+ throw new Error(`reactiveIndex: unknown capacity order '${String(order)}'`);
2678
+ }
2679
+ }
2680
+ }
2681
+ snapshot() {
2682
+ return [...this.buf];
2683
+ }
2684
+ toPrimaryMap() {
2685
+ const m = /* @__PURE__ */ new Map();
2686
+ for (const r of this.buf) m.set(r.primary, r.value);
2687
+ return m;
2688
+ }
2689
+ };
2690
+ function assertCheckpointableIndexRows(rows) {
2691
+ for (const [i, row] of rows.entries()) {
2692
+ const primary = row.primary;
2693
+ if (primary === null || typeof primary === "string" || typeof primary === "number" || typeof primary === "boolean") {
2694
+ continue;
2695
+ }
2696
+ throw new TypeError(
2697
+ `reactiveIndex checkpoint backendState[${i}].primary must be a JSON primitive primary key for D160 restore`
2698
+ );
2699
+ }
2700
+ }
2701
+ function reactiveIndex(options = {}) {
2702
+ return createReactiveIndex(options);
2703
+ }
2704
+ function restoreReactiveIndexFromBackendState(initialRows, options = {}) {
2705
+ return createReactiveIndex(options, initialRows);
2706
+ }
2707
+ function createReactiveIndex(options = {}, initialRows = []) {
2708
+ const { capacity, graph, dispatcher, name } = options;
2709
+ const lruEnabled = capacity?.order === "lru";
2710
+ const backend = new IndexBackend(lruEnabled, initialRows);
2711
+ const core = collectionCore(
2712
+ backend,
2713
+ "reactiveIndex",
2714
+ options,
2715
+ graph !== void 0 && name !== void 0 && capacity === void 0 ? {
2716
+ deltaRef: "reactiveIndex.delta",
2717
+ snapshotRef: "reactiveIndex.snapshot",
2718
+ backendState: () => {
2719
+ const rows = backend.snapshot();
2720
+ assertCheckpointableIndexRows(rows);
2721
+ return rows;
2722
+ }
2723
+ } : void 0
2724
+ );
2725
+ const binds = [];
2726
+ const base = dispatcher ? { dispatcher } : {};
2727
+ const bindDeps = /* @__PURE__ */ new WeakSet();
2728
+ const rangeMemo = new ViewMemoCache(options.viewCache, ([aStart, aEnd], [bStart, bEnd]) => {
2729
+ return cmpOrd(aStart, bStart) === 0 && cmpOrd(aEnd, bEnd) === 0;
2730
+ });
2731
+ let bindSeq = 0;
2732
+ let rangeSeq = 0;
2733
+ let capacityPolicy;
2734
+ let apply;
2735
+ let releaseApply = () => {
2736
+ };
2737
+ function isNodeOpt(x) {
2738
+ return x instanceof Node;
2739
+ }
2740
+ function validateMaxSize(v) {
2741
+ if (!Number.isFinite(v) || !Number.isInteger(v) || v < 1)
2742
+ throw new RangeError(`reactiveIndex: maxSize must be a positive integer (got ${v})`);
2743
+ return v;
2744
+ }
2745
+ function enforceCapacity() {
2746
+ if (currentCapacityOrder === void 0) return;
2747
+ for (; ; ) {
2748
+ const primary = backend.evictOneForCapacity(currentCapacityOrder, currentMaxSize);
2749
+ if (primary === void 0) return;
2750
+ core.emit({ kind: "delete", primary });
2751
+ }
2752
+ }
2753
+ const applyBody = (ctx) => {
2754
+ const deps = apply?.deps ?? [];
2755
+ for (let i = 0; i < depCount(ctx); i++) {
2756
+ const dep = deps[i];
2757
+ if (dep === capacityPolicy) {
2758
+ const latest = depLatest(ctx, i);
2759
+ if (latest !== void 0) currentMaxSize = validateMaxSize(latest);
2760
+ continue;
2761
+ }
2762
+ if (dep && bindDeps.has(dep)) {
2763
+ for (const r of depBatch(ctx, i) ?? []) {
2764
+ backend.upsert(r.primary, r.secondary, r.value);
2765
+ core.emit({ kind: "upsert", primary: r.primary, secondary: r.secondary, value: r.value });
2766
+ enforceCapacity();
2767
+ }
2768
+ }
2769
+ }
2770
+ enforceCapacity();
2771
+ };
2772
+ function ensureApply() {
2773
+ if (apply !== void 0) return apply;
2774
+ const op = {
2775
+ factory: "reactiveIndex.capacityPolicy",
2776
+ body: applyBody,
2777
+ opts: { partial: true }
2778
+ };
2779
+ const deps = capacityPolicy ? [capacityPolicy] : [];
2780
+ apply = graph ? graph.initNode(op, deps, {
2781
+ name: name ? `${name}.capacityPolicy` : void 0,
2782
+ meta: { kind: "collection_policy_apply", collection: "reactiveIndex" }
2783
+ }) : new Node(deps, op.body, {
2784
+ ...base,
2785
+ factory: "reactiveIndex.capacityPolicy",
2786
+ partial: true,
2787
+ name: name ? `${name}.capacityPolicy` : void 0
2788
+ });
2789
+ releaseApply = graph ? graph.retain(apply, { reason: "reactiveIndex.capacityPolicy" }) : apply.subscribe(() => {
2790
+ });
2791
+ return apply;
2792
+ }
2793
+ let currentMaxSize;
2794
+ const currentCapacityOrder = capacity?.order;
2795
+ if (capacity !== void 0) {
2796
+ if (currentCapacityOrder !== "secondary" && currentCapacityOrder !== "primary" && currentCapacityOrder !== "lru") {
2797
+ throw new Error("reactiveIndex: capacity.order must be one of secondary|primary|lru (D73)");
2798
+ }
2799
+ const maxSize = capacity.maxSize;
2800
+ if (isNodeOpt(maxSize) && graph === void 0) {
2801
+ throw new Error(
2802
+ "reactiveIndex capacity.maxSize Node option requires options.graph so the policy edge is describe-visible (D73)"
2803
+ );
2804
+ }
2805
+ capacityPolicy = isNodeOpt(maxSize) ? maxSize : graph ? graph.initNode(
2806
+ {
2807
+ factory: "reactiveIndex.maxSizePolicy",
2808
+ body: () => {
2809
+ },
2810
+ opts: { initial: validateMaxSize(maxSize) }
2811
+ },
2812
+ [],
2813
+ {
2814
+ name: name ? `${name}.maxSizePolicy` : void 0,
2815
+ meta: { kind: "collection_policy", collection: "reactiveIndex", policy: "maxSize" }
2816
+ }
2817
+ ) : void 0;
2818
+ if (!isNodeOpt(maxSize)) currentMaxSize = validateMaxSize(maxSize);
2819
+ if (capacityPolicy instanceof Node) ensureApply();
2820
+ }
2821
+ let byPrimaryNode;
2822
+ function getByPrimary() {
2823
+ if (byPrimaryNode === void 0) {
2824
+ byPrimaryNode = node(
2825
+ [core.delta],
2826
+ (ctx) => {
2827
+ ctx.down([["DATA", backend.toPrimaryMap()]]);
2828
+ },
2829
+ { factory: "reactiveIndex.byPrimary", partial: true }
2830
+ );
2831
+ }
2832
+ return byPrimaryNode;
2833
+ }
2834
+ function getRange(start, end) {
2835
+ const key = [start, end];
2836
+ const hit = rangeMemo.get(key);
2837
+ if (hit !== void 0) return hit;
2838
+ const materialize = () => backend.rangeByPrimary(start, end);
2839
+ let view;
2840
+ view = lightReactiveView(core.delta, {
2841
+ ...options,
2842
+ factory: "reactiveIndex.range",
2843
+ name: name ? `${name}.range#${rangeSeq++}` : void 0,
2844
+ materializeSnapshot: materialize,
2845
+ onDispose: () => {
2846
+ rangeMemo.deleteValue(view);
2847
+ }
2848
+ });
2849
+ rangeMemo.set(key, view);
2850
+ return view;
2851
+ }
2852
+ return {
2853
+ delta: core.delta,
2854
+ snapshot: core.snapshot,
2855
+ pullId: core.pullId,
2856
+ get size() {
2857
+ return backend.size;
2858
+ },
2859
+ has(primary) {
2860
+ return backend.has(primary);
2861
+ },
2862
+ get(primary) {
2863
+ return backend.get(primary);
2864
+ },
2865
+ rangeByPrimary(start, end) {
2866
+ return backend.rangeByPrimary(start, end);
2867
+ },
2868
+ range(start, end) {
2869
+ return getRange(start, end);
2870
+ },
2871
+ toArray() {
2872
+ return backend.snapshot();
2873
+ },
2874
+ toPrimaryMap() {
2875
+ return backend.toPrimaryMap();
2876
+ },
2877
+ get byPrimary() {
2878
+ return getByPrimary();
2879
+ },
2880
+ upsert(primary, secondary, value) {
2881
+ const inserted = backend.upsert(primary, secondary, value);
2882
+ core.emit({ kind: "upsert", primary, secondary, value });
2883
+ enforceCapacity();
2884
+ return inserted;
2885
+ },
2886
+ upsertMany(rows) {
2887
+ for (const r of rows) {
2888
+ backend.upsert(r.primary, r.secondary, r.value);
2889
+ core.emit({ kind: "upsert", primary: r.primary, secondary: r.secondary, value: r.value });
2890
+ enforceCapacity();
2891
+ }
2892
+ },
2893
+ delete(primary) {
2894
+ if (backend.delete(primary)) core.emit({ kind: "delete", primary });
2895
+ },
2896
+ deleteMany(primaries) {
2897
+ const removed = [];
2898
+ for (const p of primaries) if (backend.delete(p)) removed.push(p);
2899
+ if (removed.length > 0) core.emit({ kind: "deleteMany", primaries: removed });
2900
+ },
2901
+ clear() {
2902
+ const count = backend.clear();
2903
+ if (count > 0) core.emit({ kind: "clear", count });
2904
+ },
2905
+ upsertFrom(src) {
2906
+ if (graph === void 0)
2907
+ throw new Error(
2908
+ "reactiveIndex.upsertFrom requires options.graph so the input fold is describe-visible (D61)"
2909
+ );
2910
+ const op = {
2911
+ factory: "reactiveIndex.bindSource",
2912
+ body: (ctx) => {
2913
+ try {
2914
+ for (const r of depBatch(ctx, 0) ?? []) {
2915
+ ctx.down([["DATA", r]]);
2916
+ }
2917
+ } catch (e) {
2918
+ ctx.down([["ERROR", errorPayload(e, "reactiveIndex.bindSource failed")]]);
2919
+ }
2920
+ }
2921
+ };
2922
+ const folder = graph.initNode(op, [src], {
2923
+ name: name ? `${name}.bind#${bindSeq++}` : void 0,
2924
+ meta: { kind: "collection_bind_source", collection: "reactiveIndex" }
2925
+ });
2926
+ bindDeps.add(folder);
2927
+ const applyNode = ensureApply();
2928
+ applyNode.subscribeDep(folder, applyBody);
2929
+ let active2 = true;
2930
+ const dispose = () => {
2931
+ if (!active2) return;
2932
+ active2 = false;
2933
+ applyNode.unsubscribeDep(folder, applyBody);
2934
+ };
2935
+ binds.push(dispose);
2936
+ return dispose;
2937
+ },
2938
+ dispose() {
2939
+ for (const d of binds) d();
2940
+ binds.length = 0;
2941
+ releaseApply();
2942
+ rangeMemo.disposeAll();
2943
+ }
2944
+ };
2945
+ }
2946
+
2947
+ // src/graph/policies/collection.ts
2948
+ var PolicyInputs = class {
2949
+ deps;
2950
+ constructor(initial = []) {
2951
+ this.deps = [...initial];
2952
+ }
2953
+ add(node2) {
2954
+ if (node2 === void 0) return missingPolicyReader();
2955
+ const index = this.deps.push(node2) - 1;
2956
+ return policyReader(index);
2957
+ }
2958
+ };
2959
+ function missingPolicyReader() {
2960
+ return {
2961
+ index: -1,
2962
+ read: (_ctx, current) => current
2963
+ };
2964
+ }
2965
+ function policyReader(index) {
2966
+ return {
2967
+ index,
2968
+ read(ctx, current) {
2969
+ const latest = depLatest(ctx, index);
2970
+ return latest === void 0 ? current : latest;
2971
+ }
2972
+ };
2973
+ }
2974
+ function lruKeys(keys) {
2975
+ return {
2976
+ keys: () => keys
2977
+ };
2978
+ }
2979
+ function trimHeadOverflow(items, config) {
2980
+ const { maxSize } = config;
2981
+ if (maxSize === void 0) return [];
2982
+ if (!Number.isInteger(maxSize) || maxSize < 1)
2983
+ throw new RangeError(`maxSize must be a positive integer (got ${maxSize})`);
2984
+ const size = "size" in config ? config.size : Array.isArray(items) ? items.length : void 0;
2985
+ if (size === void 0) throw new Error("trimHeadOverflow requires size for non-array iterables");
2986
+ const overflow = size - maxSize;
2987
+ if (overflow <= 0) return [];
2988
+ if (Array.isArray(items) && !("size" in config)) return items.splice(0, overflow);
2989
+ const victims = [];
2990
+ for (const item of items) {
2991
+ victims.push(item);
2992
+ if (victims.length === overflow) break;
2993
+ }
2994
+ return victims;
2995
+ }
2996
+ function deadlines() {
2997
+ return new MinDeadlineHeap();
2998
+ }
2999
+ var MinDeadlineHeap = class {
3000
+ items = [];
3001
+ get size() {
3002
+ return this.items.length;
3003
+ }
3004
+ push(item) {
3005
+ this.items.push(item);
3006
+ this.up(this.items.length - 1);
3007
+ }
3008
+ peek() {
3009
+ return this.items[0];
3010
+ }
3011
+ pop() {
3012
+ const top = this.items[0];
3013
+ const last = this.items.pop();
3014
+ if (top !== void 0 && last !== void 0 && this.items.length > 0) {
3015
+ this.items[0] = last;
3016
+ this.down(0);
3017
+ }
3018
+ return top;
3019
+ }
3020
+ up(i) {
3021
+ while (i > 0) {
3022
+ const p = i - 1 >> 1;
3023
+ if (this.items[p].expiresAt <= this.items[i].expiresAt)
3024
+ return;
3025
+ [this.items[p], this.items[i]] = [
3026
+ this.items[i],
3027
+ this.items[p]
3028
+ ];
3029
+ i = p;
3030
+ }
3031
+ }
3032
+ down(i) {
3033
+ for (; ; ) {
3034
+ const left = i * 2 + 1;
3035
+ const right = left + 1;
3036
+ let smallest = i;
3037
+ if (left < this.items.length && this.expiresAt(left) < this.expiresAt(smallest))
3038
+ smallest = left;
3039
+ if (right < this.items.length && this.expiresAt(right) < this.expiresAt(smallest))
3040
+ smallest = right;
3041
+ if (smallest === i) return;
3042
+ [this.items[i], this.items[smallest]] = [
3043
+ this.items[smallest],
3044
+ this.items[i]
3045
+ ];
3046
+ i = smallest;
3047
+ }
3048
+ }
3049
+ expiresAt(i) {
3050
+ return this.items[i].expiresAt;
3051
+ }
3052
+ };
3053
+ function selectRetentionVictims(scoredEntries, config) {
3054
+ const { maxSize } = config;
3055
+ if (maxSize === void 0) return [];
3056
+ if (!Number.isInteger(maxSize) || maxSize < 0)
3057
+ throw new RangeError(`retention maxSize must be a non-negative integer (got ${maxSize})`);
3058
+ const overflow = scoredEntries.length - maxSize;
3059
+ if (overflow <= 0) return [];
3060
+ return scoredEntries.map((item, index) => {
3061
+ if (!Number.isFinite(item.score))
3062
+ throw new RangeError(`retention score must be finite (got ${item.score})`);
3063
+ return { ...item, index };
3064
+ }).sort((a, b) => a.score - b.score || a.index - b.index).slice(0, overflow).map((item) => item.entry);
3065
+ }
3066
+
3067
+ // src/graph/data-structures/reactive-list.ts
3068
+ var ListBackend = class {
3069
+ _version = 0;
3070
+ buf;
3071
+ constructor(initial, maxSize) {
3072
+ this.buf = initial ? [...initial] : [];
3073
+ trimHeadOverflow(this.buf, { maxSize });
3074
+ }
3075
+ get version() {
3076
+ return this._version;
3077
+ }
3078
+ get size() {
3079
+ return this.buf.length;
3080
+ }
3081
+ at(index) {
3082
+ if (!Number.isInteger(index)) return void 0;
3083
+ const i = index >= 0 ? index : this.buf.length + index;
3084
+ return i < 0 || i >= this.buf.length ? void 0 : this.buf[i];
3085
+ }
3086
+ snapshot() {
3087
+ return [...this.buf];
3088
+ }
3089
+ append(value) {
3090
+ this.buf.push(value);
3091
+ this._version += 1;
3092
+ }
3093
+ appendMany(values) {
3094
+ if (values.length === 0) return;
3095
+ for (const v of values) this.buf.push(v);
3096
+ this._version += 1;
3097
+ }
3098
+ insert(index, value) {
3099
+ if (!Number.isInteger(index) || index < 0 || index > this.buf.length)
3100
+ throw new RangeError(`insert: index ${index} out of range [0, ${this.buf.length}]`);
3101
+ this.buf.splice(index, 0, value);
3102
+ this._version += 1;
3103
+ }
3104
+ insertMany(index, values) {
3105
+ if (!Number.isInteger(index) || index < 0 || index > this.buf.length)
3106
+ throw new RangeError(`insertMany: index ${index} out of range [0, ${this.buf.length}]`);
3107
+ if (values.length === 0) return;
3108
+ this.buf.splice(index, 0, ...values);
3109
+ this._version += 1;
3110
+ }
3111
+ pop(index) {
3112
+ if (this.buf.length === 0) throw new RangeError("pop from empty list");
3113
+ if (!Number.isInteger(index)) throw new RangeError(`pop: index ${index} must be an integer`);
3114
+ const i = index >= 0 ? index : this.buf.length + index;
3115
+ if (i < 0 || i >= this.buf.length) throw new RangeError(`pop: index ${index} out of range`);
3116
+ const [v] = this.buf.splice(i, 1);
3117
+ this._version += 1;
3118
+ return v;
3119
+ }
3120
+ clear() {
3121
+ const n = this.buf.length;
3122
+ if (n === 0) return 0;
3123
+ this.buf.length = 0;
3124
+ this._version += 1;
3125
+ return n;
3126
+ }
3127
+ enforceMaxSize(maxSize) {
3128
+ const removed = trimHeadOverflow(this.buf, { maxSize }).length;
3129
+ if (removed > 0) this._version += 1;
3130
+ return removed;
3131
+ }
3132
+ };
3133
+ function reactiveList(initial, options = {}) {
3134
+ const { maxSize, graph, dispatcher } = options;
3135
+ const base = dispatcher ? { dispatcher } : {};
3136
+ function isNodeOpt(x) {
3137
+ return x instanceof Node;
3138
+ }
3139
+ function validateMaxSize(v) {
3140
+ if (!Number.isFinite(v) || !Number.isInteger(v) || v < 1)
3141
+ throw new RangeError(`reactiveList: maxSize must be a positive integer (got ${v})`);
3142
+ return v;
3143
+ }
3144
+ const initialMaxSize = isNodeOpt(maxSize) ? void 0 : maxSize;
3145
+ const backend = new ListBackend(
3146
+ initial,
3147
+ initialMaxSize === void 0 ? void 0 : validateMaxSize(initialMaxSize)
3148
+ );
3149
+ const restorable = graph !== void 0 && options.name !== void 0 && maxSize === void 0;
3150
+ const core = collectionCore(
3151
+ backend,
3152
+ "reactiveList",
3153
+ options,
3154
+ restorable ? {
3155
+ deltaRef: "reactiveList.delta",
3156
+ snapshotRef: "reactiveList.snapshot",
3157
+ backendState: () => backend.snapshot()
3158
+ } : void 0
3159
+ );
3160
+ const binds = [];
3161
+ const bindDeps = /* @__PURE__ */ new WeakSet();
3162
+ let bindSeq = 0;
3163
+ let capacityPolicy;
3164
+ let apply;
3165
+ let releaseApply = () => {
3166
+ };
3167
+ let currentMaxSize = initialMaxSize;
3168
+ function emitTrimmed(n) {
3169
+ if (n > 0) core.emit({ kind: "trimHead", n });
3170
+ }
3171
+ function enforceCapacity() {
3172
+ emitTrimmed(backend.enforceMaxSize(currentMaxSize));
3173
+ }
3174
+ const applyBody = (ctx) => {
3175
+ const deps = apply?.deps ?? [];
3176
+ for (let i = 0; i < depCount(ctx); i++) {
3177
+ const dep = deps[i];
3178
+ if (dep === capacityPolicy) {
3179
+ const latest = depLatest(ctx, i);
3180
+ if (latest !== void 0) currentMaxSize = validateMaxSize(latest);
3181
+ continue;
3182
+ }
3183
+ if (dep && bindDeps.has(dep)) {
3184
+ for (const value of depBatch(ctx, i) ?? []) {
3185
+ backend.append(value);
3186
+ core.emit({ kind: "append", value });
3187
+ enforceCapacity();
3188
+ }
3189
+ }
3190
+ }
3191
+ enforceCapacity();
3192
+ };
3193
+ function ensureApply() {
3194
+ if (apply !== void 0) return apply;
3195
+ const op = {
3196
+ factory: "reactiveList.capacityPolicy",
3197
+ body: applyBody,
3198
+ opts: { partial: true }
3199
+ };
3200
+ const deps = capacityPolicy ? [capacityPolicy] : [];
3201
+ apply = graph ? graph.initNode(op, deps, {
3202
+ name: options.name ? `${options.name}.capacityPolicy` : void 0,
3203
+ meta: { kind: "collection_policy_apply", collection: "reactiveList" }
3204
+ }) : new Node(deps, op.body, {
3205
+ ...base,
3206
+ factory: "reactiveList.capacityPolicy",
3207
+ partial: true,
3208
+ name: options.name ? `${options.name}.capacityPolicy` : void 0
3209
+ });
3210
+ releaseApply = graph ? graph.retain(apply, { reason: "reactiveList.capacityPolicy" }) : apply.subscribe(() => {
3211
+ });
3212
+ return apply;
3213
+ }
3214
+ function constPolicyNode(value) {
3215
+ const op = {
3216
+ factory: "reactiveList.maxSizePolicy",
3217
+ body: () => {
3218
+ },
3219
+ opts: { initial: value }
3220
+ };
3221
+ if (graph) {
3222
+ return graph.initNode(op, [], {
3223
+ name: options.name ? `${options.name}.maxSizePolicy` : void 0,
3224
+ meta: { kind: "collection_policy", collection: "reactiveList", policy: "maxSize" }
3225
+ });
3226
+ }
3227
+ return new Node([], null, {
3228
+ ...base,
3229
+ initial: value,
3230
+ factory: "reactiveList.maxSizePolicy",
3231
+ name: options.name ? `${options.name}.maxSizePolicy` : void 0
3232
+ });
3233
+ }
3234
+ if (maxSize !== void 0) {
3235
+ if (isNodeOpt(maxSize) && graph === void 0)
3236
+ throw new Error(
3237
+ "reactiveList maxSize Node option requires options.graph so the policy edge is describe-visible (D72)"
3238
+ );
3239
+ capacityPolicy = isNodeOpt(maxSize) ? maxSize : graph ? constPolicyNode(validateMaxSize(maxSize)) : void 0;
3240
+ if (capacityPolicy !== void 0) ensureApply();
3241
+ }
3242
+ return {
3243
+ delta: core.delta,
3244
+ snapshot: core.snapshot,
3245
+ pullId: core.pullId,
3246
+ get size() {
3247
+ return backend.size;
3248
+ },
3249
+ at(index) {
3250
+ return backend.at(index);
3251
+ },
3252
+ toArray() {
3253
+ return backend.snapshot();
3254
+ },
3255
+ append(value) {
3256
+ backend.append(value);
3257
+ core.emit({ kind: "append", value });
3258
+ enforceCapacity();
3259
+ },
3260
+ appendMany(values) {
3261
+ if (values.length === 0) return;
3262
+ const copy = [...values];
3263
+ backend.appendMany(copy);
3264
+ core.emit({ kind: "appendMany", values: copy });
3265
+ enforceCapacity();
3266
+ },
3267
+ insert(index, value) {
3268
+ backend.insert(index, value);
3269
+ core.emit({ kind: "insert", index, value });
3270
+ enforceCapacity();
3271
+ },
3272
+ insertMany(index, values) {
3273
+ if (values.length === 0) {
3274
+ backend.insertMany(index, values);
3275
+ return;
3276
+ }
3277
+ const copy = [...values];
3278
+ backend.insertMany(index, copy);
3279
+ core.emit({ kind: "insertMany", index, values: copy });
3280
+ enforceCapacity();
3281
+ },
3282
+ pop(index = -1) {
3283
+ const resolved = index < 0 ? backend.size + index : index;
3284
+ const value = backend.pop(index);
3285
+ core.emit({ kind: "pop", index: resolved, value });
3286
+ return value;
3287
+ },
3288
+ clear() {
3289
+ const count = backend.clear();
3290
+ if (count > 0) core.emit({ kind: "clear", count });
3291
+ },
3292
+ appendFrom(src) {
3293
+ if (graph === void 0)
3294
+ throw new Error(
3295
+ "reactiveList.appendFrom requires options.graph so the input fold is describe-visible (D61)"
3296
+ );
3297
+ const op = {
3298
+ factory: "reactiveList.bindSource",
3299
+ body: (ctx) => {
3300
+ try {
3301
+ for (const value of depBatch(ctx, 0) ?? []) {
3302
+ ctx.down([["DATA", value]]);
3303
+ }
3304
+ } catch (e) {
3305
+ ctx.down([["ERROR", errorPayload(e, "reactiveList.bindSource failed")]]);
3306
+ }
3307
+ }
3308
+ };
3309
+ const folder = graph.initNode(op, [src], {
3310
+ name: options.name ? `${options.name}.bind#${bindSeq++}` : void 0,
3311
+ meta: { kind: "collection_bind_source", collection: "reactiveList" }
3312
+ });
3313
+ bindDeps.add(folder);
3314
+ const applyNode = ensureApply();
3315
+ applyNode.subscribeDep(folder, applyBody);
3316
+ let active2 = true;
3317
+ const dispose = () => {
3318
+ if (!active2) return;
3319
+ active2 = false;
3320
+ applyNode.unsubscribeDep(folder, applyBody);
3321
+ };
3322
+ binds.push(dispose);
3323
+ return dispose;
3324
+ },
3325
+ dispose() {
3326
+ for (const d of binds) d();
3327
+ binds.length = 0;
3328
+ releaseApply();
3329
+ }
3330
+ };
3331
+ }
3332
+
3333
+ // src/graph/data-structures/reactive-log.ts
3334
+ var LogBackend = class {
3335
+ _version = 0;
3336
+ buf;
3337
+ maxSize;
3338
+ constructor(initial, maxSize) {
3339
+ if (maxSize !== void 0 && maxSize < 1)
3340
+ throw new RangeError("reactiveLog: maxSize must be >= 1");
3341
+ this.maxSize = maxSize;
3342
+ this.buf = initial ? [...initial] : [];
3343
+ trimHeadOverflow(this.buf, { maxSize });
3344
+ }
3345
+ get version() {
3346
+ return this._version;
3347
+ }
3348
+ get size() {
3349
+ return this.buf.length;
3350
+ }
3351
+ at(index) {
3352
+ if (!Number.isInteger(index)) return void 0;
3353
+ const i = index >= 0 ? index : this.buf.length + index;
3354
+ return i < 0 || i >= this.buf.length ? void 0 : this.buf[i];
3355
+ }
3356
+ snapshot() {
3357
+ return [...this.buf];
3358
+ }
3359
+ append(value) {
3360
+ this.buf.push(value);
3361
+ trimHeadOverflow(this.buf, { maxSize: this.maxSize });
3362
+ this._version += 1;
3363
+ }
3364
+ appendMany(values) {
3365
+ if (values.length === 0) return;
3366
+ for (const v of values) this.buf.push(v);
3367
+ trimHeadOverflow(this.buf, { maxSize: this.maxSize });
3368
+ this._version += 1;
3369
+ }
3370
+ clear() {
3371
+ const n = this.buf.length;
3372
+ if (n === 0) return 0;
3373
+ this.buf.length = 0;
3374
+ this._version += 1;
3375
+ return n;
3376
+ }
3377
+ trimHead(n) {
3378
+ if (!Number.isInteger(n) || n < 0)
3379
+ throw new RangeError(`trimHead: n must be a non-negative integer (got ${n})`);
3380
+ if (n === 0 || this.buf.length === 0) return 0;
3381
+ const removed = Math.min(n, this.buf.length);
3382
+ this.buf.splice(0, removed);
3383
+ this._version += 1;
3384
+ return removed;
3385
+ }
3386
+ };
3387
+ function rejectUndefined(value, ctx) {
3388
+ if (value === void 0)
3389
+ throw new TypeError(
3390
+ `${ctx}: undefined is the substrate SENTINEL, not a valid value \u2014 use null`
3391
+ );
3392
+ }
3393
+ function reactiveLog(initial, options = {}) {
3394
+ const { maxSize, viewCache, ...coreOpts } = options;
3395
+ const base = coreOpts.dispatcher ? { dispatcher: coreOpts.dispatcher } : {};
3396
+ const backend = new LogBackend(initial, maxSize);
3397
+ const restoreConfig = coreOpts.graph !== void 0 && coreOpts.name !== void 0 ? {
3398
+ ...maxSize !== void 0 ? { maxSize } : {}
3399
+ } : void 0;
3400
+ const core = collectionCore(
3401
+ backend,
3402
+ "reactiveLog",
3403
+ coreOpts,
3404
+ restoreConfig !== void 0 ? {
3405
+ deltaRef: "reactiveLog.delta",
3406
+ snapshotRef: "reactiveLog.snapshot",
3407
+ config: restoreConfig,
3408
+ backendState: () => backend.snapshot()
3409
+ } : void 0
3410
+ );
3411
+ const binds = [];
3412
+ const tailMemo = /* @__PURE__ */ new Map();
3413
+ const sliceMemo = /* @__PURE__ */ new Map();
3414
+ const pageMemo = new ViewMemoCache(viewCache);
3415
+ let pageSeq = 0;
3416
+ return {
3417
+ delta: core.delta,
3418
+ snapshot: core.snapshot,
3419
+ pullId: core.pullId,
3420
+ get size() {
3421
+ return backend.size;
3422
+ },
3423
+ at(index) {
3424
+ return backend.at(index);
3425
+ },
3426
+ toArray() {
3427
+ return backend.snapshot();
3428
+ },
3429
+ append(value) {
3430
+ rejectUndefined(value, "reactiveLog.append");
3431
+ backend.append(value);
3432
+ core.emit({ kind: "append", value });
3433
+ },
3434
+ appendMany(values) {
3435
+ if (values.length === 0) return;
3436
+ for (const v of values) rejectUndefined(v, "reactiveLog.appendMany");
3437
+ const copy = [...values];
3438
+ backend.appendMany(copy);
3439
+ core.emit({ kind: "appendMany", values: copy });
3440
+ },
3441
+ clear() {
3442
+ const count = backend.clear();
3443
+ if (count > 0) core.emit({ kind: "clear", count });
3444
+ },
3445
+ trimHead(n) {
3446
+ const removed = backend.trimHead(n);
3447
+ if (removed > 0) core.emit({ kind: "trimHead", n: removed });
3448
+ },
3449
+ tail(n) {
3450
+ if (!Number.isInteger(n) || n < 0)
3451
+ throw new RangeError(`tail: n must be a non-negative integer (got ${n})`);
3452
+ const hit = tailMemo.get(n);
3453
+ if (hit !== void 0) return hit;
3454
+ const tnode = node(
3455
+ [core.delta],
3456
+ (ctx) => {
3457
+ const all = backend.snapshot();
3458
+ ctx.down([["DATA", n === 0 ? [] : all.slice(Math.max(0, all.length - n))]]);
3459
+ },
3460
+ { ...base, factory: "reactiveLog.tail", partial: true }
3461
+ );
3462
+ tailMemo.set(n, tnode);
3463
+ return tnode;
3464
+ },
3465
+ slice(start, stop) {
3466
+ if (!Number.isInteger(start) || start < 0) {
3467
+ throw new RangeError(`slice: start must be a non-negative integer (got ${start})`);
3468
+ }
3469
+ if (stop !== void 0 && (!Number.isInteger(stop) || stop < 0)) {
3470
+ throw new RangeError(`slice: stop must be a non-negative integer (got ${stop})`);
3471
+ }
3472
+ const key = `${start}:${stop ?? ""}`;
3473
+ const hit = sliceMemo.get(key);
3474
+ if (hit !== void 0) return hit;
3475
+ const snode = node(
3476
+ [core.delta],
3477
+ (ctx) => {
3478
+ ctx.down([["DATA", backend.snapshot().slice(start, stop)]]);
3479
+ },
3480
+ { ...base, factory: "reactiveLog.slice", partial: true }
3481
+ );
3482
+ sliceMemo.set(key, snode);
3483
+ return snode;
3484
+ },
3485
+ page(offset, limit) {
3486
+ if (!Number.isInteger(offset) || offset < 0) {
3487
+ throw new RangeError(`page: offset must be a non-negative integer (got ${offset})`);
3488
+ }
3489
+ if (!Number.isInteger(limit) || limit < 0) {
3490
+ throw new RangeError(`page: limit must be a non-negative integer (got ${limit})`);
3491
+ }
3492
+ const key = `${offset}:${limit}`;
3493
+ const hit = pageMemo.get(key);
3494
+ if (hit !== void 0) return hit;
3495
+ const materialize = () => backend.snapshot().slice(offset, offset + limit);
3496
+ let view;
3497
+ view = lightReactiveView(core.delta, {
3498
+ ...coreOpts,
3499
+ factory: "reactiveLog.page",
3500
+ name: coreOpts.name ? `${coreOpts.name}.page#${pageSeq++}` : void 0,
3501
+ materializeSnapshot: materialize,
3502
+ onDispose: () => {
3503
+ pageMemo.deleteValue(view);
3504
+ }
3505
+ });
3506
+ pageMemo.set(key, view);
3507
+ return view;
3508
+ },
3509
+ scan(initial2, step) {
3510
+ return node(
3511
+ [core.delta],
3512
+ (ctx) => {
3513
+ const st = ctx.state.get() ?? {
3514
+ acc: initial2,
3515
+ processed: 0
3516
+ };
3517
+ const all = backend.snapshot();
3518
+ if (all.length < st.processed || maxSize !== void 0 && all.length <= st.processed) {
3519
+ let acc = initial2;
3520
+ for (const v of all) acc = step(acc, v);
3521
+ st.acc = acc;
3522
+ st.processed = all.length;
3523
+ } else {
3524
+ for (let i = st.processed; i < all.length; i++) st.acc = step(st.acc, all[i]);
3525
+ st.processed = all.length;
3526
+ }
3527
+ ctx.state.set(st);
3528
+ ctx.down([["DATA", st.acc]]);
3529
+ },
3530
+ { ...base, factory: "reactiveLog.scan", partial: true }
3531
+ );
3532
+ },
3533
+ attach(src) {
3534
+ const dispose = core.bindSource(src, (value) => {
3535
+ rejectUndefined(value, "reactiveLog.attach");
3536
+ backend.append(value);
3537
+ core.emit({ kind: "append", value });
3538
+ });
3539
+ binds.push(dispose);
3540
+ return dispose;
3541
+ },
3542
+ dispose() {
3543
+ for (const d of binds) d();
3544
+ binds.length = 0;
3545
+ tailMemo.clear();
3546
+ sliceMemo.clear();
3547
+ pageMemo.disposeAll();
3548
+ }
3549
+ };
3550
+ }
3551
+ function mergeReactiveLogs(logs) {
3552
+ const deps = logs.map((l) => l.delta);
3553
+ return node(
3554
+ deps,
3555
+ (ctx) => {
3556
+ for (let i = 0; i < depCount(ctx); i++) {
3557
+ const b = depBatch(ctx, i);
3558
+ if (b) for (const c of b) ctx.down([["DATA", c]]);
3559
+ }
3560
+ },
3561
+ { factory: "mergeReactiveLogs", partial: true }
3562
+ );
3563
+ }
3564
+ function scanLog(log, initial, step) {
3565
+ return log.scan(initial, step);
3566
+ }
3567
+
3568
+ // src/graph/operators.ts
3569
+ function initNode(op, deps, opts = {}) {
3570
+ return makeInitNode(op, deps, opts);
3571
+ }
3572
+ function makeInitNode(op, deps, opts) {
3573
+ const body = operatorNodeFn(op);
3574
+ return new Node([...deps], body, { factory: op.factory, ...op.opts, ...opts });
3575
+ }
3576
+ function operatorNodeFn(op) {
3577
+ return (ctx) => {
3578
+ try {
3579
+ op.body(ctx);
3580
+ } catch (e) {
3581
+ ctx.down([["ERROR", errorPayload(e, "operator threw without a valid error payload")]]);
3582
+ }
3583
+ };
3584
+ }
3585
+
3586
+ // src/graph/sources.ts
3587
+ function source(factory, setup, opts) {
3588
+ return {
3589
+ factory,
3590
+ opts,
3591
+ body: (ctx) => {
3592
+ let cleanup;
3593
+ let deactivated = false;
3594
+ ctx.onDeactivation(() => {
3595
+ deactivated = true;
3596
+ if (typeof cleanup === "function") cleanup();
3597
+ });
3598
+ const maybeCleanup = setup(ctx);
3599
+ cleanup = typeof maybeCleanup === "function" ? maybeCleanup : void 0;
3600
+ if (deactivated && typeof cleanup === "function") cleanup();
3601
+ }
3602
+ };
3603
+ }
3604
+ function timerSource(factory, ms, opts) {
3605
+ const { period, signal } = opts ?? {};
3606
+ const op = source(
3607
+ factory,
3608
+ (ctx) => {
3609
+ let done = false;
3610
+ let count = 0;
3611
+ let t;
3612
+ let iv;
3613
+ const cleanup = () => {
3614
+ done = true;
3615
+ if (t !== void 0) clearTimeout(t);
3616
+ if (iv !== void 0) clearInterval(iv);
3617
+ signal?.removeEventListener("abort", onAbort);
3618
+ };
3619
+ const onAbort = () => {
3620
+ if (done) return;
3621
+ cleanup();
3622
+ ctx.down([["ERROR", errorPayload(signal?.reason)]]);
3623
+ };
3624
+ const finish = () => {
3625
+ if (done) return;
3626
+ if (period != null) {
3627
+ ctx.down([["DATA", count++]]);
3628
+ iv = setInterval(() => {
3629
+ if (done) return;
3630
+ ctx.down([["DATA", count++]]);
3631
+ }, period);
3632
+ } else {
3633
+ done = true;
3634
+ signal?.removeEventListener("abort", onAbort);
3635
+ ctx.down([["DATA", count++], ["COMPLETE"]]);
3636
+ }
3637
+ };
3638
+ if (signal?.aborted) {
3639
+ onAbort();
3640
+ return;
3641
+ }
3642
+ signal?.addEventListener("abort", onAbort, { once: true });
3643
+ t = setTimeout(finish, ms);
3644
+ return cleanup;
3645
+ },
3646
+ { pool: "sync", pausable: false }
3647
+ );
3648
+ if (factory === "timer" && period === void 0 && signal === void 0) {
3649
+ return { ...op, restore: { ref: "timer", config: { ms: toCheckpointJson(ms, "timer.ms") } } };
3650
+ }
3651
+ return op;
3652
+ }
3653
+ function timer(ms, opts) {
3654
+ return timerSource("timer", ms, opts);
3655
+ }
3656
+
3657
+ // src/graph/data-structures/reactive-map.ts
3658
+ function strictJsonIdentity(value) {
3659
+ return Array.from(strictCanonicalJsonBytes(value)).join(",");
3660
+ }
3661
+ var MapBackend = class {
3662
+ _version = 0;
3663
+ store = /* @__PURE__ */ new Map();
3664
+ now;
3665
+ constructor(opts) {
3666
+ this.now = opts.now ?? Date.now;
3667
+ }
3668
+ get version() {
3669
+ return this._version;
3670
+ }
3671
+ get size() {
3672
+ return this.store.size;
3673
+ }
3674
+ nowMs() {
3675
+ return this.now();
3676
+ }
3677
+ expiresAtOf(key) {
3678
+ return this.store.get(key)?.expiresAt;
3679
+ }
3680
+ isExpired(e) {
3681
+ return e.expiresAt !== void 0 && this.now() >= e.expiresAt;
3682
+ }
3683
+ resolveExpiresAt(ttl) {
3684
+ if (ttl === void 0) return void 0;
3685
+ if (!Number.isFinite(ttl) || ttl <= 0)
3686
+ throw new RangeError(`reactiveMap: ttl must be a positive finite number (got ${ttl})`);
3687
+ return this.now() + ttl;
3688
+ }
3689
+ deleteExpired(key, e) {
3690
+ this.store.delete(key);
3691
+ this._version += 1;
3692
+ return [[key, e.value]];
3693
+ }
3694
+ /** Read without materializing expiry. Caller routes expired deletes through the apply node (D68). */
3695
+ lookup(key) {
3696
+ const e = this.store.get(key);
3697
+ if (e === void 0) return { found: false };
3698
+ if (this.isExpired(e)) return { found: false, expired: true, previous: e.value };
3699
+ this.store.delete(key);
3700
+ this.store.set(key, e);
3701
+ return { found: true, value: e.value };
3702
+ }
3703
+ /** Set; returns the entries evicted by LRU overflow (the caller emits their delete deltas). */
3704
+ set(key, value, expiresAt) {
3705
+ if (this.store.has(key)) this.store.delete(key);
3706
+ this.store.set(key, { value, expiresAt });
3707
+ this._version += 1;
3708
+ }
3709
+ enforceLru(maxSize) {
3710
+ const evicted = [];
3711
+ for (const oldest of trimHeadOverflow(lruKeys(this.store.keys()).keys(), {
3712
+ maxSize,
3713
+ size: this.store.size
3714
+ })) {
3715
+ const e = this.store.get(oldest);
3716
+ if (e !== void 0) evicted.push([oldest, e.value]);
3717
+ this.store.delete(oldest);
3718
+ }
3719
+ if (evicted.length > 0) this._version += 1;
3720
+ return evicted;
3721
+ }
3722
+ enforceRetention(policy) {
3723
+ if (policy === void 0 || policy.maxSize === void 0) return [];
3724
+ const scored = [];
3725
+ for (const [key, entry] of this.store) {
3726
+ if (!this.isExpired(entry)) {
3727
+ scored.push({
3728
+ entry: [key, entry.value],
3729
+ score: policy.score({ key, value: entry.value })
3730
+ });
3731
+ }
3732
+ }
3733
+ const archived = selectRetentionVictims(scored, { maxSize: policy.maxSize });
3734
+ for (const [key] of archived) this.store.delete(key);
3735
+ if (archived.length > 0) this._version += 1;
3736
+ return archived.map(([key, value]) => [key, value]);
3737
+ }
3738
+ delete(key) {
3739
+ const e = this.store.get(key);
3740
+ if (e === void 0) return { found: false };
3741
+ this.store.delete(key);
3742
+ this._version += 1;
3743
+ return { found: true, value: e.value };
3744
+ }
3745
+ clear() {
3746
+ const n = this.store.size;
3747
+ if (n === 0) return 0;
3748
+ this.store.clear();
3749
+ this._version += 1;
3750
+ return n;
3751
+ }
3752
+ pruneExpired() {
3753
+ const removed = [];
3754
+ for (const [k, e] of this.store) {
3755
+ if (this.isExpired(e)) {
3756
+ this.store.delete(k);
3757
+ removed.push([k, e.value]);
3758
+ }
3759
+ }
3760
+ if (removed.length > 0) this._version += 1;
3761
+ return removed;
3762
+ }
3763
+ pruneKeyIfExpired(key) {
3764
+ const e = this.store.get(key);
3765
+ if (e === void 0 || !this.isExpired(e)) return [];
3766
+ return this.deleteExpired(key, e);
3767
+ }
3768
+ /** Fresh snapshot of live entries. Does not prune; callers choose whether to materialize expiry. */
3769
+ snapshot() {
3770
+ const out = /* @__PURE__ */ new Map();
3771
+ for (const [k, e] of this.store) if (!this.isExpired(e)) out.set(k, e.value);
3772
+ return out;
3773
+ }
3774
+ /** D160 checkpoint payload: exact restorable map entries, excluding TTL-bearing state. */
3775
+ checkpointEntries() {
3776
+ const out = [];
3777
+ const seen = /* @__PURE__ */ new Set();
3778
+ for (const [key, entry] of this.store) {
3779
+ if (entry.expiresAt !== void 0) {
3780
+ throw new TypeError(
3781
+ "reactiveMap checkpoint backendState cannot preserve per-entry TTL; persist the map externally or clear TTL state before graph checkpoint (D160)"
3782
+ );
3783
+ }
3784
+ const stableKey = strictJsonIdentity(key);
3785
+ if (seen.has(stableKey)) {
3786
+ throw new TypeError(
3787
+ "reactiveMap checkpoint backendState contains duplicate strict-JSON keys (D160)"
3788
+ );
3789
+ }
3790
+ seen.add(stableKey);
3791
+ out.push([key, entry.value]);
3792
+ }
3793
+ return out;
3794
+ }
3795
+ };
3796
+ function reactiveMap(options = {}) {
3797
+ return createReactiveMap(options);
3798
+ }
3799
+ function restoreReactiveMapFromBackendState(initialEntries, options = {}) {
3800
+ return createReactiveMap(options, initialEntries);
3801
+ }
3802
+ function createReactiveMap(options = {}, initialEntries = []) {
3803
+ const { maxSize, defaultTtl, retention, viewCache, now, name, dispatcher, graph } = options;
3804
+ const backend = new MapBackend({ now });
3805
+ for (const [key, value] of initialEntries) backend.set(key, value);
3806
+ const base = dispatcher ? { dispatcher } : {};
3807
+ const restorable = graph !== void 0 && name !== void 0 && maxSize === void 0 && defaultTtl === void 0 && retention === void 0;
3808
+ function isNodeOpt(x) {
3809
+ return x instanceof Node;
3810
+ }
3811
+ function validateMaxSize(v) {
3812
+ if (!Number.isFinite(v) || !Number.isInteger(v) || v < 1)
3813
+ throw new RangeError(`reactiveMap: maxSize must be a positive integer (got ${v})`);
3814
+ return v;
3815
+ }
3816
+ function validateTtl(v) {
3817
+ if (!Number.isFinite(v) || v <= 0)
3818
+ throw new RangeError(`reactiveMap: defaultTtl must be a positive finite number (got ${v})`);
3819
+ return v;
3820
+ }
3821
+ function validateRetentionMaxSize(v) {
3822
+ if (!Number.isFinite(v) || !Number.isInteger(v) || v < 0)
3823
+ throw new RangeError(
3824
+ `reactiveMap: retention.maxSize must be a non-negative integer (got ${v})`
3825
+ );
3826
+ return v;
3827
+ }
3828
+ if (retention !== void 0 && typeof retention.score !== "function")
3829
+ throw new TypeError("reactiveMap: retention.score must be a function");
3830
+ function constPolicyNode(label, value) {
3831
+ const op = {
3832
+ factory: `reactiveMap.${label}`,
3833
+ body: () => {
3834
+ },
3835
+ opts: { initial: value }
3836
+ };
3837
+ if (graph) {
3838
+ return graph.initNode(op, [], {
3839
+ name: name ? `${name}.${label}` : void 0,
3840
+ meta: { kind: "collection_policy", collection: "reactiveMap", policy: label }
3841
+ });
3842
+ }
3843
+ return new Node([], null, {
3844
+ ...base,
3845
+ initial: value,
3846
+ factory: `reactiveMap.${label}`,
3847
+ name: name ? `${name}.${label}` : void 0
3848
+ });
3849
+ }
3850
+ function policyNode(label, opt, validate) {
3851
+ if (opt === void 0) return void 0;
3852
+ if (isNodeOpt(opt)) {
3853
+ if (graph === void 0)
3854
+ throw new Error(
3855
+ `reactiveMap ${label} Node option requires options.graph so the policy edge is describe-visible (${label === "retentionPolicy" ? "D72" : "D68"})`
3856
+ );
3857
+ return opt;
3858
+ }
3859
+ return constPolicyNode(label, validate(opt));
3860
+ }
3861
+ const intentOp = {
3862
+ factory: "reactiveMap.intent",
3863
+ body: () => {
3864
+ }
3865
+ };
3866
+ const intent = graph ? graph.initNode(intentOp, [], {
3867
+ name: name ? `${name}.intent` : void 0,
3868
+ meta: { kind: "collection_intent", collection: "reactiveMap" },
3869
+ ...restorable ? { restore: { ref: "reactiveMap.intent" } } : {}
3870
+ }) : new Node([], null, {
3871
+ ...base,
3872
+ factory: "reactiveMap.intent",
3873
+ name: name ? `${name}.intent` : void 0
3874
+ });
3875
+ const lruPolicy = policyNode("lruPolicy", maxSize, validateMaxSize);
3876
+ const ttlPolicy = policyNode("ttlPolicy", defaultTtl, validateTtl);
3877
+ const retentionPolicy = policyNode(
3878
+ "retentionPolicy",
3879
+ retention?.maxSize,
3880
+ validateRetentionMaxSize
3881
+ );
3882
+ let currentMaxSize = isNodeOpt(maxSize) ? void 0 : maxSize;
3883
+ let currentDefaultTtl = isNodeOpt(defaultTtl) ? void 0 : defaultTtl;
3884
+ let currentRetentionMaxSize = isNodeOpt(retention?.maxSize) ? void 0 : retention?.maxSize === void 0 ? void 0 : validateRetentionMaxSize(retention.maxSize);
3885
+ const ttlHeap = deadlines();
3886
+ let ttlTimer = null;
3887
+ let ttlTimerDue;
3888
+ const bindDeps = /* @__PURE__ */ new WeakSet();
3889
+ let bindSeq = 0;
3890
+ let apply;
3891
+ const selectMemo = new ViewMemoCache(viewCache);
3892
+ let selectSeq = 0;
3893
+ const policyInputs = new PolicyInputs([intent]);
3894
+ const lruReader = policyInputs.add(lruPolicy);
3895
+ const ttlReader = policyInputs.add(ttlPolicy);
3896
+ const retentionReader = policyInputs.add(retentionPolicy);
3897
+ function emitExpired(ctx, entries) {
3898
+ for (const [key, previous] of entries)
3899
+ ctx.down([["DATA", { kind: "delete", key, previous, reason: "expired" }]]);
3900
+ }
3901
+ function emitLruEvicted(ctx, entries) {
3902
+ for (const [key, previous] of entries)
3903
+ ctx.down([["DATA", { kind: "delete", key, previous, reason: "lru-evict" }]]);
3904
+ }
3905
+ function emitArchived(ctx, entries) {
3906
+ for (const [key, previous] of entries)
3907
+ ctx.down([["DATA", { kind: "delete", key, previous, reason: "archived" }]]);
3908
+ }
3909
+ function nextValidExpiry() {
3910
+ for (; ; ) {
3911
+ const top = ttlHeap.peek();
3912
+ if (top === void 0) return void 0;
3913
+ if (backend.expiresAtOf(top.key) === top.expiresAt) return top;
3914
+ ttlHeap.pop();
3915
+ }
3916
+ }
3917
+ function scheduleTtl(ctx, body) {
3918
+ for (; ; ) {
3919
+ const next = nextValidExpiry();
3920
+ if (next === void 0) {
3921
+ if (ttlTimer) ctx.rewireNext.unsubscribeDep(ttlTimer, body);
3922
+ ttlTimer = null;
3923
+ ttlTimerDue = void 0;
3924
+ return;
3925
+ }
3926
+ if (next.expiresAt <= backend.nowMs()) {
3927
+ emitExpired(ctx, backend.pruneExpired());
3928
+ continue;
3929
+ }
3930
+ if (ttlTimerDue === next.expiresAt) return;
3931
+ if (ttlTimer) ctx.rewireNext.unsubscribeDep(ttlTimer, body);
3932
+ ttlTimerDue = next.expiresAt;
3933
+ ttlTimer = initNode(timer(Math.max(0, next.expiresAt - backend.nowMs())), [], base);
3934
+ ctx.rewireNext.subscribeDep(ttlTimer, body);
3935
+ return;
3936
+ }
3937
+ }
3938
+ function applyIntent(ctx, intentValue) {
3939
+ if (intentValue.kind === "set") {
3940
+ const ttl = intentValue.ttl ?? currentDefaultTtl;
3941
+ const expiresAt = backend.resolveExpiresAt(ttl);
3942
+ backend.set(intentValue.key, intentValue.value, expiresAt);
3943
+ if (expiresAt !== void 0) ttlHeap.push({ key: intentValue.key, expiresAt });
3944
+ ctx.down([["DATA", { kind: "set", key: intentValue.key, value: intentValue.value }]]);
3945
+ emitLruEvicted(ctx, backend.enforceLru(currentMaxSize));
3946
+ emitArchived(
3947
+ ctx,
3948
+ backend.enforceRetention(
3949
+ retention ? { maxSize: currentRetentionMaxSize, score: retention.score } : void 0
3950
+ )
3951
+ );
3952
+ return;
3953
+ }
3954
+ if (intentValue.kind === "delete") {
3955
+ const previous = backend.delete(intentValue.key);
3956
+ if (previous.found)
3957
+ ctx.down([
3958
+ [
3959
+ "DATA",
3960
+ { kind: "delete", key: intentValue.key, previous: previous.value, reason: "explicit" }
3961
+ ]
3962
+ ]);
3963
+ return;
3964
+ }
3965
+ if (intentValue.kind === "clear") {
3966
+ const count = backend.clear();
3967
+ if (count > 0) ctx.down([["DATA", { kind: "clear", count }]]);
3968
+ return;
3969
+ }
3970
+ if (intentValue.kind === "pruneExpired") {
3971
+ emitExpired(ctx, backend.pruneExpired());
3972
+ return;
3973
+ }
3974
+ emitExpired(ctx, backend.pruneKeyIfExpired(intentValue.key));
3975
+ }
3976
+ const applyBody = (ctx) => {
3977
+ const nextMaxSize = lruReader.read(ctx, currentMaxSize);
3978
+ currentMaxSize = nextMaxSize === void 0 ? void 0 : validateMaxSize(nextMaxSize);
3979
+ const nextTtl = ttlReader.read(ctx, currentDefaultTtl);
3980
+ currentDefaultTtl = nextTtl === void 0 ? void 0 : validateTtl(nextTtl);
3981
+ const nextRetentionMaxSize = retentionReader.read(ctx, currentRetentionMaxSize);
3982
+ currentRetentionMaxSize = nextRetentionMaxSize === void 0 ? void 0 : validateRetentionMaxSize(nextRetentionMaxSize);
3983
+ for (const item of depBatch(ctx, 0) ?? []) {
3984
+ applyIntent(ctx, item);
3985
+ }
3986
+ const deps = apply.deps;
3987
+ for (let i = 1; i < depCount(ctx); i++) {
3988
+ if (i === lruReader.index || i === ttlReader.index || i === retentionReader.index) continue;
3989
+ const dep = deps[i];
3990
+ if (dep && bindDeps.has(dep)) {
3991
+ for (const item of depBatch(ctx, i) ?? []) {
3992
+ applyIntent(ctx, item);
3993
+ }
3994
+ continue;
3995
+ }
3996
+ if (dep === ttlTimer && ((depBatch(ctx, i)?.length ?? 0) > 0 || isTerminalComplete(depTerminal(ctx, i)))) {
3997
+ if (ttlTimer) ctx.rewireNext.unsubscribeDep(ttlTimer, applyBody);
3998
+ ttlTimer = null;
3999
+ ttlTimerDue = void 0;
4000
+ emitExpired(ctx, backend.pruneExpired());
4001
+ }
4002
+ }
4003
+ emitLruEvicted(ctx, backend.enforceLru(currentMaxSize));
4004
+ emitArchived(
4005
+ ctx,
4006
+ backend.enforceRetention(
4007
+ retention ? { maxSize: currentRetentionMaxSize, score: retention.score } : void 0
4008
+ )
4009
+ );
4010
+ scheduleTtl(ctx, applyBody);
4011
+ };
4012
+ const applyOp = {
4013
+ factory: "reactiveMap.apply",
4014
+ body: applyBody,
4015
+ opts: { partial: true }
4016
+ };
4017
+ apply = graph ? graph.initNode(applyOp, policyInputs.deps, {
4018
+ name: name ? `${name}.apply` : void 0,
4019
+ meta: { kind: "collection_policy_apply", collection: "reactiveMap" },
4020
+ ...restorable ? { restore: { ref: "reactiveMap.apply" } } : {}
4021
+ }) : initNode(applyOp, policyInputs.deps, {
4022
+ ...base,
4023
+ name: name ? `${name}.apply` : void 0
4024
+ });
4025
+ const pullId = /* @__PURE__ */ Symbol(name ? `${name}.snapshot` : "reactiveMap.snapshot");
4026
+ const snapshotPrepOp = {
4027
+ factory: "reactiveMap.snapshotPrep",
4028
+ body: (ctx) => {
4029
+ const expired = backend.pruneExpired().map(
4030
+ ([key, previous]) => ({
4031
+ kind: "delete",
4032
+ key,
4033
+ previous,
4034
+ reason: "expired"
4035
+ })
4036
+ );
4037
+ ctx.down([["DATA", { snapshot: backend.snapshot(), expired }]]);
4038
+ },
4039
+ opts: {
4040
+ ...base,
4041
+ pullId,
4042
+ partial: true
4043
+ }
4044
+ };
4045
+ const snapshotPrep = graph ? graph.initNode(snapshotPrepOp, [apply], {
4046
+ name: name ? `${name}.snapshotPrep` : void 0,
4047
+ meta: { kind: "collection_snapshot_prep", collection: "reactiveMap" },
4048
+ ...restorable ? { restore: { ref: "reactiveMap.snapshotPrep" } } : {}
4049
+ }) : initNode(snapshotPrepOp, [apply], {
4050
+ ...base,
4051
+ name: name ? `${name}.snapshotPrep` : void 0
4052
+ });
4053
+ const deltaOp = {
4054
+ factory: "reactiveMap.delta",
4055
+ body: (ctx) => {
4056
+ for (const change of depBatch(ctx, 0) ?? []) {
4057
+ ctx.down([["DATA", change]]);
4058
+ }
4059
+ for (const prep of depBatch(ctx, 1) ?? []) {
4060
+ for (const change of prep.expired) ctx.down([["DATA", change]]);
4061
+ }
4062
+ },
4063
+ opts: { partial: true }
4064
+ };
4065
+ const delta = graph ? graph.initNode(deltaOp, [apply, snapshotPrep], {
4066
+ name: name ? `${name}.delta` : void 0,
4067
+ meta: { kind: "collection_delta", collection: "reactiveMap" },
4068
+ ...restorable ? { restore: { ref: "reactiveMap.delta" } } : {}
4069
+ }) : initNode(deltaOp, [apply, snapshotPrep], {
4070
+ ...base,
4071
+ name: name ? `${name}.delta` : void 0
4072
+ });
4073
+ const snapshotOp = {
4074
+ factory: "reactiveMap.snapshot",
4075
+ body: (ctx) => {
4076
+ for (const prep of depBatch(ctx, 0) ?? []) {
4077
+ ctx.down([["DATA", prep.snapshot]]);
4078
+ }
4079
+ },
4080
+ opts: { partial: true }
4081
+ };
4082
+ const snapshot = graph ? graph.initNode(snapshotOp, [snapshotPrep], {
4083
+ name: name ? `${name}.snapshot` : void 0,
4084
+ meta: { kind: "collection_snapshot", collection: "reactiveMap" },
4085
+ ...restorable ? { restore: { ref: "reactiveMap.snapshot" } } : {}
4086
+ }) : initNode(snapshotOp, [snapshotPrep], {
4087
+ ...base,
4088
+ name: name ? `${name}.snapshot` : void 0
4089
+ });
4090
+ if (restorable) {
4091
+ registerBackendStateContributor(intent, () => backend.checkpointEntries());
4092
+ }
4093
+ const releaseApply = graph ? graph.retain(apply, { reason: "reactiveMap.apply" }) : apply.subscribe(() => {
4094
+ });
4095
+ const binds = [];
4096
+ function doSet(key, value, ttl) {
4097
+ if (ttl !== void 0) validateTtl(ttl);
4098
+ intent.down([
4099
+ ["DATA", ttl === void 0 ? { kind: "set", key, value } : { kind: "set", key, value, ttl }]
4100
+ ]);
4101
+ }
4102
+ function read(key) {
4103
+ const r = backend.lookup(key);
4104
+ if (!r.found && r.expired) intent.down([["DATA", { kind: "pruneKey", key }]]);
4105
+ return r;
4106
+ }
4107
+ function selectedSnapshot(predicate) {
4108
+ const out = /* @__PURE__ */ new Map();
4109
+ for (const [key, value] of backend.snapshot()) {
4110
+ if (predicate(value, key)) out.set(key, value);
4111
+ }
4112
+ return out;
4113
+ }
4114
+ function select(predicate) {
4115
+ if (typeof predicate !== "function")
4116
+ throw new TypeError("reactiveMap.select: predicate must be a function");
4117
+ const cached = selectMemo.get(predicate);
4118
+ if (cached !== void 0) return cached;
4119
+ let view;
4120
+ view = lightReactiveView(delta, {
4121
+ dispatcher,
4122
+ graph,
4123
+ factory: "reactiveMap.select",
4124
+ name: name ? `${name}.select#${selectSeq++}` : void 0,
4125
+ materializeSnapshot: () => selectedSnapshot(predicate),
4126
+ onDispose: () => {
4127
+ selectMemo.deleteValue(view);
4128
+ }
4129
+ });
4130
+ selectMemo.set(predicate, view);
4131
+ return view;
4132
+ }
4133
+ return {
4134
+ delta,
4135
+ snapshot,
4136
+ pullId,
4137
+ get size() {
4138
+ return backend.size;
4139
+ },
4140
+ get(key) {
4141
+ const r = read(key);
4142
+ return r.found ? r.value : void 0;
4143
+ },
4144
+ has(key) {
4145
+ return read(key).found;
4146
+ },
4147
+ toMap() {
4148
+ intent.down([["DATA", { kind: "pruneExpired" }]]);
4149
+ return backend.snapshot();
4150
+ },
4151
+ select,
4152
+ set(key, value, opts) {
4153
+ doSet(key, value, opts?.ttl);
4154
+ },
4155
+ setMany(entries, opts) {
4156
+ for (const [k, v] of entries) doSet(k, v, opts?.ttl);
4157
+ },
4158
+ delete(key) {
4159
+ intent.down([["DATA", { kind: "delete", key }]]);
4160
+ },
4161
+ deleteMany(keys) {
4162
+ for (const k of keys) {
4163
+ intent.down([["DATA", { kind: "delete", key: k }]]);
4164
+ }
4165
+ },
4166
+ clear() {
4167
+ intent.down([["DATA", { kind: "clear" }]]);
4168
+ },
4169
+ pruneExpired() {
4170
+ intent.down([["DATA", { kind: "pruneExpired" }]]);
4171
+ },
4172
+ setFrom(src) {
4173
+ if (graph === void 0)
4174
+ throw new Error(
4175
+ "reactiveMap.setFrom requires options.graph so the input fold is describe-visible (D61)"
4176
+ );
4177
+ const op = {
4178
+ factory: "reactiveMap.bindSource",
4179
+ body: (ctx) => {
4180
+ try {
4181
+ for (const [k, v] of depBatch(ctx, 0) ?? []) {
4182
+ ctx.down([["DATA", { kind: "set", key: k, value: v }]]);
4183
+ }
4184
+ } catch (e) {
4185
+ ctx.down([["ERROR", errorPayload(e, "reactiveMap.bindSource failed")]]);
4186
+ }
4187
+ }
4188
+ };
4189
+ const folder = graph.initNode(op, [src], {
4190
+ name: name ? `${name}.bind#${bindSeq++}` : void 0,
4191
+ meta: { kind: "collection_bind_source", collection: "reactiveMap" }
4192
+ });
4193
+ bindDeps.add(folder);
4194
+ apply.subscribeDep(folder, applyBody);
4195
+ let active2 = true;
4196
+ const dispose = () => {
4197
+ if (!active2) return;
4198
+ active2 = false;
4199
+ apply.unsubscribeDep(folder, applyBody);
4200
+ };
4201
+ binds.push(dispose);
4202
+ return dispose;
4203
+ },
4204
+ dispose() {
4205
+ selectMemo.disposeAll();
4206
+ for (const d of binds) d();
4207
+ binds.length = 0;
4208
+ releaseApply();
4209
+ }
4210
+ };
4211
+ }
4212
+
4213
+ // src/data-structures/persistence.ts
4214
+ function isListRestoreState(state) {
4215
+ return !Array.isArray(state);
4216
+ }
4217
+ function restoreListValues(state) {
4218
+ if (!isListRestoreState(state)) return state;
4219
+ if (state.kind !== "reactiveList") throw new TypeError("restore state kind must be reactiveList");
4220
+ return state.state;
4221
+ }
4222
+ function isLogRestoreState(state) {
4223
+ return !Array.isArray(state);
4224
+ }
4225
+ function restoreLogValues(state) {
4226
+ if (!isLogRestoreState(state)) return state;
4227
+ if (state.kind !== "reactiveLog") throw new TypeError("restore state kind must be reactiveLog");
4228
+ return state.state;
4229
+ }
4230
+ function isMapRestoreState(state) {
4231
+ return !Array.isArray(state);
4232
+ }
4233
+ function restoreMapEntries(state) {
4234
+ if (!isMapRestoreState(state)) return state;
4235
+ if (state.kind !== "reactiveMap") throw new TypeError("restore state kind must be reactiveMap");
4236
+ return state.state;
4237
+ }
4238
+ function isIndexRestoreState(state) {
4239
+ return !Array.isArray(state);
4240
+ }
4241
+ function restoreIndexRows(state) {
4242
+ if (!isIndexRestoreState(state)) return state;
4243
+ if (state.kind !== "reactiveIndex") {
4244
+ throw new TypeError("restore state kind must be reactiveIndex");
4245
+ }
4246
+ return state.state;
4247
+ }
4248
+ function restoreReactiveList(state, options = {}) {
4249
+ const values = restoreListValues(state);
4250
+ return reactiveList(values, options);
4251
+ }
4252
+ function restoreReactiveLog(state, options = {}) {
4253
+ const values = restoreLogValues(state);
4254
+ return reactiveLog(values, options);
4255
+ }
4256
+ function restoreReactiveMap(state, options = {}) {
4257
+ const entries = restoreMapEntries(state);
4258
+ return restoreReactiveMapFromBackendState(entries, options);
4259
+ }
4260
+ function restoreReactiveIndex(state, options = {}) {
4261
+ const rows = restoreIndexRows(state);
4262
+ return restoreReactiveIndexFromBackendState(rows, options);
4263
+ }
4264
+ // Annotate the CommonJS export names for ESM import in node:
4265
+ 0 && (module.exports = {
4266
+ mergeReactiveLogs,
4267
+ reactiveIndex,
4268
+ reactiveList,
4269
+ reactiveLog,
4270
+ reactiveMap,
4271
+ restoreReactiveIndex,
4272
+ restoreReactiveList,
4273
+ restoreReactiveLog,
4274
+ restoreReactiveMap,
4275
+ scanLog
4276
+ });
4277
+ //# sourceMappingURL=index.cjs.map