@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,14 +1,6 @@
1
- import { $REFRESH, STORE_SNAPSHOT_PROPS, NOT_PENDING, unwrapOverride, STATUS_ERROR, STATUS_UNINITIALIZED, STATUS_PENDING, NO_SNAPSHOT } from "../core/constants.js";
1
+ import "../core/core.js";
2
2
 
3
- import { suppressComputedRecompute, isEqual, signal, pendingCheckActive, untrack, setSignal, read, readNodeFast, READ_SLOW, snapshotCaptureActive, snapshotSources } from "../core/core.js";
4
-
5
- import { DEV } from "../core/dev.js";
6
-
7
- import { NotReadyError } from "../core/error.js";
8
-
9
- import { getObserver } from "../core/owner.js";
10
-
11
- import { GlobalQueue, registerTransientStoreNode, projectionWriteActive, activeTransition, globalQueue } from "../core/scheduler.js";
3
+ import { GlobalQueue } from "../core/scheduler.js";
12
4
 
13
5
  import "../core/invariants.js";
14
6
 
@@ -16,85 +8,29 @@ import "../core/verdict.js";
16
8
 
17
9
  import "../core/effect.js";
18
10
 
19
- import { createProjectionInternal } from "./projection.js";
11
+ import { storeNextLookup } from "./next/target.js";
20
12
 
21
13
  /**
22
14
  * Brand symbols used internally by the store proxy / projection plumbing.
23
15
  * Cross-package wiring; not part of the user-facing API.
24
16
  *
25
17
  * @internal
26
- */ const $TRACK = Symbol(0), $TARGET = Symbol(0), $PROXY = Symbol(0), $DELETED = Symbol(0),
27
- // Node-map slot carrying a record-level `affects()` mark: any read through
18
+ */ const $TRACK = Symbol(0), $TARGET = Symbol(0), $PROXY = Symbol(0), // Node-map slot carrying a record-level `affects()` mark: any read through
28
19
  // the record witnesses it into the active isPending() probe.
29
20
  $AFFECTS = Symbol(0);
30
21
 
31
- const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p", STORE_OPTIMISTIC_OWNERS = "t", STORE_PARENT = "u", STORE_DESC = "d", STORE_SHALLOW = "s";
32
-
33
- const STORE_SELF_PENDING = Symbol(0);
34
-
35
- // Every StoreNode field is initialized up front, in one fixed order, so all
36
- // targets share a single hidden class. The traps and reconcile read these
37
- // fields on their hottest paths; fields added lazily in varying orders made
38
- // those loads megamorphic across a large store graph (dictionary-mode probes
39
- // on every trap hit). Assignments elsewhere must never `delete` a field —
40
- // write `undefined` instead, or the shape degrades again.
41
- function initStoreFields(e) {
42
- e[STORE_OVERRIDE] = undefined;
43
- e[STORE_OPTIMISTIC_OVERRIDE] = undefined;
44
- e[STORE_OPTIMISTIC_OWNERS] = undefined;
45
- e[STORE_NODE] = undefined;
46
- e[STORE_HAS] = undefined;
47
- e[STORE_CUSTOM_PROTO] = undefined;
48
- e[STORE_WRAP] = undefined;
49
- e[STORE_LOOKUP] = undefined;
50
- e[STORE_FIREWALL] = undefined;
51
- e[STORE_OPTIMISTIC] = undefined;
52
- e[STORE_SNAPSHOT_PROPS] = undefined;
53
- e[STORE_PARENT] = undefined;
54
- e[STORE_DESC] = undefined;
55
- e[STORE_SHALLOW] = undefined;
56
- e[$PROXY] = null;
57
- }
58
-
59
- function createStoreProxy(e, t = storeTraps, r) {
60
- let n;
61
- if (Array.isArray(e)) {
62
- n = [];
63
- n[STORE_VALUE] = e;
64
- initStoreFields(n);
65
- } else {
66
- n = {
67
- [STORE_VALUE]: e
68
- };
69
- initStoreFields(n);
70
- const t = e?.[$TARGET]?.[STORE_VALUE] ?? e;
71
- const r = Object.getPrototypeOf(t);
72
- if (r !== null && r !== Object.prototype) {
73
- n[STORE_CUSTOM_PROTO] = true;
74
- }
75
- }
76
- r && r(n);
77
- return n[$PROXY] = new Proxy(n, t);
78
- }
79
-
80
- // The global lookup maps raw value -> StoreNode TARGET (not proxy): reconcile
81
- // and the unwrap/snapshot walks resolve targets through it constantly, and a
82
- // target hit is a plain field read away from its proxy while a proxy hit
83
- // costs a trap to get back to the target. Per-family STORE_LOOKUP maps
84
- // (projections/optimistic) still map raw -> proxy — their wrap functions own
85
- // that contract — so mixed-lookup consumers resolve through lookupTarget().
86
- const storeLookup = new WeakMap;
87
-
88
- // Node records that hold at least one user (non-`$TRACK`) symbol-keyed node.
89
- // Lets reconcile enumerate symbols only for records that need it (#2851).
90
- const symbolKeyedRecords = new WeakSet;
22
+ // Structural field names of store targets (StoreNextTarget aliases these, so
23
+ // shared machinery — affects walks, tests — reads targets via the consts).
24
+ const STORE_VALUE = "v", STORE_NODE = "n", STORE_HAS = "h";
91
25
 
92
26
  function lookupTarget(e, t) {
93
- if (t !== undefined && t !== storeLookup) {
94
- const r = t.get(e);
95
- if (r !== undefined) return r[$TARGET];
27
+ // Family maps (projections/optimistic) map raw -> target; the global next
28
+ // lookup maps raw -> target too. Proxies resolve through $TARGET directly.
29
+ if (t !== undefined) {
30
+ const o = t.get(e);
31
+ if (o !== undefined) return o[$TARGET] ?? o;
96
32
  }
97
- return storeLookup.get(e);
33
+ return storeNextLookup.get(e);
98
34
  }
99
35
 
100
36
  // Values marked raw never acquire a proxy identity: wrap() serves them as-is
@@ -137,50 +73,12 @@ function markRawOne(e) {
137
73
 
138
74
  function markRawIngest(e) {
139
75
  if (Array.isArray(e)) {
140
- for (let t = 0, r = e.length; t < r; t++) markRawOne(e[t]);
76
+ for (let t = 0, o = e.length; t < o; t++) markRawOne(e[t]);
141
77
  } else {
142
78
  for (const t in e) markRawOne(e[t]);
143
79
  }
144
80
  }
145
81
 
146
- function wrap(e, t) {
147
- // Raw is raw in every family: the mark must preempt family wrapping too,
148
- // or a shallow projection/optimistic store would proxy its raw records.
149
- if (rawValuesUsed && rawValues.has(e)) return e;
150
- if (t?.[STORE_WRAP]) {
151
- const r = t[STORE_WRAP](e, t);
152
- const n = r[$TARGET];
153
- if (n && !n[STORE_PARENT] && n !== t) n[STORE_PARENT] = t;
154
- return r;
155
- }
156
- const r = storeLookup.get(e);
157
- if (r !== undefined) return r[$PROXY];
158
- let n = e[$PROXY];
159
- if (!n) {
160
- n = createStoreProxy(e);
161
- const r = n[$TARGET];
162
- storeLookup.set(e, r);
163
- if (t) r[STORE_PARENT] = t;
164
- }
165
- return n;
166
- }
167
-
168
- // Shallow store root: the target itself is fully reactive (per-key nodes,
169
- // membership, $TRACK); its values are served raw. Seed values are marked at
170
- // creation; reconcile and the set trap mark on ingest.
171
- function wrapShallow(e) {
172
- const t = storeLookup.get(e);
173
- if (t !== undefined) {
174
- if (t[STORE_SHALLOW]) return t[$PROXY];
175
- }
176
- const r = createStoreProxy(e);
177
- const n = r[$TARGET];
178
- n[STORE_SHALLOW] = true;
179
- storeLookup.set(e, n);
180
- markRawIngest(e);
181
- return r;
182
- }
183
-
184
82
  const OBJECT_PROTO = Object.prototype;
185
83
 
186
84
  // Per-prototype memo for the custom-proto branch of isWrappable: the verdict
@@ -204,16 +102,16 @@ function isWrappable(e) {
204
102
  const t = Object.getPrototypeOf(e);
205
103
  if (t === OBJECT_PROTO || t === null) return true;
206
104
  if (Array.isArray(e)) return true;
207
- let r = wrappableProtos.get(t);
208
- if (r === undefined) {
209
- r = Object.prototype.toString.call(e) === "[object Object]" && (
105
+ let o = wrappableProtos.get(t);
106
+ if (o === undefined) {
107
+ o = Object.prototype.toString.call(e) === "[object Object]" && (
210
108
  // Dynamic Node check (kept dynamic so test/SSR overrides of
211
109
  // `globalThis.Node` are observed at call time): shimmed DOMs implement
212
110
  // nodes as plain user classes, which pass the tag check.
213
111
  typeof Node === "undefined" || !(e instanceof Node));
214
- wrappableProtos.set(t, r);
112
+ wrappableProtos.set(t, o);
215
113
  }
216
- return r;
114
+ return o;
217
115
  }
218
116
 
219
117
  let writeOverride = false;
@@ -222,33 +120,8 @@ function setWriteOverride(e) {
222
120
  writeOverride = e;
223
121
  }
224
122
 
225
- function writeOnly(e) {
226
- return writeOverride || !!Writing?.has(e);
227
- }
228
-
229
- function unwrapStoreValue(e, t, r) {
230
- const n = e?.[$TARGET] || lookupTarget(e, r);
231
- if (!n) return e;
232
- const o = n[STORE_OVERRIDE];
233
- if (!o) return n[STORE_VALUE];
234
- if (!t) t = new Map;
235
- if (t.has(e)) return t.get(e);
236
- const i = n[STORE_VALUE];
237
- const O = Array.isArray(i);
238
- const s = O ? [] : Object.create(Object.getPrototypeOf(i));
239
- t.set(e, s);
240
- r = n[STORE_LOOKUP] ?? storeLookup;
241
- for (const e of getStoreKeys(i, o)) {
242
- if (O && e === "length") continue;
243
- const n = e in o ? o[e] : i[e];
244
- if (n !== $DELETED) s[e] = unwrapStoreValue(n, t, r);
245
- }
246
- if (O) s.length = o.length ?? i.length;
247
- return s;
248
- }
249
-
250
- function isPrototypePollutionKey(e) {
251
- return e === "__proto__" || e === "constructor" || e === "prototype";
123
+ function getWriteOverride() {
124
+ return writeOverride;
252
125
  }
253
126
 
254
127
  // Own enumerable keys including symbols (`Object.keys` drops symbol-keyed props). #2769
@@ -256,134 +129,45 @@ function ownEnumerableKeys(e) {
256
129
  return Reflect.ownKeys(e).filter(t => Object.prototype.propertyIsEnumerable.call(e, t));
257
130
  }
258
131
 
259
- function ownEnumerableSymbols(e) {
260
- const t = Object.getOwnPropertySymbols(e);
261
- const r = [];
262
- for (let n = 0, o = t.length; n < o; n++) {
263
- const o = t[n];
264
- if (Object.prototype.propertyIsEnumerable.call(e, o)) r.push(o);
265
- }
266
- return r;
267
- }
268
-
269
- // Plain-object variant that keeps Object.keys() as the fast path and only pays
270
- // descriptor checks for symbols. Do not use this on store proxies: splitting
271
- // strings/symbols would invoke their ownKeys trap twice.
272
- function ownEnumerableKeysPlain(e) {
273
- return Object.keys(e).concat(ownEnumerableSymbols(e));
274
- }
275
-
276
- /**
277
- * Single chokepoint for the store's layered value resolution: returns the
278
- * override layer (optimistic first, then regular) that shadows `property`, or
279
- * `undefined` when the base `STORE_VALUE` is authoritative. Every trap must
280
- * resolve through this — hand-inlining the layer order is how the optimistic
281
- * layer gets missed (#2850).
282
- */ function getOverlayLayer(e, t) {
283
- const r = e[STORE_OPTIMISTIC_OVERRIDE];
284
- if (r && t in r) return r;
285
- const n = e[STORE_OVERRIDE];
286
- if (n && t in n) return n;
287
- return undefined;
288
- }
289
-
290
- /**
291
- * The value a store leaf's backing signal currently shows to readers: active
292
- * override, else held pending value, else committed value.
293
- */ function visibleNodeValue(e) {
294
- return e._e !== undefined && e._e !== NOT_PENDING ? unwrapOverride(e._e) : e.De !== NOT_PENDING ? e.De : e.Ue;
295
- }
296
-
297
- function hasOwnStoreProperty(e, t) {
298
- // Override layers are null-prototype objects, so `in` is an own check.
299
- const r = getOverlayLayer(e, t);
300
- if (r) return r[t] !== $DELETED;
301
- return Object.prototype.hasOwnProperty.call(unwrapStoreValue(e[STORE_VALUE]), t);
302
- }
303
-
304
- function hasInheritedAccessor(e, t) {
305
- let r = Object.getPrototypeOf(e);
306
- while (r && r !== Object.prototype) {
307
- const e = Reflect.getOwnPropertyDescriptor(r, t);
308
- if (e) return !!e.get;
309
- r = Object.getPrototypeOf(r);
310
- }
311
- return false;
312
- }
313
-
314
- function getNodes(e, t) {
315
- let r = e[t];
316
- if (!r) e[t] = r = Object.create(null);
317
- return r;
318
- }
319
-
320
- function getNode(e, t, r, n, o = isEqual, i) {
321
- if (t[r]) return t[r];
322
- const O = signal(n, {
323
- equals: o,
324
- unobserved() {
325
- if (t[r] === O) {
326
- delete t[r];
327
- // Drop the symbol-record mark once the last user symbol node is
328
- // gone, so reconcile's fast path stops probing a now string-only
329
- // record. Runs only on symbol-node cleanup (cold), never on reconcile.
330
- if (typeof r === "symbol" && r !== $TRACK && r !== $AFFECTS && symbolKeyedRecords.has(t)) {
331
- const e = Object.getOwnPropertySymbols(t);
332
- let r = false;
333
- for (let t = 0, n = e.length; t < n; t++) {
334
- if (e[t] !== $TRACK && e[t] !== $AFFECTS) {
335
- r = true;
336
- break;
337
- }
338
- }
339
- if (!r) symbolKeyedRecords.delete(t);
340
- }
341
- }
342
- }
343
- }, e[STORE_FIREWALL]);
344
- if (e[STORE_OPTIMISTIC]) {
345
- O._e = NOT_PENDING;
346
- }
347
- if (i && r in i) {
348
- const e = i[r];
349
- O.Le = e === undefined ? NO_SNAPSHOT : e;
350
- snapshotSources?.add(O);
351
- }
352
- if (typeof r === "symbol" && r !== $TRACK && r !== $AFFECTS) symbolKeyedRecords.add(t);
353
- // A node born inside a live mark's identity scope inherits the mark
354
- // (the declaration walk could only cover nodes that existed then). The
355
- // record's own $AFFECTS carrier is the mark's channel, never a member.
356
- if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(O, e[STORE_VALUE], r);
357
- // Node presence bubbles up the wrap chain (sticky), so reconcile can see
358
- // "subscribers live somewhere below" through node-less intermediate
359
- // records — the captured-proxy diff gate (#2902). Amortized O(1): stops at
360
- // the first already-flagged ancestor.
361
- let s = e;
362
- while (s && !s[STORE_DESC]) {
363
- s[STORE_DESC] = true;
364
- s = s[STORE_PARENT];
365
- }
366
- return t[r] = O;
367
- }
368
-
369
132
  /**
370
133
  * Scope inheritance for late-created nodes: every live mark whose identity
371
134
  * scope contains the owning record's raw — and, for keyed marks, whose key
372
135
  * is this property — gets counted on the new node. Inherited marks live
373
136
  * exactly as long as the scope's carrier — the release hook below drops
374
137
  * them with the entry.
375
- */ function inheritAffectsMarks(e, t, r) {
138
+ */ function inheritAffectsMarks(e, t, o) {
376
139
  // A live scope exists, so affects.ts already installed the mark engine.
377
- for (const [n, o] of affectsScopes) {
378
- if (n.t && o.scope.has(t) && (o.key === undefined || o.key === r)) {
379
- GlobalQueue.M(e);
380
- o.inherited.push(e);
140
+ for (const [r, s] of affectsScopes) {
141
+ if (r.o?.t && s.scope.has(t) && (s.key === undefined || s.key === o)) {
142
+ GlobalQueue.h(e);
143
+ s.inherited.push(e);
381
144
  }
382
145
  }
383
146
  }
384
147
 
385
148
  const affectsScopes = new Map;
386
149
 
150
+ /** Next-store node factory for affects carriers/slots: injected by the
151
+ * rewrite module (next targets alias the legacy field names, so everything
152
+ * here EXCEPT node creation works on them structurally). */ let nextAffectsNodeResolver = null;
153
+
154
+ function setNextAffectsNodeResolver(e) {
155
+ nextAffectsNodeResolver = e;
156
+ }
157
+
158
+ /** Next-store optimistic view for the declaration walk (optimistic rows
159
+ * pushed before the declaration are in motion too — legacy reads its write
160
+ * overlays; next composes armed-node overrides). */ let nextOptimisticViewResolver = null;
161
+
162
+ function setNextOptimisticViewResolver(e) {
163
+ nextOptimisticViewResolver = e;
164
+ }
165
+
166
+ /** @internal birth inheritance for nodes created inside a live mark window —
167
+ * exported for the rewrite's node factories. */ function affectsScopesLive() {
168
+ return affectsScopes.size > 0;
169
+ }
170
+
387
171
  /**
388
172
  * Snapshots the identities reachable from `value` into `scope`, reading
389
173
  * through write overlays (an optimistic row pushed before the declaration is
@@ -393,61 +177,65 @@ const affectsScopes = new Map;
393
177
  * readers subscribed through, so the mark registers on them directly and
394
178
  * rides the status rails to everything derived. (Nodes born later inherit
395
179
  * from the scope in `getNode`.)
396
- */ function walkAffectsScope(e, t, r, n,
180
+ */ function walkAffectsScope(e, t, o, r,
397
181
  // Cycle guard, fresh per declaration: the scope itself can't serve — a
398
182
  // re-declaration on the same carrier unions into a scope that already
399
183
  // holds the root, and must still descend to pick up records added since.
400
- o) {
184
+ s) {
401
185
  if (!isWrappable(e)) return;
402
- const i = e[$TARGET] || lookupTarget(e, n);
403
- const O = i ? i[STORE_VALUE] : e;
404
- if (o.has(O)) return;
405
- o.add(O);
406
- t.scope.add(O);
407
- let s;
408
- if (i) {
409
- collectRecordNodes(i[STORE_NODE], r);
410
- collectRecordNodes(i[STORE_HAS], r);
411
- s = mergedOverlay(i);
412
- // Carry the effective lookup into untouched descendants. Default stores
413
- // use the global lookup just like snapshotImpl; without it, nested raw
414
- // objects fall back to string-only enumeration and symbol branches vanish.
415
- n = i[STORE_LOOKUP] ?? n ?? storeLookup;
186
+ const f = e[$TARGET] || lookupTarget(e, r);
187
+ // Next targets: walk the pending backing when present (a draft's writes are
188
+ // in motion too) and cover BOTH identities in the scope.
189
+ let n = f ? f.pb ?? f[STORE_VALUE] : e;
190
+ if (s.has(n)) return;
191
+ s.add(n);
192
+ t.scope.add(n);
193
+ if (f && f.pb) t.scope.add(f[STORE_VALUE]);
194
+ // Next optimistic families: enumerate the VISIBLE view (armed-node
195
+ // overrides compose membership/values the raw doesn't carry).
196
+ if (f && f.fam?.opt && nextOptimisticViewResolver) n = nextOptimisticViewResolver(f, n);
197
+ if (f) {
198
+ collectRecordNodes(f[STORE_NODE], o);
199
+ collectRecordNodes(f[STORE_HAS], o);
200
+ // The key-set and deep-witness nodes are record-level channels: a deep()
201
+ // probe reads ONLY these (one pair per record), so a declared affects
202
+ // scope must mark them like any property node.
203
+ if (f.k) o.push(f.k);
204
+ if (f.dk) o.push(f.dk);
205
+ // Carry the effective lookup into untouched descendants (family maps for
206
+ // projections/optimistic stores; the global next lookup otherwise).
207
+ r = f.fam?.map ?? r ?? storeNextLookup;
416
208
  }
417
- if (Array.isArray(O)) {
418
- const e = s?.length ?? O.length;
419
- for (let i = 0; i < e; i++) {
420
- const e = s && i in s ? s[i] : O[i];
421
- if (e !== $DELETED) walkAffectsScope(e, t, r, n, o);
209
+ // Overlays are gone (next has no layer): raw enumeration; the optimistic
210
+ // view composition above already folded armed-node membership/values in.
211
+ if (Array.isArray(n)) {
212
+ for (let e = 0, f = n.length; e < f; e++) {
213
+ walkAffectsScope(n[e], t, o, r, s);
422
214
  }
423
- // Arrays can also carry symbol metadata. Enumerate symbols separately to
424
- // avoid scanning large index lists twice. Outside a store tree, retain the
425
- // existing index-only walk.
426
- const E = i || n ? getStoreSymbols(O, s) : [];
427
- for (let e = 0, i = E.length; e < i; e++) {
428
- const i = E[e];
429
- const S = getPropertyDescriptor(O, s, i);
430
- if (!S || S.get) continue;
431
- walkAffectsScope(S.value, t, r, n, o);
215
+ const e = Object.getOwnPropertySymbols(n);
216
+ for (let f = 0, c = e.length; f < c; f++) {
217
+ const c = Object.getOwnPropertyDescriptor(n, e[f]);
218
+ if (!c || c.get) continue;
219
+ walkAffectsScope(c.value, t, o, r, s);
432
220
  }
433
221
  } else {
434
- const e = i || n ? getStoreKeys(O, s) : getKeys(O, s);
435
- for (let i = 0, E = e.length; i < E; i++) {
436
- const E = getPropertyDescriptor(O, s, e[i]);
437
- if (!E || E.get) continue;
438
- walkAffectsScope(E.value, t, r, n, o);
222
+ const e = Reflect.ownKeys(n);
223
+ for (let f = 0, c = e.length; f < c; f++) {
224
+ const c = Object.getOwnPropertyDescriptor(n, e[f]);
225
+ if (!c || c.get) continue;
226
+ walkAffectsScope(c.value, t, o, r, s);
439
227
  }
440
228
  }
441
229
  }
442
230
 
443
231
  /** All live signal nodes of one record's node map (string + symbol keyed). */ function collectRecordNodes(e, t) {
444
232
  if (!e) return;
445
- for (const r of Object.keys(e)) t.push(e[r]);
446
- const r = Object.getOwnPropertySymbols(e);
447
- for (let n = 0, o = r.length; n < o; n++) {
233
+ for (const o of Object.keys(e)) t.push(e[o]);
234
+ const o = Object.getOwnPropertySymbols(e);
235
+ for (let r = 0, s = o.length; r < s; r++) {
448
236
  // Another mark's carrier is its own channel — counting it here would
449
237
  // extend that sibling scope's lifetime to this declaration's.
450
- if (r[n] !== $AFFECTS) t.push(e[r[n]]);
238
+ if (o[r] !== $AFFECTS) t.push(e[o[r]]);
451
239
  }
452
240
  }
453
241
 
@@ -463,12 +251,28 @@ o) {
463
251
  */ function witnessAffectsMark(e, t) {
464
252
  // Callers guard on `pendingCheckActive`, which only flips inside
465
253
  // isPending() — the verdict layer is loaded and its hook installed.
466
- const r = e[STORE_NODE]?.[$AFFECTS];
467
- if (r?.t) GlobalQueue.Lt(r);
254
+ const o = e[STORE_NODE]?.[$AFFECTS];
255
+ if (o?.o?.t) GlobalQueue.Bt(o);
468
256
  if (affectsScopes.size) {
469
- const n = e[STORE_VALUE];
470
- for (const [e, o] of affectsScopes) {
471
- if (e !== r && e.t && o.scope.has(n) && (o.key === undefined || o.key === t)) GlobalQueue.Lt(e);
257
+ // Chained backings (§7b): a wrapper's STORE_VALUE can be another store's
258
+ // proxy marks cover by identity of the BASE raw, so resolve the chain
259
+ // and check every identity along it.
260
+ let r = e[STORE_VALUE];
261
+ for (const [e, s] of affectsScopes) {
262
+ if (e !== o && e.o?.t && (s.key === undefined || s.key === t)) {
263
+ let t = r;
264
+ for (;;) {
265
+ if (s.scope.has(t)) {
266
+ GlobalQueue.Bt(e);
267
+ break;
268
+ }
269
+ const o = t?.[$TARGET];
270
+ if (o === undefined) break;
271
+ const r = o.pb ?? o[STORE_VALUE];
272
+ if (r === t) break;
273
+ t = r;
274
+ }
275
+ }
472
276
  }
473
277
  }
474
278
  }
@@ -484,598 +288,38 @@ o) {
484
288
  *
485
289
  * @internal
486
290
  */ function getStoreAffectsNodes(e, t) {
487
- const r = getNodes(e, STORE_NODE);
488
- GlobalQueue.p ||= e => {
291
+ GlobalQueue.G ||= e => {
489
292
  const t = affectsScopes.get(e);
490
293
  if (!t) return;
491
294
  affectsScopes.delete(e);
492
- for (let e = 0; e < t.inherited.length; e++) GlobalQueue.h(t.inherited[e]);
295
+ for (let e = 0; e < t.inherited.length; e++) GlobalQueue.j(t.inherited[e]);
493
296
  };
494
297
  if (t === undefined) {
495
- const t = getNode(e, r, $AFFECTS, undefined, false);
496
- let n = affectsScopes.get(t);
497
- if (!n) affectsScopes.set(t, n = {
298
+ const t = nextAffectsNodeResolver(e, $AFFECTS);
299
+ let o = affectsScopes.get(t);
300
+ if (!o) affectsScopes.set(t, o = {
498
301
  scope: new Set,
499
302
  inherited: []
500
303
  });
501
- const o = [ t ];
502
- walkAffectsScope(e[$PROXY], n, o, e[STORE_LOOKUP], new Set);
503
- return o;
504
- }
505
- let n = r[t];
506
- if (!n) {
507
- const o = getOverlayLayer(e, t);
508
- const i = o ? o[t] : e[STORE_VALUE][t];
509
- n = upsertStoreNode(e, r, t, i === $DELETED ? undefined : i, e[STORE_SNAPSHOT_PROPS]);
304
+ const r = [ t ];
305
+ walkAffectsScope(e[$PROXY], o, r, e.fam?.map, new Set);
306
+ return r;
510
307
  }
308
+ const o = e.n?.[t] ?? nextAffectsNodeResolver(e, t);
511
309
  // Keyed marks resolve by identity too (#2904): another store family's
512
310
  // proxy can share this record's raw (a derived store swaps its backing to
513
311
  // the source's raw when its projection lands), and reads through it never
514
312
  // touch this target's node map. Scope is exactly the owning record's raw,
515
313
  // narrowed to this key for witness and birth inheritance.
516
- let o = affectsScopes.get(n);
517
- if (!o) affectsScopes.set(n, o = {
314
+ let r = affectsScopes.get(o);
315
+ if (!r) affectsScopes.set(o, r = {
518
316
  scope: new Set,
519
317
  inherited: [],
520
318
  key: t
521
319
  });
522
- o.scope.add(e[STORE_VALUE]);
523
- return [ n ];
524
- }
525
-
526
- function trackSelf(e, t = $TRACK) {
527
- if (!getObserver()) return;
528
- read(getNode(e, getNodes(e, STORE_NODE), t, undefined, false));
529
- // Store-in-store: structural notifications (reconcile, notifySelf) land on
530
- // the wrapped source's own self-node, never on this wrapper view's. Chain
531
- // the read through so enumeration/$TRACK on the wrapper observes them
532
- // (#2864). Property reads already chain naturally via the inner get trap.
533
- // An override layer on the view is a hold (A17) — the shown structure is
534
- // the overlay's, so don't subscribe through it; clearing the layer notifies
535
- // this view's own self-node and the re-run re-establishes the chain.
536
- if (t === $TRACK && !e[STORE_OVERRIDE] && !e[STORE_OPTIMISTIC_OVERRIDE] && e[STORE_VALUE][$TARGET]) e[STORE_VALUE][$TRACK];
537
- }
538
-
539
- function notifySelf(e) {
540
- const t = e[STORE_NODE]?.[$TRACK];
541
- t && setSignal(t, e[STORE_OPTIMISTIC] && !projectionWriteActive ? STORE_SELF_PENDING : undefined);
542
- }
543
-
544
- /**
545
- * The write overlay a walk must read through: optimistic writes shadow
546
- * regular pending writes, the same resolution order as every proxy trap and
547
- * `reconcile` (#2850). Merging allocates only in the rare both-present case
548
- * (a derived optimistic store with an in-flight projection commit).
549
- */ function mergedOverlay(e) {
550
- const t = e[STORE_OVERRIDE];
551
- const r = e[STORE_OPTIMISTIC_OVERRIDE];
552
- return t && r ? {
553
- ...t,
554
- ...r
555
- } : r ?? t;
556
- }
557
-
558
- function getKeysImpl(e, t, r, n) {
559
- // Plain objects can't trigger proxy traps — only pay for the untrack
560
- // closure when the source is itself a wrapped store (store-in-store).
561
- const o = e[$TARGET] ? untrack(() => r ? n ? ownEnumerableKeys(e) : Object.keys(e) : Reflect.ownKeys(e)) : r ? n ? ownEnumerableKeysPlain(e) : Object.keys(e) : Reflect.ownKeys(e);
562
- return t ? mergeOverrideKeys(o, t) : o;
563
- }
564
-
565
- function getKeys(e, t, r = true) {
566
- return getKeysImpl(e, t, r, false);
567
- }
568
-
569
- function getStoreKeys(e, t) {
570
- return getKeysImpl(e, t, true, true);
571
- }
572
-
573
- function getStoreSymbols(e, t) {
574
- const r = e[$TARGET] ? untrack(() => ownEnumerableSymbols(e)) : ownEnumerableSymbols(e);
575
- return t ? mergeOverrideKeys(r, t, true) : r;
576
- }
577
-
578
- // Shared override-layer merge for key enumeration: adds live override keys,
579
- // drops $DELETED ones. `symbolsOnly` scopes the override scan for the
580
- // array-metadata passes.
581
- function mergeOverrideKeys(e, t, r) {
582
- const n = new Set(e);
583
- const o = r ? Object.getOwnPropertySymbols(t) : Reflect.ownKeys(t);
584
- for (const e of o) {
585
- if (t[e] !== $DELETED) n.add(e); else n.delete(e);
586
- }
587
- return Array.from(n);
588
- }
589
-
590
- function getPropertyDescriptor(e, t, r) {
591
- if (t && r in t) {
592
- if (t[r] === $DELETED) return void 0;
593
- const n = Reflect.getOwnPropertyDescriptor(t, r);
594
- if (n?.get || n?.set) return n;
595
- // Plain writes live in the override while the source keeps its old value.
596
- // Preserve the source descriptor flags, but report the current override
597
- // value. Source accessors cannot be patched with a value, and inherited
598
- // properties have no source own descriptor, so those keep their descriptor.
599
- const o = Reflect.getOwnPropertyDescriptor(e, r);
600
- if (!o) return n;
601
- if (o.get || o.set) return o;
602
- // Reflect returns a fresh descriptor, so patching in place is safe and
603
- // avoids an allocation on Object.keys/spread over written stores.
604
- o.value = t[r];
605
- return o;
606
- }
607
- return Reflect.getOwnPropertyDescriptor(e, r);
608
- }
609
-
610
- function prepareStoreWrite(e, t, r) {
611
- if (e[STORE_OPTIMISTIC]) {
612
- const t = e[STORE_FIREWALL];
613
- if (t?.Ne) {
614
- globalQueue.initTransition(t.Ne);
615
- }
616
- }
617
- const n = e[STORE_VALUE];
618
- const o = n[r];
619
- if (snapshotCaptureActive && typeof r !== "symbol" && !((e[STORE_FIREWALL]?.S ?? 0) & STATUS_PENDING)) {
620
- if (!e[STORE_SNAPSHOT_PROPS]) {
621
- e[STORE_SNAPSHOT_PROPS] = Object.create(null);
622
- snapshotSources?.add(e);
623
- }
624
- if (!(r in e[STORE_SNAPSHOT_PROPS])) {
625
- e[STORE_SNAPSHOT_PROPS][r] = o;
626
- }
627
- }
628
- const i = e[STORE_OPTIMISTIC] && !projectionWriteActive;
629
- const O = i ? STORE_OPTIMISTIC_OVERRIDE : STORE_OVERRIDE;
630
- return {
631
- base: o,
632
- overrideKey: O,
633
- state: n
634
- };
635
- }
636
-
637
- /**
638
- * Registers the store for transition reversion. Called only once a write is
639
- * known to be effective — ineffective writes (same value, delete of an absent
640
- * property) are no-ops and must not entangle the store. Optimistic writes are
641
- * verdict-inert (question-scoped pending model): no mask is armed — the write
642
- * neither pends its own slot nor silences anyone else's.
643
- */ function armOptimisticStoreWrite(e, t) {
644
- // STORE_OPTIMISTIC is only set by createOptimisticStore, which installs the
645
- // optimistic engine before wrapping.
646
- if (e[STORE_OPTIMISTIC] && !projectionWriteActive) {
647
- GlobalQueue.Nn(t);
648
- }
649
- }
650
-
651
- /**
652
- * Records which transition owns an optimistic layer entry (#2899), so a
653
- * settling action only consumes its own keys — the layer is store-wide, but
654
- * concurrent actions writing disjoint keys must revert independently, exactly
655
- * like optimistic signal nodes do via the transition's _optimisticNodes.
656
- * `activeTransition` is the write's transaction (action() opens it before the
657
- * body runs); null marks an ambient write, which clears at plain flush end —
658
- * unless its flush's transition is blocked on the store's own in-flight truth
659
- * (pending firewall, #2951), in which case it rides that transaction to
660
- * settle. Same-key writes across actions keep last-write-wins layer
661
- * semantics.
662
- */ function stampOptimisticOwner(e, t, r) {
663
- if (t === STORE_OPTIMISTIC_OVERRIDE) (e[STORE_OPTIMISTIC_OWNERS] ??= Object.create(null))[r] = activeTransition;
664
- }
665
-
666
- function upsertStoreNode(e, t, r, n, o) {
667
- if (t[r]) return t[r];
668
- const i = isWrappable(n) ? wrap(n, e) : n;
669
- const O = getNode(e, t, r, i, isEqual, o);
670
- registerTransientStoreNode(O);
671
- return O;
672
- }
673
-
674
- function notifyStoreProperty(e, t, r, n, o, i) {
675
- // Cold writes upsert a transient pending node so untracked reads batch like signals.
676
- // Skip for projection writes (different commit semantics) and for optimistic stores
677
- // (whose whole purpose is immediate visibility via STORE_OPTIMISTIC_OVERRIDE).
678
- const O = projectionWriteActive || e[STORE_OPTIMISTIC];
679
- const s = r !== "delete";
680
- const E = e[STORE_HAS]?.[t];
681
- if (E) {
682
- setSignal(E, s);
683
- } else if (!O && r !== "invalidate" && i !== s) {
684
- const r = upsertStoreNode(e, getNodes(e, STORE_HAS), t, i);
685
- setSignal(r, s);
686
- }
687
- const S = getNodes(e, STORE_NODE);
688
- if (r === "set") {
689
- if (S[t]) {
690
- setSignal(S[t], () => isWrappable(n) ? wrap(n, e) : n);
691
- } else if (!O) {
692
- const r = upsertStoreNode(e, S, t, o, e[STORE_SNAPSHOT_PROPS]);
693
- setSignal(r, () => isWrappable(n) ? wrap(n, e) : n);
694
- }
695
- } else if (r === "invalidate") {
696
- if (S[t]) {
697
- setSignal(S[t], {});
698
- delete S[t];
699
- }
700
- } else {
701
- if (S[t]) {
702
- setSignal(S[t], undefined);
703
- } else if (!O) {
704
- const r = upsertStoreNode(e, S, t, o, e[STORE_SNAPSHOT_PROPS]);
705
- setSignal(r, undefined);
706
- }
707
- }
708
- notifySelf(e);
709
- }
710
-
711
- let Writing = null;
712
-
713
- /**
714
- * A derived store follows async memo rules (#2897 ruling): its seed is a
715
- * draft for the derive function, never an observable value, and an errored
716
- * derive is an error state, never a silent stale/seed serve. Until the
717
- * firewall first resolves there is nothing to read, so every consumer path
718
- * throws NotReady — tracked reads through their node (core read()), and the
719
- * untracked fall-throughs in the traps through this guard. Returning the
720
- * seed leaked it; returning `undefined` would break non-nullable types.
721
- * Callers exempt the firewall itself (the derive function works its own
722
- * draft while uninitialized).
723
- *
724
- * Error rail: a firewall carrying STATUS_ERROR throws its error for every
725
- * late reader — memo parity, where read()'s error branch does the same for
726
- * plain computeds. Rejection clears STATUS_UNINITIALIZED at commit, so
727
- * without this check late readers silently got the seed while settle-time
728
- * subscribers saw the error.
729
- *
730
- * Loading rail: the veto requires the firewall to still be in flight, not
731
- * just flagged: STATUS_UNINITIALIZED's clear is deferred to batch commit,
732
- * so during the settle flush a firewall that has already recomputed — and
733
- * reconciled real values into STORE_VALUE — still carries the stale flag.
734
- * STATUS_PENDING is the live bit (it clears eagerly at settle, mirroring
735
- * core read()'s verdict), so gating on it stops the guard from throwing a
736
- * fresh NotReadyError that nothing would ever sweep. #2944: mapArray's
737
- * keyed diff reads items inside its internal owner (untracked by design)
738
- * in exactly this window, and the stale throw wedged <For> permanently.
739
- */ function throwIfUnreadable(e) {
740
- const t = e[STORE_FIREWALL];
741
- if (!t) return;
742
- const r = t.S;
743
- if (r & STATUS_ERROR || r & STATUS_UNINITIALIZED && r & STATUS_PENDING) throw t._ ?? new NotReadyError(t);
744
- }
745
-
746
- const storeTraps = {
747
- get(e, t, r) {
748
- if (t === $TARGET) return e;
749
- if (t === $PROXY) return r;
750
- if (t === $REFRESH) return e[STORE_FIREWALL];
751
- if (pendingCheckActive) witnessAffectsMark(e, t);
752
- if (t === $TRACK) {
753
- trackSelf(e);
754
- return r;
755
- }
756
- // Hot path: an existing node on a plain target — no firewall (so neither
757
- // selfRead nor the uninitialized guard can apply), no override layers,
758
- // no write scope, and a raw (non-proxy) source. This is the shape of
759
- // every effect re-read of a settled store; it pays one node read and the
760
- // wrap check, nothing else. Any exotic bit falls through to the full
761
- // resolution below, and dev strictRead keeps its warning path.
762
- if (e[STORE_FIREWALL] === undefined && e[STORE_OVERRIDE] === undefined && e[STORE_OPTIMISTIC_OVERRIDE] === undefined && !writeOverride && (Writing === null || !Writing.has(r))) {
763
- const r = e[STORE_NODE];
764
- const n = r && r[t];
765
- if (n !== undefined && e[STORE_VALUE][$TARGET] === undefined) {
766
- // readNodeFast is read()'s plain-signal fast path hoisted over the
767
- // call; READ_SLOW means a global read window (latest/pending-check/
768
- // transition/lane/snapshot capture) or a node layer is active, and
769
- // only then does the full read() resolution have anything to do.
770
- let t = readNodeFast(n);
771
- if (t === READ_SLOW) t = read(n);
772
- if (t === $DELETED) t = undefined;
773
- // Every node-writing site wraps wrappables before setSignal (see the
774
- // dev assertion below), so re-wrapping on read is redundant — except
775
- // during snapshot capture, where read() can surface a raw captured
776
- // value seeded from snapshot props.
777
- if (!snapshotCaptureActive) {
778
- return t;
779
- }
780
- return isWrappable(t) ? wrap(t, e) : t;
781
- }
782
- }
783
- const n = getObserver() === e[STORE_FIREWALL];
784
- const o = getNodes(e, STORE_NODE);
785
- const i = n ? undefined : o[t];
786
- const O = e[STORE_VALUE];
787
- if (!i && !e[STORE_OVERRIDE] && !e[STORE_OPTIMISTIC_OVERRIDE] && !e[STORE_CUSTOM_PROTO] && !e[STORE_OPTIMISTIC] && !e[STORE_SNAPSHOT_PROPS] && !O[$TARGET] && !(t in O) && getObserver() && !n && !writeOnly(r)) {
788
- return read(getNode(e, o, t, undefined));
789
- }
790
- const s = getOverlayLayer(e, t);
791
- const E = !!s;
792
- const S = !!e[STORE_VALUE][$TARGET];
793
- const T = s ?? e[STORE_VALUE];
794
- if (!i) {
795
- const n = Object.getOwnPropertyDescriptor(T, t);
796
- if (n && n.get) return n.get.call(r);
797
- if (!n && !E && e[STORE_CUSTOM_PROTO]) {
798
- const e = unwrapStoreValue(T);
799
- if (hasInheritedAccessor(e, t)) {
800
- return Reflect.get(T, t, r);
801
- }
802
- }
803
- }
804
- if (writeOnly(r)) {
805
- if (isPrototypePollutionKey(t) && !hasOwnStoreProperty(e, t)) return undefined;
806
- let r = i && (E || !S) ? visibleNodeValue(i) : T[t];
807
- r === $DELETED && (r = undefined);
808
- if (!isWrappable(r)) return r;
809
- // Shallow boundary: records are replaced, never edited in place. Reads
810
- // inside a setter serve the raw so read-then-replace, filter/pop and
811
- // projection derives all work; in-place mutation of a raw is inert by
812
- // construction — the same contract as a markRaw child in a deep store.
813
- if (e[STORE_SHALLOW]) return r;
814
- const n = wrap(r, e);
815
- Writing?.add(n);
816
- return n;
817
- }
818
- let c = i ? E || !S ? read(o[t]) : (read(o[t]), T[t]) : T[t];
819
- c === $DELETED && (c = undefined);
820
- if (!i) {
821
- if (!E && typeof c === "function" && !Object.prototype.hasOwnProperty.call(T, t)) {
822
- let t;
823
- return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ? c.bind(T) : c;
824
- } else if (getObserver() && !n) {
825
- return read(getNode(e, o, t, isWrappable(c) ? wrap(c, e) : c, isEqual, e[STORE_SNAPSHOT_PROPS]));
826
- }
827
- }
828
- // Untracked fall-through (tracked reads already threw via their node in
829
- // read(); the dev strictRead error above wins first for memo parity).
830
- // Observer-present reads must NOT re-consult the flag here: during the
831
- // settle flush the firewall has recomputed (first values live on the
832
- // pending rail, served by read() above) but its UNINITIALIZED clear is
833
- // deferred to batch commit — vetoing read()'s verdict with the stale flag
834
- // threw a fresh NotReadyError for an already-settled source, which no
835
- // sweep would ever release (#2938: projection over an async store wedged
836
- // its Loading boundary on `undefined`).
837
- if (!n && !getObserver()) throwIfUnreadable(e);
838
- return isWrappable(c) ? wrap(c, e) : c;
839
- },
840
- has(e, t) {
841
- if (t === $PROXY || t === $TRACK || t === "__proto__") return true;
842
- if (pendingCheckActive) witnessAffectsMark(e, t);
843
- const r = getOverlayLayer(e, t);
844
- const n = r ? r[t] !== $DELETED : t in e[STORE_VALUE];
845
- if (writeOnly(e[$PROXY]) || getObserver() === e[STORE_FIREWALL]) return n;
846
- const o = getNodes(e, STORE_HAS);
847
- // If a has-node already exists, it carries the batched presence — `read()`
848
- // returns `_value` (committed) for untracked reads and the pending value for
849
- // downstream computes. This keeps `in` consistent with value reads.
850
- if (o[t]) return read(o[t]);
851
- // No node yet: `has` reflects committed presence (no pending write could change
852
- // it without first upserting a has-node at the write site). Create + read only
853
- // when tracking; leave untracked reads node-free.
854
- if (getObserver()) {
855
- return read(getNode(e, o, t, n));
856
- }
857
- throwIfUnreadable(e);
858
- return n;
859
- },
860
- set(e, t, r) {
861
- if (t === "__proto__") return true;
862
- const n = e[$PROXY];
863
- if (writeOnly(n)) {
864
- untrack(() => {
865
- const {base: o, overrideKey: i, state: O} = prepareStoreWrite(e, n, t);
866
- const s = getOverlayLayer(e, t);
867
- const E = s ? s[t] : o;
868
- const S = s ? s[t] !== $DELETED : t in e[STORE_VALUE];
869
- // Shallow slots hold store proxies verbatim (pass-through reference,
870
- // never raw-marked — see markRawOne/#2932); everything else unwraps
871
- // and marks as usual.
872
- const T = !!e[STORE_SHALLOW] && r?.[$TARGET] !== undefined;
873
- const c = T ? r : unwrapStoreValue(r);
874
- if (e[STORE_SHALLOW] && !T && isWrappable(c)) {
875
- // Flip the live gate too: a bare add was inert unless something else
876
- // had already marked a raw somewhere (wrap() checks rawValuesUsed
877
- // first), so the documented set-trap ingest mark silently no-oped in
878
- // apps whose only shallow data arrived through writes.
879
- rawValuesUsed = true;
880
- rawValues.add(c);
881
- }
882
- // Symbol-keyed writes on arrays are metadata, not index writes — never run
883
- // them through the numeric index/length machinery (`parseInt` on a symbol
884
- // throws). #2769
885
- const f = typeof t === "string" ? Number(t) : -1;
886
- const R = Array.isArray(O) && Number.isInteger(f) && f >= 0 && f < 4294967295 && String(f) === t;
887
- const u = R ? f + 1 : 0;
888
- const a = R && (getOverlayLayer(e, "length") ?? O).length;
889
- const l = R && u > a ? u : undefined;
890
- if (E === c && l === undefined) return true;
891
- armOptimisticStoreWrite(e, n);
892
- if (c !== undefined && c === o && l === undefined) {
893
- delete e[i]?.[t];
894
- if (i === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];
895
- } else {
896
- const r = e[i] || (e[i] = Object.create(null));
897
- r[t] = c;
898
- stampOptimisticOwner(e, i, t);
899
- if (l !== undefined) {
900
- r.length = l;
901
- stampOptimisticOwner(e, i, "length");
902
- }
903
- }
904
- notifyStoreProperty(e, t, "set", c, E, S);
905
- // Shrinking an array's length must remove the truncated indices, otherwise
906
- // they leak through `has`, `ownKeys`, and (tracked) index reads from the
907
- // underlying value. Mark each as deleted and notify so reactive reads update. #2768
908
- if (Array.isArray(O) && t === "length" && typeof c === "number" && typeof E === "number" && c < E) {
909
- const t = e[i] || (e[i] = Object.create(null));
910
- for (let r = c; r < E; r++) {
911
- if (t[r] === $DELETED) continue;
912
- const n = r in t ? t[r] : O[r];
913
- if (!(r in t) && !(r in O)) continue;
914
- t[r] = $DELETED;
915
- stampOptimisticOwner(e, i, r);
916
- notifyStoreProperty(e, r, "delete", undefined, n, true);
917
- }
918
- }
919
- // notify length change
920
- if (Array.isArray(O) && t !== "length" && l !== undefined) {
921
- const t = getNodes(e, STORE_NODE);
922
- if (t.length) {
923
- setSignal(t.length, l);
924
- } else if (!projectionWriteActive && !e[STORE_OPTIMISTIC]) {
925
- const r = upsertStoreNode(e, t, "length", a, e[STORE_SNAPSHOT_PROPS]);
926
- setSignal(r, l);
927
- }
928
- }
929
- if (false) ;
930
- });
931
- }
932
- return true;
933
- },
934
- defineProperty(e, t, r) {
935
- if (t === "__proto__") return true;
936
- const n = e[$PROXY];
937
- if (writeOnly(n)) {
938
- untrack(() => {
939
- const {base: o, overrideKey: i} = prepareStoreWrite(e, n, t);
940
- armOptimisticStoreWrite(e, n);
941
- const O = "value" in r ? {
942
- ...r,
943
- value: unwrapStoreValue(r.value)
944
- } : r;
945
- Object.defineProperty(e[i] || (e[i] = Object.create(null)), t, O);
946
- stampOptimisticOwner(e, i, t);
947
- notifyStoreProperty(e, t, "invalidate");
948
- if (false) ;
949
- });
950
- }
951
- return true;
952
- },
953
- deleteProperty(e, t) {
954
- if (t === "__proto__") return true;
955
- // Check both optimistic and regular override for existing $DELETED
956
- const r = e[STORE_OPTIMISTIC_OVERRIDE]?.[t] === $DELETED;
957
- const n = e[STORE_OVERRIDE]?.[t] === $DELETED;
958
- if (writeOnly(e[$PROXY]) && !r && !n) {
959
- untrack(() => {
960
- const r = e[STORE_OPTIMISTIC] && !projectionWriteActive;
961
- const n = r ? STORE_OPTIMISTIC_OVERRIDE : STORE_OVERRIDE;
962
- const o = getOverlayLayer(e, t);
963
- const i = o ? o[t] : e[STORE_VALUE][t];
964
- if (t in e[STORE_VALUE] || e[STORE_OVERRIDE] && t in e[STORE_OVERRIDE]) {
965
- armOptimisticStoreWrite(e, e[$PROXY]);
966
- (e[n] || (e[n] = Object.create(null)))[t] = $DELETED;
967
- stampOptimisticOwner(e, n, t);
968
- } else if (e[n] && t in e[n]) {
969
- armOptimisticStoreWrite(e, e[$PROXY]);
970
- delete e[n][t];
971
- if (n === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];
972
- } else return true;
973
- notifyStoreProperty(e, t, "delete", undefined, i, true);
974
- });
975
- }
976
- return true;
977
- },
978
- ownKeys(e) {
979
- if (pendingCheckActive) witnessAffectsMark(e);
980
- if (getObserver() !== e[STORE_FIREWALL]) {
981
- trackSelf(e);
982
- // trackSelf no-ops untracked, so enumeration of an unresolved derived
983
- // store would otherwise leak the seed's structure (#2897). The write
984
- // path is exempt (like the get/has traps' writeOnly early returns):
985
- // the first landing's reconcile enumerates the store while
986
- // STATUS_UNINITIALIZED is still set — it IS the initialization.
987
- if (!getObserver() && !writeOnly(e[$PROXY])) throwIfUnreadable(e);
988
- }
989
- // Merge optimistic override with regular override for key enumeration
990
- let t = getKeys(e[STORE_VALUE], e[STORE_OVERRIDE], false);
991
- if (e[STORE_OPTIMISTIC_OVERRIDE]) {
992
- const r = new Set(t);
993
- for (const t of Reflect.ownKeys(e[STORE_OPTIMISTIC_OVERRIDE])) {
994
- if (e[STORE_OPTIMISTIC_OVERRIDE][t] !== $DELETED) r.add(t); else r.delete(t);
995
- }
996
- t = Array.from(r);
997
- }
998
- return t;
999
- },
1000
- getOwnPropertyDescriptor(e, t) {
1001
- if (t === $PROXY) return {
1002
- value: e[$PROXY],
1003
- writable: true,
1004
- configurable: true
1005
- };
1006
- // Check optimistic override first, but use base descriptor structure for compatibility
1007
- if (e[STORE_OPTIMISTIC_OVERRIDE] && t in e[STORE_OPTIMISTIC_OVERRIDE]) {
1008
- if (e[STORE_OPTIMISTIC_OVERRIDE][t] === $DELETED) return undefined;
1009
- const r = Reflect.getOwnPropertyDescriptor(e[STORE_OPTIMISTIC_OVERRIDE], t);
1010
- if (r?.get || r?.set || !(t in e[STORE_VALUE])) return r;
1011
- // Get base descriptor structure, override just the value
1012
- const n = getPropertyDescriptor(e[STORE_VALUE], e[STORE_OVERRIDE], t);
1013
- if (n) {
1014
- const r = Reflect.getOwnPropertyDescriptor(e, t);
1015
- const o = !r || r.configurable ? true : n.configurable;
1016
- return {
1017
- ...n,
1018
- configurable: o,
1019
- value: e[STORE_OPTIMISTIC_OVERRIDE][t]
1020
- };
1021
- }
1022
- return {
1023
- value: e[STORE_OPTIMISTIC_OVERRIDE][t],
1024
- writable: true,
1025
- enumerable: true,
1026
- configurable: true
1027
- };
1028
- }
1029
- const r = getPropertyDescriptor(e[STORE_VALUE], e[STORE_OVERRIDE], t);
1030
- // The proxy target is an internal node object, not the original source. When the
1031
- // source has a non-configurable property that does not also exist as non-configurable
1032
- // on the proxy target, the proxy invariant is violated: the engine requires that a
1033
- // property reported as non-configurable must actually be non-configurable on the
1034
- // target object. Override configurable to true only in that case.
1035
- if (r && !r.configurable) {
1036
- const n = Reflect.getOwnPropertyDescriptor(e, t);
1037
- if (!n || n.configurable) return {
1038
- ...r,
1039
- configurable: true
1040
- };
1041
- }
1042
- return r;
1043
- },
1044
- getPrototypeOf(e) {
1045
- return Object.getPrototypeOf(e[STORE_VALUE]);
1046
- }
1047
- };
1048
-
1049
- function storeSetter(e, t) {
1050
- const r = Writing;
1051
- Writing = new Set;
1052
- Writing.add(e);
1053
- try {
1054
- const r = t(e);
1055
- if (r !== e && r !== undefined) {
1056
- if (Array.isArray(r)) {
1057
- for (let t = 0, n = r.length; t < n; t++) e[t] = r[t];
1058
- e.length = r.length;
1059
- } else {
1060
- const t = new Set([ ...ownEnumerableKeys(e), ...ownEnumerableKeys(r) ]);
1061
- t.forEach(t => {
1062
- if (t in r) e[t] = r[t]; else delete e[t];
1063
- });
1064
- }
1065
- }
1066
- } finally {
1067
- Writing.clear();
1068
- Writing = r;
1069
- }
1070
- }
1071
-
1072
- function createStore(e, t, r) {
1073
- const n = typeof e === "function", o = n ? createProjectionInternal(e, t, r).store : t?.shallow ? wrapShallow(e) : wrap(e);
1074
- return [ o, n ? e => {
1075
- // Mark the projection as manually written before notifying property nodes.
1076
- suppressComputedRecompute(o[$REFRESH]);
1077
- storeSetter(o, e);
1078
- } : e => storeSetter(o, e) ];
320
+ r.scope.add(e[STORE_VALUE]);
321
+ if (e.pb) r.scope.add(e.pb);
322
+ return [ o ];
1079
323
  }
1080
324
 
1081
- export { $AFFECTS, $DELETED, $PROXY, $TARGET, $TRACK, STORE_CUSTOM_PROTO, STORE_DESC, STORE_FIREWALL, STORE_HAS, STORE_LOOKUP, STORE_NODE, STORE_OPTIMISTIC, STORE_OPTIMISTIC_OVERRIDE, STORE_OPTIMISTIC_OWNERS, STORE_OVERRIDE, STORE_PARENT, STORE_SHALLOW, STORE_VALUE, STORE_WRAP, createStore, createStoreProxy, getKeys, getOverlayLayer, getPropertyDescriptor, getStoreAffectsNodes, getStoreKeys, getStoreSymbols, isRawValue, isWrappable, lookupTarget, markRawIngest, mergedOverlay, notifySelf, ownEnumerableKeys, rawValuesUsed, setWriteOverride, storeLookup, storeSetter, storeTraps, symbolKeyedRecords, trackSelf, visibleNodeValue, witnessAffectsMark, wrap, wrapShallow };
325
+ export { $AFFECTS, $PROXY, $TARGET, $TRACK, STORE_HAS, STORE_NODE, STORE_VALUE, affectsScopesLive, getStoreAffectsNodes, getWriteOverride, inheritAffectsMarks, isRawValue, isWrappable, markRawIngest, markRawOne, nextAffectsNodeResolver, nextOptimisticViewResolver, ownEnumerableKeys, rawValuesUsed, setNextAffectsNodeResolver, setNextOptimisticViewResolver, setWriteOverride, witnessAffectsMark };