@solidjs/signals 2.0.0-beta.18 → 2.0.0-beta.20

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 (68) hide show
  1. package/dist/dev.js +3276 -928
  2. package/dist/node.cjs +6646 -4189
  3. package/dist/prod/affects.js +218 -0
  4. package/dist/prod/boundaries.js +568 -0
  5. package/dist/prod/core/action.js +96 -0
  6. package/dist/prod/core/async.js +380 -0
  7. package/dist/prod/core/constants.js +92 -0
  8. package/dist/prod/core/context.js +67 -0
  9. package/dist/prod/core/core.js +772 -0
  10. package/dist/prod/core/dev.js +3 -0
  11. package/dist/prod/core/effect.js +145 -0
  12. package/dist/prod/core/error.js +59 -0
  13. package/dist/prod/core/external.js +98 -0
  14. package/dist/prod/core/graph.js +91 -0
  15. package/dist/prod/core/heap.js +132 -0
  16. package/dist/prod/core/invariants.js +42 -0
  17. package/dist/prod/core/lanes.js +140 -0
  18. package/dist/prod/core/optimistic.js +273 -0
  19. package/dist/prod/core/owner.js +293 -0
  20. package/dist/prod/core/scheduler.js +689 -0
  21. package/dist/prod/core/verdict.js +292 -0
  22. package/dist/prod/index.js +45 -0
  23. package/dist/prod/map.js +293 -0
  24. package/dist/prod/signals.js +389 -0
  25. package/dist/prod/store/optimistic.js +184 -0
  26. package/dist/prod/store/projection.js +184 -0
  27. package/dist/prod/store/reconcile.js +426 -0
  28. package/dist/prod/store/store.js +870 -0
  29. package/dist/prod/store/storePath.js +103 -0
  30. package/dist/prod/store/utils.js +316 -0
  31. package/dist/types/affects.d.ts +1 -1
  32. package/dist/types/boundaries.d.ts +6 -6
  33. package/dist/types/core/action.d.ts +19 -6
  34. package/dist/types/core/async.d.ts +4 -38
  35. package/dist/types/core/constants.d.ts +12 -0
  36. package/dist/types/core/core.d.ts +12 -78
  37. package/dist/types/core/dev.d.ts +7 -0
  38. package/dist/types/core/external.d.ts +0 -30
  39. package/dist/types/core/heap.d.ts +8 -0
  40. package/dist/types/core/index.d.ts +3 -2
  41. package/dist/types/core/lanes.d.ts +2 -0
  42. package/dist/types/core/optimistic.d.ts +6 -0
  43. package/dist/types/core/owner.d.ts +8 -0
  44. package/dist/types/core/scheduler.d.ts +40 -39
  45. package/dist/types/core/types.d.ts +9 -1
  46. package/dist/types/core/verdict.d.ts +2 -0
  47. package/dist/types/store/projection.d.ts +0 -1
  48. package/dist/types/store/store.d.ts +8 -2
  49. package/dist/types-cjs/affects.d.cts +1 -1
  50. package/dist/types-cjs/boundaries.d.cts +6 -6
  51. package/dist/types-cjs/core/action.d.cts +19 -6
  52. package/dist/types-cjs/core/async.d.cts +4 -38
  53. package/dist/types-cjs/core/constants.d.cts +12 -0
  54. package/dist/types-cjs/core/core.d.cts +12 -78
  55. package/dist/types-cjs/core/dev.d.cts +7 -0
  56. package/dist/types-cjs/core/external.d.cts +0 -30
  57. package/dist/types-cjs/core/heap.d.cts +8 -0
  58. package/dist/types-cjs/core/index.d.cts +3 -2
  59. package/dist/types-cjs/core/lanes.d.cts +2 -0
  60. package/dist/types-cjs/core/optimistic.d.cts +6 -0
  61. package/dist/types-cjs/core/owner.d.cts +8 -0
  62. package/dist/types-cjs/core/scheduler.d.cts +40 -39
  63. package/dist/types-cjs/core/types.d.cts +9 -1
  64. package/dist/types-cjs/core/verdict.d.cts +2 -0
  65. package/dist/types-cjs/store/projection.d.cts +0 -1
  66. package/dist/types-cjs/store/store.d.cts +8 -2
  67. package/package.json +8 -6
  68. package/dist/prod.js +0 -4637
@@ -0,0 +1,689 @@
1
+ import { EFFECT_RENDER, EFFECT_USER, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, NOT_PENDING, EFFECT_TRACKED, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_MANUAL_WRITE, STATUS_UNINITIALIZED } from "./constants.js";
2
+
3
+ import { currentOptimisticLane } from "./core.js";
4
+
5
+ import { DEV } from "./dev.js";
6
+
7
+ import { NotReadyError } from "./error.js";
8
+
9
+ import { runHeap, enqueueSub } from "./heap.js";
10
+
11
+ import { activeLanes, assignOrMergeLane, findLane } from "./lanes.js";
12
+
13
+ export { getOrCreateLane, hasActiveOverride, mergeLanes, resolveLane } from "./lanes.js";
14
+
15
+ import { devCheckFlushStart, devCheckActiveOverrides, devCensusCompanions, devCheckQuiescent } from "./invariants.js";
16
+
17
+ const transitions = new Set;
18
+
19
+ const dirtyQueue = {
20
+ eE: new Array(2e3).fill(undefined),
21
+ tE: false,
22
+ Le: 0,
23
+ EE: 0
24
+ };
25
+
26
+ const zombieQueue = {
27
+ eE: new Array(2e3).fill(undefined),
28
+ tE: false,
29
+ Le: 0,
30
+ EE: 0
31
+ };
32
+
33
+ let clock = 0;
34
+
35
+ let activeTransition = null;
36
+
37
+ let scheduled = false;
38
+
39
+ let halted = false;
40
+
41
+ let haltNotified = false;
42
+
43
+ let syncDepth = 0;
44
+
45
+ let projectionWriteActive = false;
46
+
47
+ // Store property nodes that were created solely to carry a pending write (no
48
+ // subscribers at write time). Swept after each flush that commits pending
49
+ // values — any still without subs get disposed via their `_unobserved` hook,
50
+ // releasing the slot in the parent store's node map.
51
+ const transientStoreNodes = new Set;
52
+
53
+ function registerTransientStoreNode(e) {
54
+ transientStoreNodes.add(e);
55
+ }
56
+
57
+ function canUseSimpleSyncFlush(e) {
58
+ const t = e.N;
59
+ return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.Be.length === 0 && t._.length === 0 && t.dn.size === 0 && transientStoreNodes.size === 0;
60
+ }
61
+
62
+ function sweepTransientStoreNodes() {
63
+ if (transientStoreNodes.size === 0) return;
64
+ for (const e of transientStoreNodes) {
65
+ if (e.p !== null) {
66
+ transientStoreNodes.delete(e);
67
+ continue;
68
+ }
69
+ if (e.ge !== NOT_PENDING) continue;
70
+ if (e.be !== undefined && e.be !== NOT_PENDING) continue;
71
+ // A live affects() mark keeps the node addressable: sweeping it would
72
+ // detach the refcount from the slot (a fresh probe would upsert a new,
73
+ // unmarked node for the same property).
74
+ if (e.M) continue;
75
+ transientStoreNodes.delete(e);
76
+ e.ct?.();
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Toggles the dev-mode "must be inside a `<Loading>` boundary" enforcement
82
+ * window. Only `render()` calls this — wrapping the initial mount so that a
83
+ * top-level uncaught async read surfaces the diagnostic. Not part of the
84
+ * user-facing API.
85
+ *
86
+ * @internal
87
+ */ function enforceLoadingBoundary(e) {}
88
+
89
+ function setProjectionWriteActive(e) {
90
+ projectionWriteActive = e;
91
+ }
92
+
93
+ /**
94
+ * Ambient work IS a transaction: the global queue always carries one
95
+ * current-transaction-shaped batch (`globalQueue._batch`). With no transition
96
+ * active, registrations (pending commits, optimistic nodes, affects marks,
97
+ * optimistic stores) land in a plain ambient batch that the plain flush
98
+ * finalizes; when a transition initializes it adopts the ambient batch's
99
+ * contents and `_batch` becomes the transition itself, so later registrations
100
+ * land there directly — no per-field aliasing.
101
+ */ function createBatch() {
102
+ return {
103
+ Ie: clock,
104
+ yt: [],
105
+ Lt: new Map,
106
+ Be: [],
107
+ _: [],
108
+ dn: new Set,
109
+ Te: [],
110
+ Bt: {
111
+ Mt: [ [], [] ],
112
+ Qt: []
113
+ },
114
+ cn: false,
115
+ un: new Set
116
+ };
117
+ }
118
+
119
+ function mergeTransitionState(e, t) {
120
+ t.cn = e;
121
+ e.Te.push(...t.Te);
122
+ for (const i of activeLanes) if (i.ve === t) i.ve = e;
123
+ if (t.Be.length) {
124
+ // Move (don't copy): the global queue's batch may still be the outgoing
125
+ // transition, and the adoption pass in initTransition would re-push its
126
+ // contents into the target — duplicating every entry.
127
+ e.Be.push(...t.Be);
128
+ t.Be.length = 0;
129
+ }
130
+ if (t._.length) {
131
+ // Move (don't copy): the global queue's batch may still be the outgoing
132
+ // transition, and the adoption pass in initTransition would re-push its
133
+ // contents into the target — double-releasing every mark.
134
+ e._.push(...t._);
135
+ t._.length = 0;
136
+ }
137
+ for (const i of t.dn) e.dn.add(i);
138
+ for (const [i, n] of t.Lt) {
139
+ let t = e.Lt.get(i);
140
+ if (!t) e.Lt.set(i, t = new Set);
141
+ for (const e of n) t.add(e);
142
+ }
143
+ for (const i of t.un) e.un.add(i);
144
+ }
145
+
146
+ function schedule() {
147
+ if (halted) {
148
+ notifyHalted();
149
+ return;
150
+ }
151
+ if (scheduled) return;
152
+ scheduled = true;
153
+ if (!syncDepth && !globalQueue.Ut && !projectionWriteActive) queueMicrotask(flush);
154
+ }
155
+
156
+ /**
157
+ * Permanently halts the reactive system. Called when a user error escapes
158
+ * every boundary — app state is undefined at that point, so scheduling stops
159
+ * entirely rather than limping along with a half-applied update.
160
+ */ function haltReactivity(e) {
161
+ if (halted) return;
162
+ halted = true;
163
+ let t = "[REACTIVITY_HALTED]";
164
+ // Log the cause here too: callers rethrow it, but a creation-time throw
165
+ // unwinds through ancestor recomputes that convert it to status instead of
166
+ // surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
167
+ e === undefined ? console.error(t) : console.error(t, e);
168
+ }
169
+
170
+ // Logs on the first write after a halt so a frozen interaction is traceable.
171
+ function notifyHalted() {
172
+ if (haltNotified) return;
173
+ haltNotified = true;
174
+ console.error("[REACTIVITY_HALTED]");
175
+ }
176
+
177
+ /** @internal Test/dev-reload hook. Revives scheduling after a halt. */ function resetErrorHalt() {
178
+ halted = false;
179
+ haltNotified = false;
180
+ }
181
+
182
+ class Queue {
183
+ Fe=null;
184
+ Mt=[ [], [] ];
185
+ Qt=[];
186
+ created=clock;
187
+ addChild(e) {
188
+ this.Qt.push(e);
189
+ e.Fe = this;
190
+ }
191
+ removeChild(e) {
192
+ const t = this.Qt.indexOf(e);
193
+ if (t >= 0) {
194
+ this.Qt.splice(t, 1);
195
+ e.Fe = null;
196
+ }
197
+ }
198
+ notify(e, t, i, n) {
199
+ if (this.Fe) return this.Fe.notify(e, t, i, n);
200
+ return false;
201
+ }
202
+ run(e) {
203
+ if (this.Mt[e - 1].length) {
204
+ const t = this.Mt[e - 1];
205
+ this.Mt[e - 1] = [];
206
+ runQueue(t, e);
207
+ }
208
+ for (let t = 0; t < this.Qt.length; t++) this.Qt[t].run?.(e);
209
+ }
210
+ enqueue(e, t) {
211
+ if (e) {
212
+ // Route to lane's effect queue if we're in an optimistic recomputation
213
+ if (currentOptimisticLane) {
214
+ const i = findLane(currentOptimisticLane);
215
+ i.tn[e - 1].push(t);
216
+ } else {
217
+ this.Mt[e - 1].push(t);
218
+ }
219
+ }
220
+ schedule();
221
+ }
222
+ stashQueues(e) {
223
+ e.Mt[0].push(...this.Mt[0]);
224
+ e.Mt[1].push(...this.Mt[1]);
225
+ this.Mt = [ [], [] ];
226
+ for (let t = 0; t < this.Qt.length; t++) {
227
+ let i = this.Qt[t];
228
+ let n = e.Qt[t];
229
+ if (!n) {
230
+ n = {
231
+ Mt: [ [], [] ],
232
+ Qt: []
233
+ };
234
+ e.Qt[t] = n;
235
+ }
236
+ i.stashQueues(n);
237
+ }
238
+ }
239
+ restoreQueues(e) {
240
+ this.Mt[0].push(...e.Mt[0]);
241
+ this.Mt[1].push(...e.Mt[1]);
242
+ for (let t = 0; t < e.Qt.length; t++) {
243
+ const i = e.Qt[t];
244
+ let n = this.Qt[t];
245
+ if (n) n.restoreQueues(i);
246
+ }
247
+ }
248
+ }
249
+
250
+ class GlobalQueue extends Queue {
251
+ Ut=false;
252
+ // The current transaction-shaped batch: a plain ambient batch while no
253
+ // transition is active, the active transition itself after initTransition.
254
+ N=createBatch();
255
+ static Ce;
256
+ static Oe;
257
+ static ut;
258
+ static Vt=null;
259
+ // Store-side hook: drops a keyless affects() mark's identity scope when the
260
+ // carrier node's last registration releases (wired by store.ts, mirroring
261
+ // _clearOptimisticStore).
262
+ static T=null;
263
+ // affects()-side hooks (wired by affects.ts, mirroring _update): the mark
264
+ // engine — count/register/release plus the post-commit re-application of
265
+ // marked reads — lives with the feature. Every call site is gated by state
266
+ // only that module creates, so `!` invocations are safe once the gate holds.
267
+ static j=null;
268
+ static h=null;
269
+ static D=null;
270
+ static F=null;
271
+ static $=null;
272
+ static I=null;
273
+ // External-source bridge (wired by enableExternalSource(); null while no
274
+ // config is active — including after _resetExternalSourceConfig()).
275
+ static Ot=null;
276
+ static Rt=null;
277
+ // Verdict-layer hooks (wired by verdict.ts when isPending()/latest() are
278
+ // imported; null in apps that never use them). Call sites either guard for
279
+ // null or sit behind state only the verdict layer can create (`!` is safe
280
+ // there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
281
+ // verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
282
+ // isPending()/latest()).
283
+ static _e=null;
284
+ static P=null;
285
+ static me=null;
286
+ static R=null;
287
+ static Gt=null;
288
+ static Pt=null;
289
+ static kt=null;
290
+ static tt=null;
291
+ static nt=null;
292
+ static wt=null;
293
+ // Optimistic-engine hooks (wired by core/optimistic.ts via
294
+ // installOptimisticEngine(), called from verdict.ts / createOptimistic /
295
+ // createOptimisticStore — every module that can create optimistic state).
296
+ // Call sites are gated by state only the engine can create: an
297
+ // `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
298
+ // entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
299
+ // once the gate holds.
300
+ static bt=null;
301
+ static En=null;
302
+ static Tn=null;
303
+ static In=null;
304
+ static Nn=null;
305
+ static On=null;
306
+ static Dt=null;
307
+ static gt=null;
308
+ static ht=null;
309
+ static vt=null;
310
+ static $e=null;
311
+ static et=null;
312
+ static Xe=null;
313
+ static mn=null;
314
+ flush() {
315
+ if (this.Ut) return;
316
+ this.Ut = true;
317
+ try {
318
+ if (false) ;
319
+ runHeap(dirtyQueue, GlobalQueue.Ce);
320
+ if (activeTransition) {
321
+ const e = transitionComplete(activeTransition);
322
+ if (!e) {
323
+ const e = activeTransition;
324
+ runHeap(zombieQueue, GlobalQueue.Ce);
325
+ // Detach: the stashed transition keeps its batch; ambient work that
326
+ // follows lands in a fresh one.
327
+ currentBatch = this.N = createBatch();
328
+ // Run lane effects immediately (before stashing) - lanes with no pending async
329
+ if (activeLanes.size) {
330
+ GlobalQueue.On(EFFECT_RENDER);
331
+ GlobalQueue.On(EFFECT_USER);
332
+ }
333
+ this.stashQueues(e.Bt);
334
+ clock++;
335
+ scheduled = dirtyQueue.EE >= dirtyQueue.Le;
336
+ reassignPendingTransition(e.yt);
337
+ activeTransition = null;
338
+ // The stash pass (committed-view rerun of plain optimistic signals)
339
+ // wraps finalizePureQueue in the engine; a non-empty _optimisticNodes
340
+ // means _optimisticWrite ran, which installed the hook.
341
+ if (!e.Te.length && !e.Lt.size && e.Be.length) {
342
+ GlobalQueue.Tn(e);
343
+ } else {
344
+ finalizePureQueue(null, true);
345
+ }
346
+ return;
347
+ }
348
+ const t = activeTransition;
349
+ const i = this.N;
350
+ i !== t && i.yt.push(...t.yt);
351
+ this.restoreQueues(t.Bt);
352
+ transitions.delete(t);
353
+ activeTransition = null;
354
+ reassignPendingTransition(i.yt);
355
+ finalizePureQueue(t);
356
+ if (i === t) {
357
+ // Drop the dead Transition wrapper but keep its (drained) containers
358
+ // as the ambient batch — late registrations during finalization live
359
+ // there and must survive to the next flush.
360
+ const e = createBatch();
361
+ e.yt = i.yt;
362
+ e.Be = i.Be;
363
+ e._ = i._;
364
+ e.dn = i.dn;
365
+ currentBatch = this.N = e;
366
+ }
367
+ } else {
368
+ if (canUseSimpleSyncFlush(this)) {
369
+ commitPendingNodes();
370
+ if (dirtyQueue.EE >= dirtyQueue.Le) {
371
+ runHeap(dirtyQueue, GlobalQueue.Ce);
372
+ commitPendingNodes();
373
+ }
374
+ } else {
375
+ if (transitions.size) runHeap(zombieQueue, GlobalQueue.Ce);
376
+ finalizePureQueue();
377
+ }
378
+ }
379
+ clock++;
380
+ // Check if finalization added items to the heap (from optimistic reversion)
381
+ scheduled = dirtyQueue.EE >= dirtyQueue.Le;
382
+ // Run lane effects first (for ready lanes), then regular effects
383
+ activeLanes.size && GlobalQueue.On(EFFECT_RENDER);
384
+ this.run(EFFECT_RENDER);
385
+ activeLanes.size && GlobalQueue.On(EFFECT_USER);
386
+ this.run(EFFECT_USER);
387
+ if (false) ;
388
+ if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
389
+ if (false) ;
390
+ } finally {
391
+ this.Ut = false;
392
+ }
393
+ }
394
+ notify(e, t, i, n) {
395
+ // Only track async if the boundary is propagating STATUS_PENDING (not caught by boundary)
396
+ if (t & STATUS_PENDING) {
397
+ if (i & STATUS_PENDING) {
398
+ const t = n !== undefined ? n : e.k;
399
+ if (activeTransition && t) {
400
+ const i = t.source;
401
+ let n = activeTransition.Lt.get(i);
402
+ if (!n) activeTransition.Lt.set(i, n = new Set);
403
+ const s = n.size;
404
+ n.add(e);
405
+ if (n.size !== s) schedule();
406
+ }
407
+ }
408
+ return true;
409
+ }
410
+ return false;
411
+ }
412
+ initTransition(e) {
413
+ if (e) e = currentTransition(e);
414
+ if (e && e === activeTransition) return;
415
+ if (!e && activeTransition && activeTransition.Ie === clock) return;
416
+ if (!activeTransition) {
417
+ activeTransition = e ?? createBatch();
418
+ } else if (e) {
419
+ const t = activeTransition;
420
+ mergeTransitionState(e, t);
421
+ transitions.delete(t);
422
+ activeTransition = e;
423
+ }
424
+ transitions.add(activeTransition);
425
+ activeTransition.Ie = clock;
426
+ const t = this.N;
427
+ if (t !== activeTransition) {
428
+ // Adopt the ambient batch into the transaction, then make the
429
+ // transaction the batch so later registrations land there directly.
430
+ // Pending and optimistic nodes are re-stamped as the transaction's;
431
+ // marks don't hijack the node's _transition — a mark on a plain signal
432
+ // must not entangle unrelated writes to it; the same rule holds one hop
433
+ // downstream: propagation never queues pended subscribers as pending
434
+ // nodes, see propagateAffectsMark, #2893.
435
+ for (let e = 0; e < t.yt.length; e++) {
436
+ const i = t.yt[e];
437
+ i.ve = activeTransition;
438
+ activeTransition.yt.push(i);
439
+ }
440
+ for (let e = 0; e < t.Be.length; e++) {
441
+ const i = t.Be[e];
442
+ i.ve = activeTransition;
443
+ activeTransition.Be.push(i);
444
+ }
445
+ if (t._.length) activeTransition._.push(...t._);
446
+ for (const e of t.dn) activeTransition.dn.add(e);
447
+ currentBatch = this.N = activeTransition;
448
+ }
449
+ for (const e of activeLanes) {
450
+ if (!e.ve) e.ve = activeTransition;
451
+ }
452
+ }
453
+ }
454
+
455
+ function queuePendingNode(e) {
456
+ currentBatch.yt.push(e);
457
+ }
458
+
459
+ // Sticky: flips true on the first refresh() ever (the only setter of
460
+ // REACTIVE_REASK) so the hot notification loop skips the per-subscriber flag
461
+ // clear entirely in apps that never refresh.
462
+ let reaskArmed = false;
463
+
464
+ function armReaskClear() {
465
+ reaskArmed = true;
466
+ }
467
+
468
+ function insertSubs(e, t = false) {
469
+ // Get source lane: prefer node's own lane over current context
470
+ // This is important for isPending signals which need their own lane to flush immediately
471
+ const i = e.Je || currentOptimisticLane;
472
+ const n = e.Ye !== undefined;
473
+ const s = reaskArmed;
474
+ for (let r = e.p; r !== null; r = r.de) {
475
+ // A value-change notification is a new question for the subscriber: any
476
+ // pending re-ask mark (refresh) it carried is superseded.
477
+ if (s) r.Ee.u &= ~REACTIVE_REASK;
478
+ if (n && r.Ee.U & CONFIG_IN_SNAPSHOT_SCOPE) {
479
+ r.Ee.u |= REACTIVE_SNAPSHOT_STALE;
480
+ continue;
481
+ }
482
+ if (t && i) {
483
+ r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
484
+ assignOrMergeLane(r.Ee, i);
485
+ } else if (t) {
486
+ r.Ee.u |= REACTIVE_OPTIMISTIC_DIRTY;
487
+ // No source lane means reversion - clear subscriber's lane so effects go to regular queue
488
+ r.Ee.Je = undefined;
489
+ }
490
+ enqueueSub(r.Ee);
491
+ }
492
+ }
493
+
494
+ function commitPendingNode(e) {
495
+ const t = e;
496
+ if (!t.xe) {
497
+ if (e.ge !== NOT_PENDING) {
498
+ e.Ue = e.ge;
499
+ e.ge = NOT_PENDING;
500
+ }
501
+ if (e.ye || e.pe) GlobalQueue.R(e);
502
+ return;
503
+ }
504
+ if (e.ge !== NOT_PENDING) {
505
+ e.Ue = e.ge;
506
+ e.ge = NOT_PENDING;
507
+ // Set _modified for effects, but not for tracked effects (they handle their own scheduling)
508
+ if (e.De && e.De !== EFFECT_TRACKED) e.it = true;
509
+ }
510
+ t.u &= ~REACTIVE_MANUAL_WRITE;
511
+ if (!(t.i & STATUS_PENDING)) t.i &= ~STATUS_UNINITIALIZED;
512
+ if (t.Ze !== null || t.We !== null) GlobalQueue.Oe(t, false, true);
513
+ if (e.ye || e.pe) GlobalQueue.R(e);
514
+ }
515
+
516
+ function commitPendingNodes() {
517
+ const e = currentBatch.yt;
518
+ for (let t = 0; t < e.length; t++) {
519
+ commitPendingNode(e[t]);
520
+ }
521
+ e.length = 0;
522
+ }
523
+
524
+ function finalizePureQueue(e = null, t = false) {
525
+ // For incomplete transitions, skip pending resolution and optimistic reversion
526
+ // For completing transitions or no-transition, resolve pending and revert optimistic
527
+ const i = !t;
528
+ if (i) commitPendingNodes();
529
+ if (!t && globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
530
+ const n = dirtyQueue.EE >= dirtyQueue.Le;
531
+ if (n) runHeap(dirtyQueue, GlobalQueue.Ce);
532
+ if (i) {
533
+ if (n) commitPendingNodes();
534
+ // The settling batch: the completing transaction's, or the ambient one.
535
+ const t = e ?? globalQueue.N;
536
+ // Optimistic reversion: a non-empty batch means _optimisticWrite ran,
537
+ // which installed the engine's hooks.
538
+ if (t.Be.length) GlobalQueue.En(t.Be);
539
+ // Replay entanglement: subs recorded by the read-time gate get rescheduled
540
+ // so they re-run with the now-committed values visible.
541
+ if (e && e.un.size) {
542
+ for (const t of e.un) {
543
+ if (t.u & REACTIVE_DISPOSED) continue;
544
+ enqueueSub(t);
545
+ }
546
+ e.un.clear();
547
+ }
548
+ // Declared motion ends with the transaction: settle (or plain flush end
549
+ // for ambient marks) releases each registration's refcount. A non-empty
550
+ // batch means registerAffectsMark ran, which installed the hook.
551
+ if (t._.length) GlobalQueue.h(t._);
552
+ // A non-empty set means trackOptimisticStore ran, which installed the
553
+ // hook; the hook iterates, clears, and schedules (keeping the loop out of
554
+ // core lets esbuild shake it — rollup already folds the null guard). The
555
+ // completing transition scopes the clear to its own layer keys (#2899).
556
+ if (t.dn.size) GlobalQueue.Vt(t.dn, e);
557
+ sweepTransientStoreNodes();
558
+ // Lanes only enter activeLanes through the engine's getOrCreateLane.
559
+ if (activeLanes.size) GlobalQueue.Nn(e);
560
+ }
561
+ }
562
+
563
+ function checkBoundaryChildren(e) {
564
+ for (const t of e.Qt) {
565
+ t.Se?.();
566
+ checkBoundaryChildren(t);
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Count of live `affects()` registrations across the system (including
572
+ * store-scope inherited marks). Gates the read-path mark check in `read()` so
573
+ * graphs that never use the feature pay one integer compare.
574
+ */ let activeAffectsMarks = 0;
575
+
576
+ /**
577
+ * Counter mutation seam for the mark engine in affects.ts: an imported `let`
578
+ * binding is read-only, and the read-path gate above must stay a plain module
579
+ * variable so `read()` pays one integer compare, not a function call.
580
+ *
581
+ * @internal
582
+ */ function shiftAffectsMarks(e) {
583
+ activeAffectsMarks += e;
584
+ }
585
+
586
+ function reassignPendingTransition(e) {
587
+ for (let t = 0; t < e.length; t++) {
588
+ e[t].ve = activeTransition;
589
+ }
590
+ }
591
+
592
+ const globalQueue = new GlobalQueue;
593
+
594
+ // Hot-path mirror of `globalQueue._batch`: `queuePendingNode` runs once per
595
+ // staged write and `commitPendingNodes` once per flush, and the extra
596
+ // property hop through `_batch` was a measured instruction-count regression
597
+ // (CodSpeed update1to1, PR #2905). The field stays authoritative for
598
+ // cross-module readers; every `_batch` assignment updates both.
599
+ let currentBatch = globalQueue.N;
600
+
601
+ function flush(e) {
602
+ if (e) {
603
+ syncDepth++;
604
+ try {
605
+ return e();
606
+ } finally {
607
+ // Decrement even if the drain throws (a throwing effect): a leaked
608
+ // syncDepth would stop `schedule()` from ever queuing a microtask again.
609
+ try {
610
+ flush();
611
+ } finally {
612
+ syncDepth--;
613
+ }
614
+ }
615
+ }
616
+ if (globalQueue.Ut) {
617
+ return;
618
+ }
619
+ if (halted) return;
620
+ // `flush()` is an explicit drain point, so it must also process an active
621
+ // transition even if no microtask was scheduled for it yet.
622
+ while (scheduled || activeTransition) {
623
+ globalQueue.flush();
624
+ }
625
+ }
626
+
627
+ function runQueue(e, t) {
628
+ for (let i = 0; i < e.length; i++) e[i](t);
629
+ }
630
+
631
+ function reporterBlocksSource(e, t) {
632
+ if (e.u & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
633
+ if (e.m?.has(t)) return true;
634
+ for (let i = e.S; i; i = i.st) {
635
+ let e = i.ot;
636
+ while (e) {
637
+ if (e === t || e.rt === t) return true;
638
+ e = e.en;
639
+ }
640
+ }
641
+ return !!(e.i & STATUS_PENDING && e.k instanceof NotReadyError && e.k.source === t);
642
+ }
643
+
644
+ function transitionComplete(e) {
645
+ if (e.cn) return true;
646
+ if (e.Te.length) return false;
647
+ let t = true;
648
+ for (const [i, n] of e.Lt) {
649
+ let s = false;
650
+ for (const e of n) {
651
+ if (reporterBlocksSource(e, i)) {
652
+ s = true;
653
+ break;
654
+ }
655
+ n.delete(e);
656
+ }
657
+ if (!s) e.Lt.delete(i); else if (i.i & STATUS_PENDING && i.k?.source === i) {
658
+ t = false;
659
+ break;
660
+ }
661
+ }
662
+ // Override blockage lives with the engine. Absent hook = "no optimistic
663
+ // blockage", which is exact: only _optimisticWrite (engine) pushes to
664
+ // _optimisticNodes, so without the engine the loop was vacuous anyway.
665
+ if (t && e.Be.length && GlobalQueue.In(e)) t = false;
666
+ t && (e.cn = true);
667
+ return t;
668
+ }
669
+
670
+ function currentTransition(e) {
671
+ while (e.cn && typeof e.cn === "object") e = e.cn;
672
+ return e;
673
+ }
674
+
675
+ function setActiveTransition(e) {
676
+ activeTransition = e;
677
+ }
678
+
679
+ function runInTransition(e, t) {
680
+ const i = activeTransition;
681
+ try {
682
+ activeTransition = currentTransition(e);
683
+ return t();
684
+ } finally {
685
+ activeTransition = i;
686
+ }
687
+ }
688
+
689
+ export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, clock, currentTransition, dirtyQueue, enforceLoadingBoundary, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, insertSubs, projectionWriteActive, queuePendingNode, registerTransientStoreNode, resetErrorHalt, runInTransition, schedule, setActiveTransition, setProjectionWriteActive, shiftAffectsMarks, zombieQueue };