@solidjs/signals 2.0.0-rc.19 → 2.0.0-rc.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 (74) hide show
  1. package/dist/dev-shared.js +553 -264
  2. package/dist/dev.attribution.js +18 -18
  3. package/dist/dev.js +1030 -439
  4. package/dist/observe/affects.js +12 -2
  5. package/dist/observe/boundaries.js +168 -115
  6. package/dist/observe/core/async.js +57 -37
  7. package/dist/observe/core/attribution.js +6 -6
  8. package/dist/observe/core/constants.js +9 -1
  9. package/dist/observe/core/copies.js +10 -0
  10. package/dist/observe/core/core.js +261 -181
  11. package/dist/observe/core/effect.js +32 -27
  12. package/dist/observe/core/graph.js +7 -7
  13. package/dist/observe/core/heap.js +29 -29
  14. package/dist/observe/core/lanes.js +301 -101
  15. package/dist/observe/core/owner.js +13 -13
  16. package/dist/observe/core/scheduler.js +124 -69
  17. package/dist/observe/core/truth.js +9 -7
  18. package/dist/observe/core/verdict.js +160 -98
  19. package/dist/observe/deferred.js +1 -1
  20. package/dist/observe/index.js +2 -0
  21. package/dist/observe/map.js +120 -116
  22. package/dist/observe/reveal.js +48 -48
  23. package/dist/observe/signals.js +5 -5
  24. package/dist/observe/store/affects.js +2 -2
  25. package/dist/observe/store/optimistic.js +272 -176
  26. package/dist/observe/store/projection.js +21 -10
  27. package/dist/observe/store/reconcile.js +8 -2
  28. package/dist/observe/store/store.js +91 -55
  29. package/dist/observe/store/types.js +11 -1
  30. package/dist/observe/store/utils.js +1 -1
  31. package/dist/prod/affects.js +12 -2
  32. package/dist/prod/boundaries.js +164 -109
  33. package/dist/prod/core/async.js +201 -181
  34. package/dist/prod/core/constants.js +9 -1
  35. package/dist/prod/core/copies.js +10 -0
  36. package/dist/prod/core/core.js +244 -164
  37. package/dist/prod/core/dev.js +108 -3
  38. package/dist/prod/core/effect.js +28 -23
  39. package/dist/prod/core/graph.js +9 -9
  40. package/dist/prod/core/heap.js +16 -16
  41. package/dist/prod/core/identities.js +1 -1
  42. package/dist/prod/core/lanes.js +307 -95
  43. package/dist/prod/core/owner.js +22 -22
  44. package/dist/prod/core/scheduler.js +266 -211
  45. package/dist/prod/core/truth.js +9 -7
  46. package/dist/prod/core/verdict.js +161 -99
  47. package/dist/prod/deferred.js +1 -1
  48. package/dist/prod/index.js +2 -0
  49. package/dist/prod/map.js +78 -74
  50. package/dist/prod/reveal.js +34 -34
  51. package/dist/prod/signals.js +3 -3
  52. package/dist/prod/store/affects.js +15 -15
  53. package/dist/prod/store/optimistic.js +185 -89
  54. package/dist/prod/store/projection.js +49 -38
  55. package/dist/prod/store/reconcile.js +8 -2
  56. package/dist/prod/store/store.js +99 -63
  57. package/dist/prod/store/types.js +11 -1
  58. package/dist/types/affects.d.ts +3 -2
  59. package/dist/types/boundaries.d.ts +14 -2
  60. package/dist/types/core/async.d.ts +2 -1
  61. package/dist/types/core/constants.d.ts +8 -0
  62. package/dist/types/core/copies.d.ts +54 -0
  63. package/dist/types/core/core.d.ts +15 -0
  64. package/dist/types/core/dev.d.ts +1 -1
  65. package/dist/types/core/lanes.d.ts +8 -1
  66. package/dist/types/core/scheduler.d.ts +36 -4
  67. package/dist/types/core/truth.d.ts +3 -1
  68. package/dist/types/core/types.d.ts +6 -0
  69. package/dist/types/index.d.ts +2 -0
  70. package/dist/types/store/hooks.d.ts +15 -1
  71. package/dist/types/store/store.d.ts +2 -1
  72. package/dist/types/store/target.d.ts +5 -0
  73. package/dist/types/store/types.d.ts +7 -0
  74. package/package.json +1 -1
@@ -1,4 +1,10 @@
1
- import "./core.js";
1
+ import { context } from "./core.js";
2
+
3
+ const diagnosticListeners = new Set;
4
+
5
+ const diagnosticCaptures = new Set;
6
+
7
+ let diagnosticSequence = 0;
2
8
 
3
9
  /**
4
10
  * Registers the console footer appended to the first console report of
@@ -11,10 +17,109 @@ import "./core.js";
11
17
  * @internal A seam for `solid-js`, which owns the repair skill the footer
12
18
  * names and installs it from both of its entries; not part of `DEV`. No-op
13
19
  * outside dev builds, where nothing reports to the console.
14
- */ function setConsoleFooter(o) {
20
+ */ function setConsoleFooter(e) {
15
21
  return;
16
22
  }
17
23
 
24
+ /** Events built for an excluded subject: never delivered, never reported. */ const suppressedEvents = new WeakSet;
25
+
26
+ function isExcluded(e) {
27
+ return false;
28
+ }
29
+
18
30
  const DEV = undefined;
19
31
 
20
- export { DEV, setConsoleFooter };
32
+ /**
33
+ * Root-first names of the owners enclosing `subject` (inclusive when the
34
+ * subject is itself a named owner). Signals hop to their registering owner
35
+ * (`_owner`, set by registerGraph). Unnamed owners are skipped so the path
36
+ * reads as the component tree plus the scope: `<App> › <TodoRow> › effect`.
37
+ * Public as `OBSERVE.ownerPath`; the core's own sites import it directly.
38
+ */ function ownerPath(e) {
39
+ if (!e) return undefined;
40
+ let n = "_parent" in e ? e : e.H ?? null;
41
+ const t = [];
42
+ for (;n !== null; n = n._parent) {
43
+ const e = n._name;
44
+ if (typeof e === "string" && e.length) t.push(e);
45
+ }
46
+ return t.length ? t.reverse() : undefined;
47
+ }
48
+
49
+ /**
50
+ * The innermost source location on the subject's chain: the subject's own
51
+ * `_loc` (a signal or computation created with `{ loc }`), else the first
52
+ * `_loc` up the owners — a signal hops to its registering owner, then
53
+ * `_parent` upward (a component root carries its JSX site). The same walk
54
+ * `ownerPath` makes; `undefined` when nothing on the chain was located.
55
+ */ function locationOf(e) {
56
+ // Owners have a `_parent` key (null on a root); a signal has none and hops
57
+ // to its registering `_owner` instead.
58
+ for (let n = e; n; n = n._parent !== undefined ? n._parent : n.H) if (n._loc) return n._loc;
59
+ return undefined;
60
+ }
61
+
62
+ /**
63
+ * Records a diagnostic on the structured channel (listeners, captures) and
64
+ * returns the entry. `subject` locates it: the current reactive `context` by
65
+ * default (right for the synchronous rule checks — they fire inside the
66
+ * scope that misbehaved); pass the node for scheduler-time findings whose
67
+ * ambient context is the flush, or `null` for events that have no location
68
+ * by nature. An `ownerPath` (or `loc`) already on the event wins over the
69
+ * subject walk (hosts whose owners are not signals' owners compute their
70
+ * own). Console output is a separate, dev-tier step — see `reportDiagnostic`.
71
+ */ function emitDiagnostic(e, n = context) {
72
+ const t = {
73
+ sequence: ++diagnosticSequence,
74
+ ...e
75
+ };
76
+ // The observer's own subtree: build the entry (the caller may throw its
77
+ // message) but tell nobody.
78
+ if (isExcluded()) {
79
+ suppressedEvents.add(t);
80
+ return t;
81
+ }
82
+ if (t.ownerPath === undefined) {
83
+ const e = ownerPath(n);
84
+ if (e) t.ownerPath = e;
85
+ }
86
+ if (t.loc === undefined) {
87
+ const e = locationOf(n);
88
+ if (e !== undefined) t.loc = e;
89
+ }
90
+ const o = n ?? undefined;
91
+ if (o !== undefined) eventSubjects.set(t, o);
92
+ for (const e of diagnosticListeners) e(t, o);
93
+ for (const e of diagnosticCaptures) e.push(t);
94
+ return t;
95
+ }
96
+
97
+ /**
98
+ * The subject each emitted event was about, for the console step, which
99
+ * runs after `emitDiagnostic` returned and can show what the node knows: a
100
+ * rendering runtime may stamp a binding effect with the DOM element it
101
+ * writes (`_devElement`), and a live element reference beside the message
102
+ * is the most addressable pointer a console can print. Listeners get the
103
+ * subject as their second argument instead; the map exists only to carry
104
+ * it from `emitDiagnostic` to `reportDiagnostic` across the call site's
105
+ * `reportDiagnostic(emitDiagnostic(…))`. Weak, keyed by the entry.
106
+ */ const eventSubjects = new WeakMap;
107
+
108
+ /**
109
+ * The console face of a diagnostic — ONE entry per finding: the message, the
110
+ * owner path (`in <App> › <TodoRow> › effect`) so a human can locate it, the
111
+ * source location (`at src/App.tsx:18:3` — see `locationLine`) when a node on
112
+ * the chain was compiled with one, the once-per-code footer as trailing
113
+ * lines, and — when the subject is a binding effect the rendering runtime
114
+ * tagged — the element it writes, as a second console argument (hover
115
+ * highlights it, click jumps to Elements).
116
+ * Severity picks the console method. Call sites report the entry
117
+ * `emitDiagnostic` returned so the structured and console channels never
118
+ * disagree. Dev-tier: in an observe build this is a no-op, so wiring paths
119
+ * that both emit and report (graph-size warnings) reach the channel only —
120
+ * production observability never writes to the console.
121
+ */ function reportDiagnostic(e) {
122
+ return;
123
+ }
124
+
125
+ export { DEV, emitDiagnostic, isExcluded, ownerPath, reportDiagnostic, setConsoleFooter };
@@ -22,7 +22,8 @@ import { globalQueue, schedule, heldRunHook, haltReactivity, GlobalQueue } from
22
22
  // staged); the flush's commit replays this effect. Its first run is not this
23
23
  // creation's (A29). A lane's node likewise: its run is the lane's, queued
24
24
  // by the pass and released at the reveal (lanes.ts).
25
- !u?.defer && f.ie === NOT_PENDING && !f.T?.Re?.P && (f.i === EFFECT_USER || u?.schedule ? globalQueue.enqueue(f.i, runEffect.bind(null, f)) : runEffect(f, f.i));
25
+ !u?.defer && f.ue === NOT_PENDING && !f.T?.Re?.P && (f.i === EFFECT_USER || u?.schedule ? globalQueue.enqueue(f.i, runEffect.bind(null, f)) : // Type 0: the synchronous first render, not a queued run (D-19).
26
+ runEffect(f, 0));
26
27
  }
27
28
 
28
29
  function notifyEffectStatus() {
@@ -44,18 +45,20 @@ function notifyEffectStatus() {
44
45
  return;
45
46
  }
46
47
  // Dev locates the halt at the node that threw: the StatusError's
47
- // source, upstream of this effect when a computed failed.
48
- escalate(this, unwrapStatusError(e), e, this);
48
+ // source, upstream of this effect when a computed failed. The rethrow is
49
+ // the user's error, not the wrapper: the wrapper's `source` is a node, and
50
+ // a reporter that serializes the throw walks the reactive graph.
51
+ escalate(this, unwrapStatusError(e), this);
49
52
  } else if (this.i === EFFECT_RENDER) {
50
53
  globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, t, e);
51
54
  }
52
55
  }
53
56
 
54
57
  /** An effect's error no boundary takes halts the system (boundary-or-halt):
55
- * halted with `cause`, `thrown` rethrown; dev locates it at `subject`. */ function escalate(t, e, E = e, u = t) {
58
+ * halted with `cause` and rethrown; dev locates it at `subject`. */ function escalate(t, e, E = t) {
56
59
  if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
57
60
  haltReactivity(e);
58
- throw E;
61
+ throw e;
59
62
  }
60
63
  }
61
64
 
@@ -66,10 +69,12 @@ function runEffect(t, e) {
66
69
  // effect's update too (maintainer, 2026-10-02: "we held the queue but let
67
70
  // the sync renders through… we definitely shouldn't be showing portals
68
71
  // early") — a Portal's or head-tag's render effect writes outside the
69
- // hidden subtree. The synchronous first render on creation is not a queued
70
- // run and goes through. `_modified` stays set; the boundary re-queues the
71
- // run (`release`).
72
- if (heldRunHook?.(t)) return;
72
+ // hidden subtree. The synchronous first render on creation (`type` 0) is
73
+ // not a queued run and goes through, whichever boundary above is on its
74
+ // fallback then — an outer one about to reveal in this flush included
75
+ // (#4343). `_modified` stays set; the boundary re-queues the run
76
+ // (`release`).
77
+ if (e && heldRunHook?.(t)) return;
73
78
  // Error arm (#2840), user effects only: a compute-phase error that is still
74
79
  // the node's settled state at effect time runs the bundle's error handler in
75
80
  // this same imperative, writable scope. Unwrap the StatusError used for
@@ -82,12 +87,12 @@ function runEffect(t, e) {
82
87
  // same flush must not be hijacked by a later-arriving error status.
83
88
  if (t.m & STATUS_ERROR && t.i === EFFECT_USER) {
84
89
  const e = unwrapStatusError(t.T?.U);
85
- t.ke = t.le;
90
+ t.ge = t.le;
86
91
  t.Ae = false;
87
92
  try {
88
- t.qe ? t.qe(e, () => {
89
- const e = t.Je;
90
- t.Je = undefined;
93
+ t.Je ? t.Je(e, () => {
94
+ const e = t.ze;
95
+ t.ze = undefined;
91
96
  e?.();
92
97
  }) : console.error(e);
93
98
  } catch (e) {
@@ -98,20 +103,20 @@ function runEffect(t, e) {
98
103
  // Captured before the callback: its own throw errors the node below, but
99
104
  // the compute pass that produced `_value` was clean, so its tail still goes.
100
105
  const E = t.T?.U == null;
101
- const u = t.Je;
102
- t.Je = undefined;
106
+ const u = t.ze;
107
+ t.ze = undefined;
103
108
  try {
104
109
  u?.();
105
- const e = t.Ke(t.le, t.ke);
110
+ const e = t.qe(t.le, t.ge);
106
111
  if (false && e !== undefined && typeof e !== "function") ;
107
112
  // The final cleanup is invoked by disposeChildren at true disposal.
108
- t.Je = e;
113
+ t.ze = e;
109
114
  } catch (e) {
110
115
  ext(t).U = new StatusError(t, e);
111
116
  t.m |= STATUS_ERROR;
112
117
  escalate(t, e);
113
118
  } finally {
114
- t.ke = t.le;
119
+ t.ge = t.le;
115
120
  t.Ae = false;
116
121
  // The run applied: this is the frame now, so the dependency tail the
117
122
  // compute pass left linked goes (A30, #3438 — `recompute` defers an
@@ -122,7 +127,7 @@ function runEffect(t, e) {
122
127
  }
123
128
  }
124
129
 
125
- GlobalQueue.K = runEffect;
130
+ GlobalQueue.q = runEffect;
126
131
 
127
132
  /**
128
133
  * Internal tracked effect - bypasses heap, goes directly to effect queue.
@@ -141,16 +146,16 @@ GlobalQueue.K = runEffect;
141
146
  } finally {}
142
147
  };
143
148
  const E = computed(() => {
144
- const e = E.Je;
145
- E.Je = undefined;
149
+ const e = E.ze;
150
+ E.ze = undefined;
146
151
  e?.();
147
152
  const u = staleValues(t);
148
- E.Je = u;
153
+ E.ze = u;
149
154
  }, {
150
155
  ...e,
151
156
  lazy: true
152
157
  });
153
- E.Je = undefined;
158
+ E.ze = undefined;
154
159
  E.l = E.l & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
155
160
  E.Ae = true;
156
161
  E.i = EFFECT_TRACKED;
@@ -20,8 +20,8 @@ function unlinkSubs(e) {
20
20
  const n = e.S;
21
21
  const l = e.G;
22
22
  const o = e.t;
23
- const s = e.Xe;
24
- if (o !== null) o.Xe = s; else n.ve = s;
23
+ const s = e.$e;
24
+ if (o !== null) o.$e = s; else n.Ze = s;
25
25
  if (s !== null) s.t = o; else {
26
26
  n.o = o;
27
27
  if (o === null) {
@@ -29,7 +29,7 @@ function unlinkSubs(e) {
29
29
  // unobserved closure, no NodeExtension to hold it.
30
30
  // A user hook that throws is collected like a cleanup (owner.ts): the
31
31
  // unlink and the teardown around it carry on.
32
- if (n.l & CONFIG_SLOT_NODE) slotUnobservedHook(n); else if (n.T?.we) runCleanup(n.T.we);
32
+ if (n.l & CONFIG_SLOT_NODE) slotUnobservedHook(n); else if (n.T?.Ye) runCleanup(n.T.Ye);
33
33
  // No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
34
34
  // A pending node is exempt: its in-flight async work is an observer —
35
35
  // tearing down would orphan the work and re-execute it on the next
@@ -133,7 +133,7 @@ function link(e, n) {
133
133
  if (s) {
134
134
  o = l !== null ? l.G : n.C;
135
135
  if (o !== null && o.S === e) {
136
- o.de = n.Ne;
136
+ o.Ne = n.de;
137
137
  n._ = o;
138
138
  return;
139
139
  }
@@ -143,15 +143,15 @@ function link(e, n) {
143
143
  // [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
144
144
  // (the old alien-signals `isValidLink` walk, O(n²) when a computation
145
145
  // re-reads earlier deps non-consecutively, e.g. store leaf reads).
146
- const r = e.ve;
147
- if (r !== null && r.u === n && (!s || r.de === n.Ne)) return;
148
- const t = n._ = e.ve = {
146
+ const r = e.Ze;
147
+ if (r !== null && r.u === n && (!s || r.Ne === n.de)) return;
148
+ const t = n._ = e.Ze = {
149
149
  S: e,
150
150
  u: n,
151
151
  G: o,
152
- Xe: r,
152
+ $e: r,
153
153
  t: null,
154
- de: n.Ne
154
+ Ne: n.de
155
155
  };
156
156
  if (l !== null) l.G = t; else n.C = t;
157
157
  if (r !== null) r.t = t; else e.o = t;
@@ -16,17 +16,17 @@ import { dirtyQueue } from "./scheduler.js";
16
16
  }
17
17
 
18
18
  function actualInsertIntoHeap(e, t) {
19
- const E = (e._parent?.Qe ? e._parent.Ye?.he : e._parent?.he) ?? -1;
19
+ const E = (e._parent?.Qe ? e._parent.Ke?.he : e._parent?.he) ?? -1;
20
20
  if (E >= e.he) e.he = E + 1;
21
21
  const n = e.he;
22
- const I = t.$e[n];
23
- if (I === undefined) t.$e[n] = e; else {
22
+ const I = t.et[n];
23
+ if (I === undefined) t.et[n] = e; else {
24
24
  const t = I.We;
25
- t.be = e;
25
+ t.ve = e;
26
26
  e.We = t;
27
27
  I.We = e;
28
28
  }
29
- if (n > t.et) t.et = n;
29
+ if (n > t.Et) t.Et = n;
30
30
  }
31
31
 
32
32
  function insertIntoHeap(e, t) {
@@ -66,22 +66,22 @@ function deleteFromHeap(e, t) {
66
66
  if (!(E & (REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT))) return;
67
67
  e.R = E & -25;
68
68
  const n = e.he;
69
- if (e.We === e) t.$e[n] = undefined; else {
70
- const E = e.be;
71
- const I = t.$e[n];
69
+ if (e.We === e) t.et[n] = undefined; else {
70
+ const E = e.ve;
71
+ const I = t.et[n];
72
72
  const i = E ?? I;
73
- if (e === I) t.$e[n] = E; else e.We.be = E;
73
+ if (e === I) t.et[n] = E; else e.We.ve = E;
74
74
  i.We = e.We;
75
75
  }
76
76
  e.We = e;
77
- e.be = undefined;
77
+ e.ve = undefined;
78
78
  }
79
79
 
80
80
  function markHeap(e) {
81
81
  if (e.p) return;
82
82
  e.p = true;
83
- for (let t = 0; t <= e.et; t++) {
84
- for (let E = e.$e[t]; E !== undefined; E = E.be) {
83
+ for (let t = 0; t <= e.Et; t++) {
84
+ for (let E = e.et[t]; E !== undefined; E = E.ve) {
85
85
  if (E.R & REACTIVE_IN_HEAP) markNode(E);
86
86
  }
87
87
  }
@@ -98,14 +98,14 @@ function markNode(e, t = REACTIVE_DIRTY) {
98
98
 
99
99
  function runHeap(e, t) {
100
100
  e.p = false;
101
- for (e.Fe = 0; e.Fe <= e.et; e.Fe++) {
102
- let E = e.$e[e.Fe];
101
+ for (e.Fe = 0; e.Fe <= e.Et; e.Fe++) {
102
+ let E = e.et[e.Fe];
103
103
  while (E !== undefined) {
104
104
  if (E.R & REACTIVE_IN_HEAP) t(E); else adjustHeight(E, e);
105
- E = e.$e[e.Fe];
105
+ E = e.et[e.Fe];
106
106
  }
107
107
  }
108
- e.et = 0;
108
+ e.Et = 0;
109
109
  }
110
110
 
111
111
  function adjustHeight(e, t) {
@@ -31,7 +31,7 @@ function identify(n) {
31
31
  if (!e) return;
32
32
  while (e.l & CONFIG_TRANSPARENT && e._parent) e = e._parent;
33
33
  if (e.id == null) return;
34
- const t = e.Ne;
34
+ const t = e.de;
35
35
  let i = counts.get(e);
36
36
  if (i === undefined || i[0] !== t) counts.set(e, i = [ t, 0 ]);
37
37
  const s = e.id + "~" + i[1]++;