@solidjs/signals 2.0.0-rc.0 → 2.0.0-rc.2

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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/dev.js +4287 -2803
  3. package/dist/node.cjs +4563 -3756
  4. package/dist/prod/affects.js +16 -16
  5. package/dist/prod/boundaries.js +90 -90
  6. package/dist/prod/core/action.js +3 -3
  7. package/dist/prod/core/async.js +234 -178
  8. package/dist/prod/core/constants.js +39 -1
  9. package/dist/prod/core/core.js +362 -243
  10. package/dist/prod/core/effect.js +56 -56
  11. package/dist/prod/core/external.js +4 -4
  12. package/dist/prod/core/graph.js +65 -54
  13. package/dist/prod/core/heap.js +47 -39
  14. package/dist/prod/core/invariants.js +3 -2
  15. package/dist/prod/core/lanes.js +41 -34
  16. package/dist/prod/core/optimistic.js +78 -58
  17. package/dist/prod/core/owner.js +101 -100
  18. package/dist/prod/core/scheduler.js +249 -192
  19. package/dist/prod/core/verdict.js +185 -78
  20. package/dist/prod/index.js +7 -7
  21. package/dist/prod/map.js +108 -106
  22. package/dist/prod/signals.js +20 -1
  23. package/dist/prod/store/index.js +36 -0
  24. package/dist/prod/store/next/optimistic.js +385 -0
  25. package/dist/prod/store/next/projection.js +172 -0
  26. package/dist/prod/store/next/reconcile.js +331 -0
  27. package/dist/prod/store/next/store.js +1499 -0
  28. package/dist/prod/store/next/target.js +20 -0
  29. package/dist/prod/store/store.js +126 -882
  30. package/dist/prod/store/utils.js +59 -186
  31. package/dist/types/core/attribution-hooks.d.ts +52 -0
  32. package/dist/types/core/attribution.d.ts +186 -0
  33. package/dist/types/core/constants.d.ts +26 -0
  34. package/dist/types/core/core.d.ts +16 -1
  35. package/dist/types/core/dev.d.ts +20 -3
  36. package/dist/types/core/graph.d.ts +1 -0
  37. package/dist/types/core/lanes.d.ts +4 -16
  38. package/dist/types/core/scheduler.d.ts +12 -2
  39. package/dist/types/core/types.d.ts +85 -41
  40. package/dist/types/signals.d.ts +19 -0
  41. package/dist/types/store/index.d.ts +15 -5
  42. package/dist/types/store/next/optimistic.d.ts +20 -0
  43. package/dist/types/store/next/projection.d.ts +8 -0
  44. package/dist/types/store/next/reconcile.d.ts +3 -0
  45. package/dist/types/store/next/store.d.ts +77 -0
  46. package/dist/types/store/next/target.d.ts +117 -0
  47. package/dist/types/store/store.d.ts +26 -101
  48. package/dist/types/store/utils.d.ts +0 -40
  49. package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
  50. package/dist/types-cjs/core/attribution.d.cts +186 -0
  51. package/dist/types-cjs/core/constants.d.cts +26 -0
  52. package/dist/types-cjs/core/core.d.cts +16 -1
  53. package/dist/types-cjs/core/dev.d.cts +20 -3
  54. package/dist/types-cjs/core/graph.d.cts +1 -0
  55. package/dist/types-cjs/core/lanes.d.cts +4 -16
  56. package/dist/types-cjs/core/scheduler.d.cts +12 -2
  57. package/dist/types-cjs/core/types.d.cts +85 -41
  58. package/dist/types-cjs/signals.d.cts +19 -0
  59. package/dist/types-cjs/store/index.d.cts +15 -5
  60. package/dist/types-cjs/store/next/optimistic.d.cts +20 -0
  61. package/dist/types-cjs/store/next/projection.d.cts +8 -0
  62. package/dist/types-cjs/store/next/reconcile.d.cts +3 -0
  63. package/dist/types-cjs/store/next/store.d.cts +77 -0
  64. package/dist/types-cjs/store/next/target.d.cts +117 -0
  65. package/dist/types-cjs/store/store.d.cts +26 -101
  66. package/dist/types-cjs/store/utils.d.cts +0 -40
  67. package/package.json +2 -1
  68. package/dist/prod/store/optimistic.js +0 -217
  69. package/dist/prod/store/projection.js +0 -231
  70. package/dist/prod/store/reconcile.js +0 -707
@@ -1,6 +1,6 @@
1
1
  import { REACTIVE_DISPOSED, STATUS_ERROR, EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED, STATUS_PENDING, EFFECT_RENDER } from "./constants.js";
2
2
 
3
- import { createEffectNode, recompute, computed, staleValues } from "./core.js";
3
+ import { ext, createEffectNode, recompute, computed, staleValues } from "./core.js";
4
4
 
5
5
  import { unwrapStatusError, StatusError } from "./error.js";
6
6
 
@@ -10,18 +10,18 @@ import { GlobalQueue, haltReactivity } from "./scheduler.js";
10
10
  * Effects are the leaf nodes of our reactive graph. When their sources change, they are
11
11
  * automatically added to the queue of effects to re-execute, which will cause them to fetch their
12
12
  * sources and recompute
13
- */ function effect(t, E, e, R) {
13
+ */ function effect(t, e, E, R) {
14
14
  const r = !!R?.user;
15
- const f = createEffectNode(t, E, e, r ? EFFECT_USER : EFFECT_RENDER, notifyEffectStatus, R);
15
+ const f = createEffectNode(t, e, E, r ? EFFECT_USER : EFFECT_RENDER, notifyEffectStatus, R);
16
16
  recompute(f, true);
17
17
  !R?.defer && (f.Re === EFFECT_USER || R?.schedule ? f.C.enqueue(f.Re, runEffect.bind(null, f)) : runEffect(f));
18
18
  }
19
19
 
20
- function notifyEffectStatus(t, E) {
20
+ function notifyEffectStatus(t, e) {
21
21
  // Use passed values if provided, otherwise read from node
22
- const e = t !== undefined ? t : this.S;
23
- const R = E !== undefined ? E : this._;
24
- if (e & STATUS_ERROR) {
22
+ const E = t !== undefined ? t : this.S;
23
+ const R = e !== undefined ? e : this.o?._;
24
+ if (E & STATUS_ERROR) {
25
25
  this.C.notify(this, STATUS_PENDING, 0);
26
26
  if (this.Re === EFFECT_USER) {
27
27
  // The error handler is the error arm of the effect phase (#2840 ruling):
@@ -34,8 +34,8 @@ function notifyEffectStatus(t, E) {
34
34
  // `status` arg without node-state writes) don't queue: the status
35
35
  // re-propagates unblocked at commit.
36
36
  if (this.S & STATUS_ERROR) {
37
- this.Je = true;
38
- this.C.enqueue(this.Re, this.Xe ??= runEffect.bind(null, this));
37
+ this.Xe = true;
38
+ this.C.enqueue(this.Re, this.et ??= runEffect.bind(null, this));
39
39
  }
40
40
  return;
41
41
  }
@@ -44,12 +44,12 @@ function notifyEffectStatus(t, E) {
44
44
  throw R;
45
45
  }
46
46
  } else if (this.Re === EFFECT_RENDER) {
47
- this.C.notify(this, STATUS_PENDING | STATUS_ERROR, e, R);
47
+ this.C.notify(this, STATUS_PENDING | STATUS_ERROR, E, R);
48
48
  }
49
49
  }
50
50
 
51
51
  function runEffect(t) {
52
- if (!t.Je || t.se & REACTIVE_DISPOSED) return;
52
+ if (!t.Xe || t.ie & REACTIVE_DISPOSED) return;
53
53
  // Error arm (#2840), user effects only: a compute-phase error that is still
54
54
  // the node's settled state at effect time runs the bundle's error handler in
55
55
  // this same imperative, writable scope. Unwrap the StatusError used for
@@ -61,85 +61,85 @@ function runEffect(t) {
61
61
  // notifyEffectStatus, and a runner queued by an earlier valueChanged in the
62
62
  // same flush must not be hijacked by a later-arriving error status.
63
63
  if (t.S & STATUS_ERROR && t.Re === EFFECT_USER) {
64
- const E = unwrapStatusError(t._);
65
- t._t = t.Ue;
66
- t.Je = false;
64
+ const e = unwrapStatusError(t.o?._);
65
+ t.Tt = t.be;
66
+ t.Xe = false;
67
67
  try {
68
- t.Et ? t.Et(E, () => {
69
- const E = t.Tt;
70
- t.Tt = undefined;
71
- E?.();
72
- }) : console.error(E);
73
- } catch (E) {
68
+ t.St ? t.St(e, () => {
69
+ const e = t.At;
70
+ t.At = undefined;
71
+ e?.();
72
+ }) : console.error(e);
73
+ } catch (e) {
74
74
  if (!t.C.notify(t, STATUS_ERROR, STATUS_ERROR)) {
75
- haltReactivity(E);
76
- throw E;
75
+ haltReactivity(e);
76
+ throw e;
77
77
  }
78
78
  }
79
79
  return;
80
80
  }
81
- const E = t.Tt;
82
- t.Tt = undefined;
81
+ const e = t.At;
82
+ t.At = undefined;
83
83
  try {
84
- E?.();
85
- const e = t.ft(t.Ue, t._t);
86
- if (false && e !== undefined && typeof e !== "function") ;
84
+ e?.();
85
+ const E = t.dt(t.be, t.Tt);
86
+ if (false && E !== undefined && typeof E !== "function") ;
87
87
  // The final cleanup is invoked by disposeChildren at true disposal.
88
- t.Tt = e;
89
- } catch (E) {
90
- t._ = new StatusError(t, E);
88
+ t.At = E;
89
+ } catch (e) {
90
+ ext(t)._ = new StatusError(t, e);
91
91
  t.S |= STATUS_ERROR;
92
92
  if (!t.C.notify(t, STATUS_ERROR, STATUS_ERROR)) {
93
- haltReactivity(E);
94
- throw E;
93
+ haltReactivity(e);
94
+ throw e;
95
95
  }
96
96
  } finally {
97
- t._t = t.Ue;
98
- t.Je = false;
97
+ t.Tt = t.be;
98
+ t.Xe = false;
99
99
  }
100
100
  }
101
101
 
102
- GlobalQueue.et = runEffect;
102
+ GlobalQueue.tt = runEffect;
103
103
 
104
104
  /**
105
105
  * Internal tracked effect - bypasses heap, goes directly to effect queue.
106
106
  * Runs as a leaf owner: child primitives and onCleanup are forbidden (false throws).
107
107
  * Uses stale reads.
108
- */ function trackedEffect(t, E) {
108
+ */ function trackedEffect(t, e) {
109
109
  const run = () => {
110
- if (!e.Je || e.se & REACTIVE_DISPOSED) return;
110
+ if (!E.Xe || E.ie & REACTIVE_DISPOSED) return;
111
111
  try {
112
- e.Je = false;
113
- recompute(e);
112
+ E.Xe = false;
113
+ recompute(E);
114
114
  } finally {}
115
115
  };
116
- const e = computed(() => {
117
- const E = e.Tt;
118
- e.Tt = undefined;
119
- E?.();
116
+ const E = computed(() => {
117
+ const e = E.At;
118
+ E.At = undefined;
119
+ e?.();
120
120
  const R = staleValues(t);
121
- e.Tt = R;
121
+ E.At = R;
122
122
  }, {
123
- ...E,
123
+ ...e,
124
124
  lazy: true
125
125
  });
126
- e.Tt = undefined;
127
- e.T = e.T & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
128
- e.Je = true;
129
- e.Re = EFFECT_TRACKED;
130
- e.i = (t, E) => {
131
- const R = t !== undefined ? t : e.S;
126
+ E.At = undefined;
127
+ E.T = E.T & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
128
+ E.Xe = true;
129
+ E.Re = EFFECT_TRACKED;
130
+ ext(E).A = (t, e) => {
131
+ const R = t !== undefined ? t : E.S;
132
132
  if (R & STATUS_ERROR) {
133
- e.C.notify(e, STATUS_PENDING, 0);
134
- const t = E !== undefined ? E : e._;
135
- if (!e.C.notify(e, STATUS_ERROR, STATUS_ERROR)) {
133
+ E.C.notify(E, STATUS_PENDING, 0);
134
+ const t = e !== undefined ? e : E.o?._;
135
+ if (!E.C.notify(E, STATUS_ERROR, STATUS_ERROR)) {
136
136
  haltReactivity(unwrapStatusError(t));
137
137
  throw t;
138
138
  }
139
139
  }
140
140
  };
141
- e.Ft = run;
142
- e.C.enqueue(EFFECT_USER, run);
141
+ E.Ut = run;
142
+ E.C.enqueue(EFFECT_USER, run);
143
143
  }
144
144
 
145
145
  export { effect, trackedEffect };
@@ -46,11 +46,11 @@ function wireExternalSource(e) {
46
46
  equals: false,
47
47
  ownedWrite: true
48
48
  });
49
- const r = externalSourceConfig.factory(e.ce, () => {
49
+ const r = externalSourceConfig.factory(e.Se, () => {
50
50
  setSignal(n, undefined);
51
51
  });
52
52
  cleanup(() => r.dispose());
53
- e.ce = e => {
53
+ e.Se = e => {
54
54
  read(n);
55
55
  return r.track(e);
56
56
  };
@@ -64,8 +64,8 @@ function externalUntrack(e) {
64
64
  // removed on reset) so core's null checks stay equivalent to the old
65
65
  // `externalSourceConfig` truthiness checks.
66
66
  function syncExternalHooks() {
67
- GlobalQueue.It = externalSourceConfig ? wireExternalSource : null;
68
- GlobalQueue.St = externalSourceConfig ? externalUntrack : null;
67
+ GlobalQueue.Rt = externalSourceConfig ? wireExternalSource : null;
68
+ GlobalQueue.Gt = externalSourceConfig ? externalUntrack : null;
69
69
  }
70
70
 
71
71
  function enableExternalSource(e) {
@@ -4,75 +4,84 @@ import { deleteFromHeap, queueFor } from "./heap.js";
4
4
 
5
5
  import { disposeChildren } from "./owner.js";
6
6
 
7
- import "./scheduler.js";
7
+ import { bumpNotifyEpoch } from "./scheduler.js";
8
8
 
9
9
  // https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L100
10
- function unlinkSubs(l) {
11
- const n = l.it;
12
- const e = l.nt;
13
- const u = l.le;
14
- const s = l.ll;
15
- if (u !== null) u.ll = s; else n.rt = s;
16
- if (s !== null) s.le = u; else {
17
- n.o = u;
10
+ function unlinkSubs(e) {
11
+ const l = e.ut;
12
+ const n = e.it;
13
+ const u = e.fe;
14
+ const s = e.el;
15
+ if (u !== null) u.el = s; else l.rt = s;
16
+ if (s !== null) s.fe = u; else {
17
+ l.u = u;
18
18
  if (u === null) {
19
- n.ut?.();
19
+ l.o?.ft?.();
20
20
  // No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
21
21
  // A pending node is exempt: its in-flight async work (or the
22
22
  // transition holding it) is an observer — tearing down would orphan
23
23
  // the work and re-execute it on the next read. The settle path runs
24
24
  // this same last-one-out check when that observer releases (the
25
25
  // untracked-read dispose in core.ts guards on pending identically).
26
- const l = n;
27
- l.ce && l.T & CONFIG_AUTO_DISPOSE && !(l.se & REACTIVE_ZOMBIE) && !(l.S & STATUS_PENDING) && unobserved(l);
26
+ const e = l;
27
+ e.Se && e.T & CONFIG_AUTO_DISPOSE && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
28
28
  }
29
29
  }
30
- return e;
30
+ return n;
31
31
  }
32
32
 
33
- function trimStaleDeps(l) {
34
- const n = l.Ye;
35
- let e = n !== null ? n.nt : l.tt;
36
- if (e !== null) {
33
+ function trimStaleDeps(e) {
34
+ const l = e.Ye;
35
+ let n = l !== null ? l.it : e.nt;
36
+ if (n !== null) {
37
37
  do {
38
- e = unlinkSubs(e);
39
- } while (e !== null);
40
- if (n !== null) n.nt = null; else l.tt = null;
38
+ n = unlinkSubs(n);
39
+ } while (n !== null);
40
+ if (l !== null) l.it = null; else e.nt = null;
41
41
  }
42
42
  }
43
43
 
44
- function unobserved(l) {
45
- deleteFromHeap(l, queueFor(l));
46
- let n = l.tt;
47
- while (n !== null) {
48
- n = unlinkSubs(n);
49
- }
50
- l.tt = null;
51
- l.Ye = null;
52
- disposeChildren(l, true);
44
+ // Shared by unobserved() and the disposeChildren child loop. The truthy guard
45
+ // (not `!== null`) matters: plain Owners in a child chain have no _deps field,
46
+ // and skipping early also avoids adding one (hidden-class churn) via the
47
+ // null-out below.
48
+ function clearDeps(e) {
49
+ let l = e.nt;
50
+ if (!l) return;
51
+ do {
52
+ l = unlinkSubs(l);
53
+ } while (l !== null);
54
+ e.nt = null;
55
+ e.Ye = null;
56
+ }
57
+
58
+ function unobserved(e) {
59
+ deleteFromHeap(e, queueFor(e));
60
+ clearDeps(e);
61
+ disposeChildren(e, true);
53
62
  }
54
63
 
55
64
  // https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L52
56
- function link(l, n, e = false) {
65
+ function link(e, l, n = false) {
57
66
  // Repeat touches within one pass AND-combine `_pendingObserver`: a probe
58
67
  // read (`isPending(() => x())`) beside a value read of the same dep must
59
68
  // not relabel the value dependency as probe-only — the value read is what
60
69
  // real-error propagation and affects() coverage key off, regardless of
61
70
  // read order within the computation.
62
- const u = n.Ye;
63
- if (u !== null && u.it === l) {
64
- u.Ge &&= e;
71
+ const u = l.Ye;
72
+ if (u !== null && u.ut === e) {
73
+ u.me &&= n;
65
74
  return;
66
75
  }
67
76
  let s = null;
68
- const t = n.se & REACTIVE_RECOMPUTING_DEPS;
69
- if (t) {
70
- s = u !== null ? u.nt : n.tt;
71
- if (s !== null && s.it === l) {
72
- s.nl = n.Ze;
73
- n.Ye = s;
77
+ const o = l.ie & REACTIVE_RECOMPUTING_DEPS;
78
+ if (o) {
79
+ s = u !== null ? u.it : l.nt;
80
+ if (s !== null && s.ut === e) {
81
+ s.ll = l.Ze;
82
+ l.Ye = s;
74
83
  // First touch of this pass: the previous pass's label is stale.
75
- s.Ge = e;
84
+ s.me = n;
76
85
  return;
77
86
  }
78
87
  }
@@ -81,24 +90,26 @@ function link(l, n, e = false) {
81
90
  // [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
82
91
  // (the old alien-signals `isValidLink` walk, O(n²) when a computation
83
92
  // re-reads earlier deps non-consecutively, e.g. store leaf reads).
84
- const i = l.rt;
85
- if (i !== null && i.fe === n && (!t || i.nl === n.Ze)) {
93
+ const t = e.rt;
94
+ if (t !== null && t.ae === l && (!o || t.ll === l.Ze)) {
86
95
  // Gen-matched during a recompute = repeat touch this pass (AND); outside
87
96
  // a recompute there is no pass boundary, so the latest read labels it.
88
- if (t) i.Ge &&= e; else i.Ge = e;
97
+ if (o) t.me &&= n; else t.me = n;
89
98
  return;
90
99
  }
91
- const o = n.Ye = l.rt = {
92
- it: l,
93
- fe: n,
94
- nt: s,
95
- ll: i,
96
- le: null,
97
- nl: n.Ze,
98
- Ge: e
100
+ const i = l.Ye = e.rt = {
101
+ ut: e,
102
+ ae: l,
103
+ it: s,
104
+ el: t,
105
+ fe: null,
106
+ ll: l.Ze,
107
+ me: n
99
108
  };
100
- if (u !== null) u.nt = o; else n.tt = o;
101
- if (i !== null) i.le = o; else l.o = o;
109
+ if (u !== null) u.it = i; else l.nt = i;
110
+ if (t !== null) t.fe = i; else e.u = i;
111
+ // New subscriber edge: staged-rewrite skips (§12d) must not miss it.
112
+ bumpNotifyEpoch();
102
113
  }
103
114
 
104
- export { link, trimStaleDeps, unlinkSubs, unobserved };
115
+ export { clearDeps, link, trimStaleDeps, unlinkSubs, unobserved };
@@ -1,9 +1,11 @@
1
- import { REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, EFFECT_TRACKED, EFFECT_USER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_ZOMBIE, REACTIVE_CHECK, REACTIVE_DIRTY } from "./constants.js";
1
+ import "./core.js";
2
+
3
+ import { REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, EFFECT_TRACKED, EFFECT_USER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_ZOMBIE, REACTIVE_CHECK, REACTIVE_DIRTY, CONFIG_FW_CHILDREN } from "./constants.js";
2
4
 
3
5
  import { zombieQueue, dirtyQueue } from "./scheduler.js";
4
6
 
5
7
  /** The queue a node belongs to, picked from its own zombie flag. */ function queueFor(e) {
6
- return e.se & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue;
8
+ return e.ie & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue;
7
9
  }
8
10
 
9
11
  /**
@@ -13,21 +15,21 @@ import { zombieQueue, dirtyQueue } from "./scheduler.js";
13
15
  */ function enqueueSub(e) {
14
16
  if (e.Re === EFFECT_TRACKED) {
15
17
  const E = e;
16
- if (!E.Je) {
17
- E.Je = true;
18
- E.C.enqueue(EFFECT_USER, E.Ft);
18
+ if (!E.Xe) {
19
+ E.Xe = true;
20
+ E.C.enqueue(EFFECT_USER, E.Ut);
19
21
  }
20
22
  return;
21
23
  }
22
24
  const E = queueFor(e);
23
- if (E.Ve > e.xe) E.Ve = e.xe;
25
+ if (E.xe > e.Le) E.xe = e.Le;
24
26
  insertIntoHeap(e, E);
25
27
  }
26
28
 
27
29
  function actualInsertIntoHeap(e, E) {
28
- const t = (e.ke?.Nt ? e.ke.dt?.xe : e.ke?.xe) ?? -1;
29
- if (t >= e.xe) e.xe = t + 1;
30
- const n = e.xe;
30
+ const t = (e.ve?.Ct ? e.ve.Ot?.Le : e.ve?.Le) ?? -1;
31
+ if (t >= e.Le) e.Le = t + 1;
32
+ const n = e.Le;
31
33
  const I = E.eE[n];
32
34
  if (I === undefined) E.eE[n] = e; else {
33
35
  const E = I.st;
@@ -39,12 +41,15 @@ function actualInsertIntoHeap(e, E) {
39
41
  }
40
42
 
41
43
  function insertIntoHeap(e, E) {
42
- let t = e.se;
43
- if (t & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_MANUAL_WRITE)) return;
44
+ let t = e.ie;
45
+ // RECOMPUTING refusals are not always losses: a genuinely missed wake (a
46
+ // write to a link this pass already validated) is latched link-side in
47
+ // insertSubs as REACTIVE_MISSED_WAKE for recompute's tail (#3037).
48
+ if (t & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_MANUAL_WRITE)) return;
44
49
  if (t & REACTIVE_CHECK) {
45
- e.se = t & -4 | REACTIVE_DIRTY | REACTIVE_IN_HEAP;
50
+ e.ie = t & -4 | REACTIVE_DIRTY | REACTIVE_IN_HEAP;
46
51
  } else {
47
- e.se = t | REACTIVE_IN_HEAP;
52
+ e.ie = t | REACTIVE_IN_HEAP;
48
53
  // An unmarked node entering a marked heap invalidates the markHeap memo:
49
54
  // `_marked` is only reset by runHeap, so a write between two mid-tick
50
55
  // pulls (read-time markHeap + updateIfNecessary) would otherwise leave
@@ -56,17 +61,17 @@ function insertIntoHeap(e, E) {
56
61
  }
57
62
 
58
63
  function insertIntoHeapHeight(e, E) {
59
- let t = e.se;
64
+ let t = e.ie;
60
65
  if (t & (REACTIVE_IN_HEAP | REACTIVE_RECOMPUTING_DEPS | REACTIVE_IN_HEAP_HEIGHT | REACTIVE_MANUAL_WRITE)) return;
61
- e.se = t | REACTIVE_IN_HEAP_HEIGHT;
66
+ e.ie = t | REACTIVE_IN_HEAP_HEIGHT;
62
67
  actualInsertIntoHeap(e, E);
63
68
  }
64
69
 
65
70
  function deleteFromHeap(e, E) {
66
- const t = e.se;
71
+ const t = e.ie;
67
72
  if (!(t & (REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT))) return;
68
- e.se = t & -25;
69
- const n = e.xe;
73
+ e.ie = t & -25;
74
+ const n = e.Le;
70
75
  if (e.st === e) E.eE[n] = undefined; else {
71
76
  const t = e.ot;
72
77
  const I = E.eE[n];
@@ -83,22 +88,25 @@ function markHeap(e) {
83
88
  e.tE = true;
84
89
  for (let E = 0; E <= e.EE; E++) {
85
90
  for (let t = e.eE[E]; t !== undefined; t = t.ot) {
86
- if (t.se & REACTIVE_IN_HEAP) markNode(t);
91
+ if (t.ie & REACTIVE_IN_HEAP) markNode(t);
87
92
  }
88
93
  }
89
94
  }
90
95
 
91
96
  function markNode(e, E = REACTIVE_DIRTY) {
92
- const t = e.se;
97
+ const t = e.ie;
93
98
  if ((t & (REACTIVE_CHECK | REACTIVE_DIRTY)) >= E) return;
94
- e.se = t & -4 | E;
95
- for (let E = e.o; E !== null; E = E.le) {
96
- markNode(E.fe, REACTIVE_CHECK);
99
+ e.ie = t & -4 | E;
100
+ for (let E = e.u; E !== null; E = E.fe) {
101
+ markNode(E.ae, REACTIVE_CHECK);
97
102
  }
98
- if (e.u !== null) {
99
- for (let E = e.u; E !== null; E = E.ae) {
100
- for (let e = E.o; e !== null; e = e.le) {
101
- markNode(e.fe, REACTIVE_CHECK);
103
+ // Firewall children (projection machinery only): gate the cold-extension
104
+ // deref on the config bit markNode runs per sub edge per write, and an
105
+ // unconditional _x chase here taxed every propagation (diamond -22%).
106
+ if (e.T & CONFIG_FW_CHILDREN) {
107
+ for (let E = e.o.l; E !== null; E = E.ce) {
108
+ for (let e = E.u; e !== null; e = e.fe) {
109
+ markNode(e.ae, REACTIVE_CHECK);
102
110
  }
103
111
  }
104
112
  }
@@ -106,11 +114,11 @@ function markNode(e, E = REACTIVE_DIRTY) {
106
114
 
107
115
  function runHeap(e, E) {
108
116
  e.tE = false;
109
- for (e.Ve = 0; e.Ve <= e.EE; e.Ve++) {
110
- let t = e.eE[e.Ve];
117
+ for (e.xe = 0; e.xe <= e.EE; e.xe++) {
118
+ let t = e.eE[e.xe];
111
119
  while (t !== undefined) {
112
- if (t.se & REACTIVE_IN_HEAP) E(t); else adjustHeight(t, e);
113
- t = e.eE[e.Ve];
120
+ if (t.ie & REACTIVE_IN_HEAP) E(t); else adjustHeight(t, e);
121
+ t = e.eE[e.xe];
114
122
  }
115
123
  }
116
124
  e.EE = 0;
@@ -118,21 +126,21 @@ function runHeap(e, E) {
118
126
 
119
127
  function adjustHeight(e, E) {
120
128
  deleteFromHeap(e, E);
121
- let t = e.xe;
122
- for (let E = e.tt; E; E = E.nt) {
123
- const e = E.it;
129
+ let t = e.Le;
130
+ for (let E = e.nt; E; E = E.it) {
131
+ const e = E.ut;
124
132
  const n = e.lt || e;
125
- if (n.ce && n.xe >= t) t = n.xe + 1;
133
+ if (n.Se && n.Le >= t) t = n.Le + 1;
126
134
  }
127
- if (e.xe !== t) {
128
- e.xe = t;
129
- for (let E = e.o; E !== null; E = E.le) {
135
+ if (e.Le !== t) {
136
+ e.Le = t;
137
+ for (let E = e.u; E !== null; E = E.fe) {
130
138
  // Route each subscriber by its own zombie flag, mirroring the
131
139
  // post-recompute height-adjust path. Inserting into the running `heap`
132
140
  // unconditionally can park a zombie in `dirtyQueue` (or a live node in
133
141
  // `zombieQueue`), breaking the flag/queue invariant `deleteFromHeap`
134
142
  // relies on — the same corruption class as #2759.
135
- insertIntoHeapHeight(E.fe, queueFor(E.fe));
143
+ insertIntoHeapHeight(E.ae, queueFor(E.ae));
136
144
  }
137
145
  }
138
146
  }
@@ -1,3 +1,5 @@
1
+ import "./core.js";
2
+
1
3
  /**
2
4
  * INV-2: a node with an *active* override must be registered for reversion in
3
5
  * the queue's or a transition's `_optimisticNodes`. An unregistered active
@@ -6,8 +8,7 @@
6
8
  * (There is no revert-target requirement: authoritative values commit
7
9
  * silently into `_value` under the override mask — A17 — so reverting is
8
10
  * just dropping the override.)
9
- */
10
- function devCheckActiveOverrides(e) {
11
+ */ function devCheckActiveOverrides(e) {
11
12
  return;
12
13
  }
13
14