@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.18

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 (40) hide show
  1. package/dist/dev-shared.js +296 -191
  2. package/dist/dev.attribution.js +16 -16
  3. package/dist/dev.js +527 -311
  4. package/dist/observe/core/async.js +148 -115
  5. package/dist/observe/core/constants.js +3 -1
  6. package/dist/observe/core/core.js +27 -27
  7. package/dist/observe/core/effect.js +29 -32
  8. package/dist/observe/core/graph.js +5 -5
  9. package/dist/observe/core/owner.js +7 -7
  10. package/dist/observe/core/scheduler.js +23 -12
  11. package/dist/observe/core/verdict.js +2 -2
  12. package/dist/observe/deferred.js +16 -13
  13. package/dist/observe/from.js +248 -0
  14. package/dist/observe/index.js +2 -0
  15. package/dist/observe/store/reconcile.js +137 -121
  16. package/dist/observe/store/store.js +595 -618
  17. package/dist/observe/store/target.js +1 -5
  18. package/dist/prod/core/async.js +209 -176
  19. package/dist/prod/core/constants.js +3 -1
  20. package/dist/prod/core/core.js +33 -33
  21. package/dist/prod/core/effect.js +24 -27
  22. package/dist/prod/core/graph.js +2 -2
  23. package/dist/prod/core/lanes.js +14 -14
  24. package/dist/prod/core/scheduler.js +35 -24
  25. package/dist/prod/core/truth.js +4 -4
  26. package/dist/prod/core/verdict.js +7 -7
  27. package/dist/prod/deferred.js +17 -14
  28. package/dist/prod/from.js +248 -0
  29. package/dist/prod/index.js +2 -0
  30. package/dist/prod/store/optimistic.js +1 -1
  31. package/dist/prod/store/reconcile.js +137 -121
  32. package/dist/prod/store/store.js +94 -117
  33. package/dist/prod/store/target.js +13 -17
  34. package/dist/types/core/async.d.ts +12 -2
  35. package/dist/types/core/constants.d.ts +5 -0
  36. package/dist/types/core/core.d.ts +19 -3
  37. package/dist/types/core/scheduler.d.ts +15 -3
  38. package/dist/types/from.d.ts +47 -0
  39. package/dist/types/index.d.ts +1 -0
  40. package/package.json +1 -1
package/dist/dev.js CHANGED
@@ -64,123 +64,128 @@ import {
64
64
  a8 as link,
65
65
  a9 as pullComputed,
66
66
  aa as strictRead,
67
- ab as warnStrictReadUntracked,
68
- ac as staleReader,
69
- ad as globalQueue,
70
- ae as joinPass,
71
- af as markLateLinker,
72
- ag as spectating,
73
- ah as unflushedValue,
74
- ai as CONFIG_STAGED_ERROR,
75
- aj as stagedReaders,
76
- ak as bornInto,
77
- al as enterAction,
78
- am as setEffectCallback,
79
- an as exitAction,
80
- ao as isThenable,
81
- ap as actionDepth,
82
- aq as flush,
83
- ar as setQuestion,
84
- as as emitDiagnostic,
85
- at as enterCallback,
86
- au as exitCallback,
87
- av as getOwner,
88
- aw as inEffectCallback,
89
- ax as setExternalHooks,
90
- ay as signal,
91
- az as setSignal,
92
- aA as cleanup,
93
- aB as read,
94
- aC as DEV$1,
95
- aD as CONFIG_TRANSPARENT,
96
- aE as NoOwnerError,
97
- aF as ContextNotFoundError,
98
- aG as createEffectNode,
99
- aH as recompute,
100
- aI as EFFECT_USER,
101
- aJ as reportDiagnostic,
102
- aK as computed,
103
- aL as staleValues,
104
- aM as CONFIG_AUTO_DISPOSE,
105
- aN as EFFECT_TRACKED,
106
- aO as heldRunHook,
107
- aP as unwrapStatusError,
108
- aQ as haltReactivityAt,
109
- aR as setStrictRead,
110
- aS as StatusError,
111
- aT as trimStaleDeps,
112
- aU as setTrackedQueueCallback,
113
- aV as setEffectStatusNotify,
114
- aW as _hitUnhandledAsync,
115
- aX as resetUnhandledAsync,
116
- aY as ROOT_ERROR_HOOK,
117
- aZ as registerGraph,
118
- a_ as runWithOwner,
119
- a$ as setMemo,
120
- b0 as $REFRESH,
121
- b1 as markRefresh,
122
- b2 as createRoot,
123
- b3 as getObserver,
124
- b4 as dispose,
125
- b5 as TimeoutError,
126
- b6 as untrack,
127
- b7 as dirtyQueue,
128
- b8 as CONFIG_REDERIVE,
129
- b9 as passTx,
130
- ba as CONFIG_FRESH_READ,
131
- bb as createOwner,
132
- bc as writeHook,
133
- bd as REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE,
134
- be as ownedScopeWriteMessage,
135
- bf as isEqual,
136
- bg as CONFIG_MANUAL_WRITE,
137
- bh as snapshotCaptureActive,
138
- bi as captureWriteSnapshot,
139
- bj as notePromotedWrite,
140
- bk as slotSignal,
141
- bl as stagedScreen,
142
- bm as CONFIG_PROMOTED,
143
- bn as setSlotUnobserved,
144
- bo as installStoreHooks,
145
- bp as joinPassTx,
146
- bq as asyncTailFlights,
147
- br as untrackDepth,
148
- bs as checkPostAwaitRead,
149
- bt as disposalDepth,
150
- bu as insertIntoHeap,
151
- bv as isDisposed,
152
- bw as handleAsync,
153
- bx as addPendingSource,
154
- by as propagateStatus,
155
- bz as forEachDependent,
156
- bA as settlePendingSource,
157
- bB as joinPassAt,
158
- bC as notifyEpoch,
159
- bD as SUPPORTS_PROXY,
160
- bE as releaseMarksHook,
161
- bF as installMarkHooks,
162
- bG as CONFIG_LAGS,
163
- bH as REACTIVE_REASK,
164
- bI as haltReactivity,
165
- bJ as heldUnder,
166
- bK as unhold,
167
- bL as laneStageHook,
168
- bM as REACTIVE_ZOMBIE,
169
- bN as installBoundaryHooks,
170
- bO as CAUGHT_REARMED,
171
- bP as OBSERVE$1
67
+ ab as strictReadActive,
68
+ ac as warnStrictReadUntracked,
69
+ ad as staleReader,
70
+ ae as globalQueue,
71
+ af as joinPass,
72
+ ag as markLateLinker,
73
+ ah as spectating,
74
+ ai as unflushedValue,
75
+ aj as CONFIG_STAGED_ERROR,
76
+ ak as stagedReaders,
77
+ al as bornInto,
78
+ am as enterAction,
79
+ an as setEffectCallback,
80
+ ao as exitAction,
81
+ ap as isThenable,
82
+ aq as actionDepth,
83
+ ar as flush,
84
+ as as setQuestion,
85
+ at as emitDiagnostic,
86
+ au as enterCallback,
87
+ av as exitCallback,
88
+ aw as getOwner,
89
+ ax as inEffectCallback,
90
+ ay as effectCallbackEvent,
91
+ az as setExternalHooks,
92
+ aA as signal,
93
+ aB as setSignal,
94
+ aC as cleanup,
95
+ aD as read,
96
+ aE as DEV$1,
97
+ aF as CONFIG_TRANSPARENT,
98
+ aG as NoOwnerError,
99
+ aH as ContextNotFoundError,
100
+ aI as createEffectNode,
101
+ aJ as recompute,
102
+ aK as EFFECT_USER,
103
+ aL as reportDiagnostic,
104
+ aM as computed,
105
+ aN as staleValues,
106
+ aO as CONFIG_AUTO_DISPOSE,
107
+ aP as EFFECT_TRACKED,
108
+ aQ as heldRunHook,
109
+ aR as unwrapStatusError,
110
+ aS as setStrictRead,
111
+ aT as queueEvent,
112
+ aU as StatusError,
113
+ aV as trimStaleDeps,
114
+ aW as haltReactivityAt,
115
+ aX as strictReadEvent,
116
+ aY as setTrackedQueueCallback,
117
+ aZ as setEffectStatusNotify,
118
+ a_ as _hitUnhandledAsync,
119
+ a$ as resetUnhandledAsync,
120
+ b0 as ROOT_ERROR_HOOK,
121
+ b1 as registerGraph,
122
+ b2 as runWithOwner,
123
+ b3 as setMemo,
124
+ b4 as $REFRESH,
125
+ b5 as markRefresh,
126
+ b6 as createRoot,
127
+ b7 as getObserver,
128
+ b8 as dispose,
129
+ b9 as TimeoutError,
130
+ ba as untrack,
131
+ bb as dirtyQueue,
132
+ bc as CONFIG_REDERIVE,
133
+ bd as passTx,
134
+ be as CONFIG_FRESH_READ,
135
+ bf as createOwner,
136
+ bg as writeHook,
137
+ bh as REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE,
138
+ bi as ownedScopeWriteMessage,
139
+ bj as isEqual,
140
+ bk as CONFIG_MANUAL_WRITE,
141
+ bl as snapshotCaptureActive,
142
+ bm as captureWriteSnapshot,
143
+ bn as notePromotedWrite,
144
+ bo as slotSignal,
145
+ bp as stagedScreen,
146
+ bq as CONFIG_PROMOTED,
147
+ br as setSlotUnobserved,
148
+ bs as installStoreHooks,
149
+ bt as joinPassTx,
150
+ bu as asyncTailFlights,
151
+ bv as untrackDepth,
152
+ bw as checkPostAwaitRead,
153
+ bx as disposalDepth,
154
+ by as insertIntoHeap,
155
+ bz as isDisposed,
156
+ bA as handleAsync,
157
+ bB as addPendingSource,
158
+ bC as propagateStatus,
159
+ bD as forEachDependent,
160
+ bE as settlePendingSource,
161
+ bF as joinPassAt,
162
+ bG as notifyEpoch,
163
+ bH as SUPPORTS_PROXY,
164
+ bI as releaseMarksHook,
165
+ bJ as installMarkHooks,
166
+ bK as CONFIG_LAGS,
167
+ bL as REACTIVE_REASK,
168
+ bM as onSupersede,
169
+ bN as haltReactivity,
170
+ bO as heldUnder,
171
+ bP as unhold,
172
+ bQ as laneStageHook,
173
+ bR as REACTIVE_ZOMBIE,
174
+ bS as installBoundaryHooks,
175
+ bT as CAUGHT_REARMED,
176
+ bU as OBSERVE$1
172
177
  } from "./dev-shared.js";
173
178
  export {
174
- bQ as clearSnapshots,
175
- bR as enforceLoadingBoundary,
176
- bS as getNextChildId,
177
- bT as markSnapshotScope,
178
- bU as peekNextChildId,
179
- bV as releaseSnapshotScope,
180
- bW as resetErrorHalt,
181
- bX as setConsoleFooter,
182
- bY as setSnapshotCapture,
183
- bZ as setWriteHook
179
+ bV as clearSnapshots,
180
+ bW as enforceLoadingBoundary,
181
+ bX as getNextChildId,
182
+ bY as markSnapshotScope,
183
+ bZ as peekNextChildId,
184
+ b_ as releaseSnapshotScope,
185
+ b$ as resetErrorHalt,
186
+ c0 as setConsoleFooter,
187
+ c1 as setSnapshotCapture,
188
+ c2 as setWriteHook
184
189
  } from "./dev-shared.js";
185
190
 
186
191
  // Truth reads (`until`, `refresh`; CONFIG_AUTHORITATIVE) over lane nodes.
@@ -1350,7 +1355,7 @@ function verdictValue(el, c) {
1350
1355
  // verdict read as on any untracked read; the pending throw they add for a
1351
1356
  // plain read does not apply — a verdict read is a sanctioned read of a
1352
1357
  // pending node (an uninitialized one still throws NotReady below).
1353
- if (strictRead)
1358
+ if (strictRead && strictReadActive())
1354
1359
  warnStrictReadUntracked(strictRead, {
1355
1360
  ownerId: c?.id,
1356
1361
  ownerName: c?._name,
@@ -1583,21 +1588,23 @@ function action(genFn) {
1583
1588
  // it may have been called from — a root created in it (`until`,
1584
1589
  // `resolve`) is deliberate (PRIMITIVE_IN_EFFECT_CALLBACK is off).
1585
1590
  let effectCallback = false;
1591
+ let effectEvent;
1586
1592
  {
1587
1593
  enterCallback();
1588
1594
  effectCallback = inEffectCallback;
1595
+ effectEvent = effectCallbackEvent;
1589
1596
  setEffectCallback(false);
1590
1597
  }
1591
1598
  try {
1592
1599
  r = err ? it.throw(v) : it.next(v);
1593
1600
  } catch (e) {
1594
1601
  exitAction();
1595
- (exitCallback(), setEffectCallback(effectCallback));
1602
+ (exitCallback(), setEffectCallback(effectCallback, effectEvent));
1596
1603
  if (attrHooks !== null) attrHooks.actionStepEnd(it);
1597
1604
  return done(undefined, e, true);
1598
1605
  }
1599
1606
  exitAction();
1600
- (exitCallback(), setEffectCallback(effectCallback));
1607
+ (exitCallback(), setEffectCallback(effectCallback, effectEvent));
1601
1608
  if (attrHooks !== null) attrHooks.actionStepEnd(it);
1602
1609
  // A rejected iterator result (async generators) means the error already
1603
1610
  // escaped the generator body — it is completed, and throwing back in
@@ -1887,10 +1894,9 @@ function effect(compute, effect, error, options) {
1887
1894
  );
1888
1895
  }
1889
1896
  }
1890
- function notifyEffectStatus(status, error) {
1891
- // Use passed values if provided, otherwise read from node
1892
- const actualStatus = status !== undefined ? status : this._statusFlags;
1893
- const actualError = error !== undefined ? error : this._x?._error;
1897
+ function notifyEffectStatus() {
1898
+ const actualStatus = this._statusFlags;
1899
+ const actualError = this._x?._error;
1894
1900
  if (actualStatus & STATUS_ERROR) {
1895
1901
  globalQueue.notify(this, STATUS_PENDING, 0);
1896
1902
  if (this._type === EFFECT_USER) {
@@ -1898,24 +1904,22 @@ function notifyEffectStatus(status, error) {
1898
1904
  // queue it like the effect function. It runs in the same imperative,
1899
1905
  // writable scope and throws escalate the same way. No payload is queued — the node already carries `_statusFlags`/`_error`,
1900
1906
  // and the runner dispatches on them, so a recovery before the effect
1901
- // phase takes the success arm instead. Blocked forwards (explicit
1902
- // `status` arg without node-state writes) don't queue: the status
1903
- // re-propagates unblocked at commit.
1907
+ // phase takes the success arm instead. (Re-read: the boundary
1908
+ // notify above can run a redraw.)
1904
1909
  if (this._statusFlags & STATUS_ERROR) {
1905
1910
  this._modified = true;
1906
1911
  globalQueue.enqueue(this._type, (this._boundRunEffect ??= runEffect.bind(null, this)));
1907
1912
  }
1908
1913
  return;
1909
1914
  }
1910
- if (!globalQueue.notify(this, STATUS_ERROR, STATUS_ERROR)) {
1911
- // Dev locates the halt at the node that threw: the StatusError's
1912
- // source, upstream of this effect when a computed failed.
1913
- haltReactivityAt(
1914
- unwrapStatusError(actualError),
1915
- actualError instanceof StatusError ? actualError.source : this
1916
- );
1917
- throw actualError;
1918
- }
1915
+ // Dev locates the halt at the node that threw: the StatusError's
1916
+ // source, upstream of this effect when a computed failed.
1917
+ escalate(
1918
+ this,
1919
+ unwrapStatusError(actualError),
1920
+ actualError,
1921
+ actualError instanceof StatusError ? actualError.source : this
1922
+ );
1919
1923
  } else if (this._type === EFFECT_RENDER) {
1920
1924
  globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
1921
1925
  if (_hitUnhandledAsync && resetUnhandledAsync()) {
@@ -1944,6 +1948,14 @@ function notifyEffectStatus(status, error) {
1944
1948
  }
1945
1949
  }
1946
1950
  }
1951
+ /** An effect's error no boundary takes halts the system (boundary-or-halt):
1952
+ * halted with `cause`, `thrown` rethrown; dev locates it at `subject`. */
1953
+ function escalate(node, cause, thrown = cause, subject = node) {
1954
+ if (!globalQueue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
1955
+ haltReactivityAt(cause, subject);
1956
+ throw thrown;
1957
+ }
1958
+ }
1947
1959
  function runEffect(node, type) {
1948
1960
  if (!node._modified || node._flags & REACTIVE_DISPOSED) return;
1949
1961
  // A queued run behind a fallback (boundaries.ts) waits for the reveal: a
@@ -1978,10 +1990,7 @@ function runEffect(node, type) {
1978
1990
  })
1979
1991
  : console.error(err);
1980
1992
  } catch (error) {
1981
- if (!globalQueue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
1982
- haltReactivityAt(error, node);
1983
- throw error;
1984
- }
1993
+ escalate(node, error);
1985
1994
  }
1986
1995
  return;
1987
1996
  }
@@ -1989,9 +1998,13 @@ function runEffect(node, type) {
1989
1998
  // the compute pass that produced `_value` was clean, so its tail still goes.
1990
1999
  const cleanPass = node._x?._error == null;
1991
2000
  let prevStrictRead = false;
2001
+ let prevStrictReadEvent;
1992
2002
  {
1993
- prevStrictRead = setStrictRead("an effect callback");
1994
- setEffectCallback(true);
2003
+ prevStrictReadEvent = strictReadEvent;
2004
+ // A run outside a queue (a render effect's first run at creation) records
2005
+ // no event: its callback is the compiler's DOM writes.
2006
+ prevStrictRead = setStrictRead("an effect callback", queueEvent);
2007
+ setEffectCallback(true, queueEvent);
1995
2008
  enterCallback();
1996
2009
  }
1997
2010
  // Observe tier, like its `effectRunEnd` twin below: the frame the engine
@@ -2014,13 +2027,10 @@ function runEffect(node, type) {
2014
2027
  } catch (error) {
2015
2028
  ext(node)._error = new StatusError(node, error);
2016
2029
  node._statusFlags |= STATUS_ERROR;
2017
- if (!globalQueue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
2018
- haltReactivityAt(error, node);
2019
- throw error;
2020
- }
2030
+ escalate(node, error);
2021
2031
  } finally {
2022
2032
  {
2023
- setStrictRead(prevStrictRead);
2033
+ setStrictRead(prevStrictRead, prevStrictReadEvent);
2024
2034
  setEffectCallback(false);
2025
2035
  exitCallback();
2026
2036
  }
@@ -2210,9 +2220,6 @@ function lookupTarget(raw, fam) {
2210
2220
  const owner = raw[$OWNER];
2211
2221
  return owner !== undefined && owner.fam === fam ? owner : (fam?.map ?? storeLookup).get(raw);
2212
2222
  }
2213
- function devAssertNeverUserMutation(target) {
2214
- return;
2215
- }
2216
2223
  /** Sticky descendants flag up the parent chain (§6d): adoption descends
2217
2224
  * into a changed child pair only where a target with nodes exists below. */
2218
2225
  function markDescendants(target) {
@@ -3835,7 +3842,7 @@ function wrap(value, parent = null, parentKey = null, fam = parent?.fam ?? null)
3835
3842
  const existing = lookupTarget(value, fam);
3836
3843
  if (existing !== undefined) return existing.px;
3837
3844
  const t = value[$TARGET];
3838
- if (t !== undefined && t.px === value) {
3845
+ if (t?.px === value) {
3839
3846
  // Foreign-family proxies re-wrap into THIS family (writes stay isolated);
3840
3847
  // same-family and plain-store proxies pass through.
3841
3848
  if (fam === null || t.fam === fam) return value;
@@ -3907,7 +3914,7 @@ function asBacking(v) {
3907
3914
  * pending backing is never "equal" to the committed one); a lane value is
3908
3915
  * an arrangement (`LaneView`), judged by the optimistic machinery. */
3909
3916
  const containerEquals = function (a, b) {
3910
- return a === b || (optHooks !== null && optHooks.arrangement(this._host, a, b));
3917
+ return a === b || !!optHooks?.arrangement(this._host, a, b);
3911
3918
  };
3912
3919
  function sameLogicalSlot(target, a, b) {
3913
3920
  if (a === null || typeof a !== "object" || b === null || typeof b !== "object") return false;
@@ -3924,10 +3931,10 @@ function releaseSlot(node) {
3924
3931
  if (t.k === node) t.k = null;
3925
3932
  } else if (key === $DEEP) {
3926
3933
  if (t.dk === node) t.dk = null;
3927
- } else if (t.n !== null && t.n[key] === node) {
3934
+ } else if (t.n?.[key] === node) {
3928
3935
  delete t.n[key];
3929
3936
  t.nc--;
3930
- } else if (t.h !== null && t.h[key] === node) delete t.h[key];
3937
+ } else if (t.h?.[key] === node) delete t.h[key];
3931
3938
  // The family's index keeps only targets that still carry a node.
3932
3939
  if (
3933
3940
  t.fam !== null &&
@@ -4008,12 +4015,7 @@ function getNode(
4008
4015
  accKnown === -1 ? isOwnAccessor(pb ?? target.v, key) : accKnown === 1
4009
4016
  ));
4010
4017
  if (pb !== null && !created.acc)
4011
- bornStaged(
4012
- target,
4013
- created,
4014
- target.del !== null && target.del.has(key) ? undefined : pb[key],
4015
- key
4016
- );
4018
+ bornStaged(target, created, target.del?.has(key) ? undefined : pb[key], key);
4017
4019
  // Attribution-only: name store property nodes by path segment so
4018
4020
  // attribution chains and wide-scope warnings read "store.todos" (or
4019
4021
  // "todos.title" when the store was declared with a name), not
@@ -4038,8 +4040,7 @@ function getHasNode(target, key) {
4038
4040
  if (node === undefined) {
4039
4041
  const created = (node = slotSignal(key in committed(target), isEqual, target, key, false));
4040
4042
  const pb = target.pb;
4041
- if (pb !== null)
4042
- bornStaged(target, created, key in pb && !(target.del !== null && target.del.has(key)), key);
4043
+ if (pb !== null) bornStaged(target, created, key in pb && !target.del?.has(key), key);
4043
4044
  if (attrHooks !== null) stampNodeOwner(created, target);
4044
4045
  nodes[key] = node;
4045
4046
  noteNode(target, created, key);
@@ -4213,7 +4214,7 @@ function scanAccessorsOnce(target) {
4213
4214
  const keys = Reflect.ownKeys(src);
4214
4215
  let plain = Object.getPrototypeOf(src) === Object.prototype;
4215
4216
  for (const key of keys) {
4216
- if (lookupGetter.call(src, key) !== undefined || lookupSetter.call(src, key) !== undefined) {
4217
+ if (isOwnAccessor(src, key)) {
4217
4218
  target.a = true;
4218
4219
  plain = false;
4219
4220
  break;
@@ -4238,7 +4239,7 @@ const plainProto = o => {
4238
4239
  * container at flatten so its shape stays fast (#3689). */
4239
4240
  function overlayRebuilds(t, pb) {
4240
4241
  if (t.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
4241
- if (t.del !== null && t.del.size !== 0) return true;
4242
+ if (t.del?.size) return true;
4242
4243
  const wk = t.wk;
4243
4244
  if (wk !== null && wk !== WK_ALL && wk.u === undefined && wk.size <= OVERLAY_REBUILD_MIN_ADDS)
4244
4245
  return false;
@@ -4252,18 +4253,14 @@ function overlayRebuilds(t, pb) {
4252
4253
  function materializePB(target) {
4253
4254
  const pb = target.pb;
4254
4255
  if (!target.ovl) return pb;
4255
- const clone = target.sc === 2 ? wideClone(target.v, target) : cloneRaw(target.v, target);
4256
- if (target.del !== null) {
4257
- for (const key of target.del) delete clone[key];
4258
- target.kc -= target.del.size;
4259
- target.del = null;
4260
- }
4261
- for (const key of Reflect.ownKeys(pb))
4262
- target.sc === 2 ? (clone[key] = pb[key]) : copyOwn(clone, pb, key);
4256
+ const clone = overlayOnto(
4257
+ target,
4258
+ target.sc === 2 ? wideClone(target.v, target) : cloneRaw(target.v, target),
4259
+ pb
4260
+ );
4263
4261
  target.pb = clone;
4264
- target.ovl = false;
4265
4262
  // The staging IS the pending backing: re-point the container node.
4266
- if (target.k !== null && target.k._pendingValue === pb) target.k._pendingValue = clone;
4263
+ if (target.k?._pendingValue === pb) target.k._pendingValue = clone;
4267
4264
  return clone;
4268
4265
  }
4269
4266
  /** The batch's pending backing, created on the first draft write: an overlay
@@ -4315,7 +4312,6 @@ function privatizeCommitted(target) {
4315
4312
  target.ch = false;
4316
4313
  if (target.u) {
4317
4314
  privatizeCommitted(target.u);
4318
- devAssertNeverUserMutation(target.u.v);
4319
4315
  const pv = target.u.v;
4320
4316
  const slot = parentSlotKey(target, before);
4321
4317
  if (pv[slot] === before) pv[slot] = target.v;
@@ -4339,16 +4335,19 @@ function parentSlotKey(target, expected) {
4339
4335
  * place — the backing keeps its identity. */
4340
4336
  function flattenOverlay(t, pb) {
4341
4337
  privatizeCommitted(t);
4342
- const v = t.v;
4343
- for (const key of Reflect.ownKeys(pb)) t.sc === 2 ? (v[key] = pb[key]) : copyOwn(v, pb, key);
4338
+ overlayOnto(t, t.v, pb); // the caller (`foldTarget`) closes the staging
4339
+ }
4340
+ /** An overlay's own keys and deletions onto `to` (the two are disjoint: a
4341
+ * write un-deletes its key); the overlay is closed. */
4342
+ function overlayOnto(t, to, pb) {
4343
+ for (const key of Reflect.ownKeys(pb)) t.sc === 2 ? (to[key] = pb[key]) : copyOwn(to, pb, key);
4344
4344
  if (t.del !== null) {
4345
- for (const key of t.del) delete v[key];
4345
+ for (const key of t.del) delete to[key];
4346
4346
  t.kc -= t.del.size;
4347
4347
  t.del = null;
4348
4348
  }
4349
- t.pb = null;
4350
4349
  t.ovl = false;
4351
- t.wk = null; // written-keys window closes with the commit
4350
+ return to;
4352
4351
  }
4353
4352
  /** Adoption (a setter's returned replacement; reconcile and projection
4354
4353
  * landings in S3): the incoming object becomes the backing by reference at
@@ -4387,7 +4386,7 @@ function adoptPB(target, incoming, notify = true) {
4387
4386
  target.a = false;
4388
4387
  target.wk = null; // adoption supersedes staged trap writes
4389
4388
  const owner = incoming[$OWNER];
4390
- if (owner !== undefined && owner.fam === target.fam) incoming[$OWNER] = target;
4389
+ if (owner?.fam === target.fam) incoming[$OWNER] = target;
4391
4390
  else setLookup(incoming, target);
4392
4391
  if (notify && base !== incoming) notifyFold(target, base, incoming);
4393
4392
  }
@@ -4484,7 +4483,7 @@ function drainFolds() {
4484
4483
  * landing) read it. */
4485
4484
  GlobalQueue._slotFlight = n => {
4486
4485
  const fam = n._host?.fam;
4487
- return fam != null && fam.node !== null && (fam.node._statusFlags & STATUS_PENDING) !== 0;
4486
+ return (fam?.node?._statusFlags & STATUS_PENDING) !== 0;
4488
4487
  };
4489
4488
  /** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
4490
4489
  * A chained link's committed value is the inner store's at the last
@@ -4495,8 +4494,8 @@ GlobalQueue._slotCovered = n => {
4495
4494
  if (t.ch) {
4496
4495
  const key = n._key;
4497
4496
  const inner = t.v;
4498
- if (t.h !== null && t.h[key] === n) n._value = untrack(() => key in inner);
4499
- else if (t.n !== null && t.n[key] === n) n._value = untrack(() => unwrapValue(inner[key]));
4497
+ if (t.h?.[key] === n) n._value = untrack(() => key in inner);
4498
+ else if (t.n?.[key] === n) n._value = untrack(() => unwrapValue(inner[key]));
4500
4499
  }
4501
4500
  return n._value;
4502
4501
  };
@@ -4529,12 +4528,8 @@ function foldTarget(t, old) {
4529
4528
  const k = t.k;
4530
4529
  const pb = t.pb;
4531
4530
  if (pb !== null) {
4532
- if (pb === t.v) {
4533
- // An eager adoption nothing wrote after: the backing is already
4534
- // the adopted object (shared ownership — never cloned).
4535
- t.pb = null;
4536
- t.wk = null;
4537
- } else if (
4531
+ if (pb === t.v);
4532
+ else if (
4538
4533
  t.ovl &&
4539
4534
  (t.v !== old || (!overlayRebuilds(t, pb) && k?._x?._snapshotValue !== old))
4540
4535
  ) {
@@ -4557,26 +4552,19 @@ function foldTarget(t, old) {
4557
4552
  else delete v[key];
4558
4553
  }
4559
4554
  } else {
4560
- for (const key of Reflect.ownKeys(pb)) {
4561
- const d = Object.getOwnPropertyDescriptor(pb, key);
4562
- if (d.get || d.set || !d.enumerable || !d.writable || !d.configurable)
4563
- Object.defineProperty(v, key, d);
4564
- else if (d.value !== old[key] || !hasOwn$1.call(old, key)) v[key] = d.value;
4565
- }
4555
+ for (const key of Reflect.ownKeys(pb)) copyOwn(v, pb, key);
4566
4556
  for (const key of Reflect.ownKeys(old)) {
4567
4557
  if (!hasOwn$1.call(pb, key)) delete v[key];
4568
4558
  }
4569
4559
  }
4570
- t.pb = null;
4571
- t.wk = null;
4572
4560
  } else {
4573
4561
  // The clone (or adopted object) becomes the committed backing
4574
4562
  // (path copy below).
4575
4563
  t.v = t.ovl ? materializePB(t) : pb;
4576
4564
  t.ch = t.v[$TARGET] !== undefined;
4577
- t.pb = null;
4578
- t.wk = null;
4579
4565
  }
4566
+ t.pb = null;
4567
+ t.wk = null; // the written-keys window closes with the commit
4580
4568
  }
4581
4569
  if (k !== null) {
4582
4570
  // The node's committed value is the backing. A container nobody
@@ -4591,7 +4579,6 @@ function foldTarget(t, old) {
4591
4579
  const slot = parentSlotKey(t, old);
4592
4580
  if (t.u.v[slot] === old) {
4593
4581
  privatizeCommitted(t.u);
4594
- devAssertNeverUserMutation(t.u.v);
4595
4582
  t.u.v[slot] = t.v;
4596
4583
  }
4597
4584
  }
@@ -4619,7 +4606,7 @@ function reportReplacedContainers(t, old, pb, writtenKeys) {
4619
4606
  const owner = storeRootOwner(t);
4620
4607
  for (const key of keys) {
4621
4608
  if ((isArray && key === "length") || key === $OWNER) continue;
4622
- if (t.del !== null && t.del.has(key)) continue;
4609
+ if (t.del?.has(key)) continue;
4623
4610
  const ov = unwrapValue(old[key]);
4624
4611
  const nv = unwrapValue(pb[key]);
4625
4612
  if (
@@ -4767,7 +4754,7 @@ function notifyWrites(t) {
4767
4754
  if (od?.get || od?.set || !isEqual(od?.value, nd.value)) setSignal(node, () => nd.value);
4768
4755
  continue;
4769
4756
  }
4770
- const nv = t.del !== null && t.del.has(key) ? undefined : pb[key];
4757
+ const nv = t.del?.has(key) ? undefined : pb[key];
4771
4758
  setSignal(node, () => nv);
4772
4759
  if (t.fam !== null) {
4773
4760
  // The derive's write is a derivation; a user setter's is a proposal
@@ -4785,13 +4772,13 @@ function notifyWrites(t) {
4785
4772
  for (const key of keys) {
4786
4773
  const node = has[key];
4787
4774
  if (node !== undefined) {
4788
- setSignal(node, key in pb && !(t.del !== null && t.del.has(key)));
4775
+ setSignal(node, key in pb && !t.del?.has(key));
4789
4776
  if (t.fam !== null) holdWithDerive(t, node);
4790
4777
  }
4791
4778
  }
4792
4779
  }
4793
4780
  if (t.dk !== null) {
4794
- if (t.del !== null && t.del.size !== 0) bumpDeep(t);
4781
+ if (t.del?.size) bumpDeep(t);
4795
4782
  else
4796
4783
  for (const key of writtenKeys ?? Reflect.ownKeys(pb)) {
4797
4784
  if (key === $OWNER) continue;
@@ -4811,7 +4798,7 @@ function notifyWrites(t) {
4811
4798
  if (k !== null && (k._subs !== null || k._config & CONFIG_OVERRIDE)) {
4812
4799
  let changed;
4813
4800
  if (t.ovl) {
4814
- changed = t.del !== null && t.del.size !== 0;
4801
+ changed = !!t.del?.size;
4815
4802
  if (!changed) {
4816
4803
  for (const key of Reflect.ownKeys(pb)) {
4817
4804
  if (!hasOwn$1.call(old, key)) {
@@ -4966,7 +4953,7 @@ function serveShallow(target, key, v) {
4966
4953
  function draftServe(target, proxy) {
4967
4954
  if (writeScopes !== null && inDraft(target)) {
4968
4955
  const ct = proxy?.[$TARGET];
4969
- if (ct !== undefined && ct.v !== undefined) writeScopes.add(scopeKey(ct));
4956
+ if (ct?.v !== undefined) writeScopes.add(scopeKey(ct));
4970
4957
  }
4971
4958
  return proxy;
4972
4959
  }
@@ -5009,13 +4996,7 @@ function readSource(target, key, shape = false) {
5009
4996
  // sees — the lanes' values and the tick's own guesses over the truth
5010
4997
  // (#3665), never a staging awaiting its fold: the draft is born on the
5011
4998
  // read (and notified at the setter's exit like any).
5012
- if (
5013
- userWrite &&
5014
- target.fam !== null &&
5015
- target.fam.opt === true &&
5016
- !pendingNotify.has(target) &&
5017
- inDraft(target)
5018
- ) {
4999
+ if (userWrite && target.fam?.opt === true && !pendingNotify.has(target) && inDraft(target)) {
5019
5000
  const draft = ensurePB(target);
5020
5001
  pendingNotify.add(target);
5021
5002
  return draft;
@@ -5054,7 +5035,7 @@ function readSource(target, key, shape = false) {
5054
5035
  // it).
5055
5036
  if (key !== undefined) {
5056
5037
  const leaf = target.n?.[key];
5057
- if (leaf !== undefined && leaf._config & CONFIG_OVERRIDE) return committed(target);
5038
+ if (leaf?._config & CONFIG_OVERRIDE) return committed(target);
5058
5039
  }
5059
5040
  const held = k !== null && (k._config & CONFIG_HELD) !== 0;
5060
5041
  // A held container: the hold's unit is the key (#3706). A key the HELD
@@ -5168,18 +5149,14 @@ function adoptChild(target, key) {
5168
5149
  // A raw-marked value wraps to itself, a proxy to its own target: neither
5169
5150
  // is a fresh target around `ov`.
5170
5151
  const ct = wrap(ov, target, key)[$TARGET];
5171
- if (ct === undefined || ct.v !== ov) return false;
5152
+ if (ct?.v !== ov) return false;
5172
5153
  adoptPB(ct, nv, false);
5173
5154
  holdNode(getContainerNode(ct), txOf(k));
5174
5155
  return true;
5175
5156
  }
5176
5157
  /** A family target's unheld staging, read with no pass (a handler). */
5177
5158
  function familyAhead(target) {
5178
- return (
5179
- target.fam !== null &&
5180
- context === null &&
5181
- !(target.k !== null && target.k._config & CONFIG_HELD)
5182
- );
5159
+ return target.fam !== null && context === null && !(target.k?._config & CONFIG_HELD);
5183
5160
  }
5184
5161
  /** Did this batch change `key` — value (slot equality), presence, and (for
5185
5162
  * a `shape` reader — a descriptor read, a node's hold) enumerability or
@@ -5204,7 +5181,7 @@ function changedBetween(target, v, pb, key, shape) {
5204
5181
  !plainProto(v)
5205
5182
  )
5206
5183
  return true;
5207
- if (target.del !== null && target.del.has(key)) return key in v;
5184
+ if (target.del?.has(key)) return key in v;
5208
5185
  const inNew = hasOwn$1.call(pb, key) || (target.ovl && hasOwn$1.call(target.v, key));
5209
5186
  if (inNew !== hasOwn$1.call(v, key)) return true;
5210
5187
  if (!inNew) return false;
@@ -5385,7 +5362,7 @@ function enumerationSource(target) {
5385
5362
  * draft's (it composed already), not a derive's write-override read (the
5386
5363
  * derive composes on the truth). */
5387
5364
  function optRead(target) {
5388
- return target.fam !== null && target.fam.opt === true && !inDraft(target) && !writeOverride;
5365
+ return target.fam?.opt === true && !inDraft(target) && !writeOverride;
5389
5366
  }
5390
5367
  /** Pollution keys are never served from the prototype (core R30). */
5391
5368
  const UNSAFE_KEYS = new Set(["__proto__", "prototype", "constructor"]);
@@ -5511,7 +5488,7 @@ const traps = {
5511
5488
  const src = readSource(target, key);
5512
5489
  // Overlay delete (#3044): a prototype overlay cannot shadow a delete, so
5513
5490
  // deleted keys are tracked aside and read as absent in the pending view.
5514
- if (target.del !== null && src === target.pb && target.del.has(key)) {
5491
+ if (src === target.pb && target.del?.has(key)) {
5515
5492
  if (!inDraft(target) && getObserver() !== null) read(getNode(target, key));
5516
5493
  return undefined;
5517
5494
  }
@@ -5539,7 +5516,8 @@ const traps = {
5539
5516
  !inDraft(target) &&
5540
5517
  typeof key === "string" &&
5541
5518
  key !== "then" &&
5542
- getObserver() === null
5519
+ getObserver() === null &&
5520
+ strictReadActive()
5543
5521
  ) {
5544
5522
  warnStrictReadUntracked(strictRead, {
5545
5523
  nodeName: key,
@@ -5643,7 +5621,7 @@ const traps = {
5643
5621
  if (guessed !== undefined) return guessed;
5644
5622
  }
5645
5623
  }
5646
- return key in src && !(target.del !== null && src === target.pb && target.del.has(key));
5624
+ return key in src && !(src === target.pb && target.del?.has(key));
5647
5625
  },
5648
5626
  ownKeys(target) {
5649
5627
  if (verdict !== null && affectsHooks !== null && getObserver() === null)
@@ -5690,8 +5668,7 @@ const traps = {
5690
5668
  },
5691
5669
  set(target, key, value) {
5692
5670
  const draft = inDraft(target);
5693
- const override = !draft && writeOverride;
5694
- if (!draft && !override) {
5671
+ if (!draft && !writeOverride) {
5695
5672
  warnWriteOutsideSetter(target, key, "set");
5696
5673
  return true;
5697
5674
  }
@@ -5717,17 +5694,8 @@ const traps = {
5717
5694
  touch(target, key);
5718
5695
  if (!(key in pb)) target.kc++;
5719
5696
  }
5720
- if (UNSAFE_KEYS.has(key)) {
5721
- Object.defineProperty(pb, key, {
5722
- value: uv,
5723
- writable: true,
5724
- enumerable: true,
5725
- configurable: true
5726
- });
5727
- if (target.del !== null) target.del.delete(key);
5728
- return true;
5729
- }
5730
- if (target.ovl && target.sc !== 2 && !hasOwn$1.call(pb, key)) {
5697
+ const unsafe = UNSAFE_KEYS.has(key);
5698
+ if (unsafe || (target.ovl && target.sc !== 2 && !hasOwn$1.call(pb, key))) {
5731
5699
  Object.defineProperty(pb, key, {
5732
5700
  value: uv,
5733
5701
  writable: true,
@@ -5735,15 +5703,15 @@ const traps = {
5735
5703
  configurable: true
5736
5704
  });
5737
5705
  } else pb[key] = uv;
5738
- if (target.del !== null) target.del.delete(key);
5706
+ target.del?.delete(key);
5707
+ if (unsafe) return true;
5739
5708
  if (target.s && uv !== null && typeof uv === "object") markRawOne(uv);
5740
- if (override) notifyWrites(target);
5709
+ if (!draft) notifyWrites(target); // a write-override write
5741
5710
  return true;
5742
5711
  },
5743
5712
  defineProperty(target, key, desc) {
5744
5713
  const draft = inDraft(target);
5745
- const override = !draft && writeOverride;
5746
- if (!draft && !override) {
5714
+ if (!draft && !writeOverride) {
5747
5715
  warnWriteOutsideSetter(target, key, "defineProperty");
5748
5716
  return true;
5749
5717
  }
@@ -5754,14 +5722,13 @@ const traps = {
5754
5722
  if (target.a || !(desc.enumerable && desc.writable && desc.configurable)) target.sc = 1;
5755
5723
  touch(target, key);
5756
5724
  Object.defineProperty(pb, key, desc);
5757
- if (target.del !== null) target.del.delete(key);
5758
- if (override) notifyWrites(target);
5725
+ target.del?.delete(key);
5726
+ if (!draft) notifyWrites(target); // a write-override write
5759
5727
  return true;
5760
5728
  },
5761
5729
  deleteProperty(target, key) {
5762
5730
  const draft = inDraft(target);
5763
- const override = !draft && writeOverride;
5764
- if (!draft && !override) {
5731
+ if (!draft && !writeOverride) {
5765
5732
  warnWriteOutsideSetter(target, key, "deleteProperty");
5766
5733
  return true;
5767
5734
  }
@@ -5782,7 +5749,7 @@ const traps = {
5782
5749
  committed || target.kc--;
5783
5750
  }
5784
5751
  if (committed) (target.del ??= new Set()).add(key);
5785
- if (override) notifyWrites(target);
5752
+ if (!draft) notifyWrites(target); // a write-override write
5786
5753
  return true;
5787
5754
  }
5788
5755
  };
@@ -5943,17 +5910,17 @@ function createStore$1(initialValue, options) {
5943
5910
  * slot-patch channel (collected row bodies) over per-record registration. */
5944
5911
  function storeIsShallow(proxy) {
5945
5912
  const t = proxy?.[$TARGET];
5946
- return t !== undefined && t.s === true;
5913
+ return t?.s === true;
5947
5914
  }
5948
5915
  /** True when `proxy` belongs to a projection family (S3). */
5949
5916
  function storeHasFamily(proxy) {
5950
5917
  const t = proxy?.[$TARGET];
5951
- return t !== undefined && t.fam !== null;
5918
+ return t?.fam != null;
5952
5919
  }
5953
5920
  /** True when `proxy` belongs to an OPTIMISTIC family (S4). */
5954
5921
  function storeHasOptimisticFamily(proxy) {
5955
5922
  const t = proxy?.[$TARGET];
5956
- return t !== undefined && t.fam?.opt === true;
5923
+ return t?.fam?.opt === true;
5957
5924
  }
5958
5925
  // ---------------------------------------------------------------------------
5959
5926
  // enumeration views (one visibility rule for the traps AND the deep walk)
@@ -5962,7 +5929,7 @@ function visibleKeys(target, src) {
5962
5929
  if (target.ovl && src === target.pb) {
5963
5930
  keys = Reflect.ownKeys(target.v);
5964
5931
  const del = target.del;
5965
- if (del !== null && del.size !== 0) keys = keys.filter(key => !del.has(key));
5932
+ if (del?.size) keys = keys.filter(key => !del.has(key));
5966
5933
  for (const key of Reflect.ownKeys(src)) {
5967
5934
  if (!hasOwn$1.call(target.v, key)) keys.push(key);
5968
5935
  }
@@ -5979,7 +5946,7 @@ function visibleKeys(target, src) {
5979
5946
  function visibleDescriptor(target, src, key) {
5980
5947
  let desc = Object.getOwnPropertyDescriptor(src, key);
5981
5948
  if (target.ovl && src === target.pb) {
5982
- if (target.del !== null && target.del.has(key)) return undefined;
5949
+ if (target.del?.has(key)) return undefined;
5983
5950
  if (desc === undefined) desc = Object.getOwnPropertyDescriptor(target.v, key);
5984
5951
  }
5985
5952
  return desc;
@@ -5990,7 +5957,7 @@ function visibleDescriptor(target, src, key) {
5990
5957
  * witness (one node per record, not one per path), then snapshots. */
5991
5958
  function deep$1(value) {
5992
5959
  const t0 = value?.[$TARGET];
5993
- if (t0 === undefined || t0.px !== value) return value;
5960
+ if (t0?.px !== value) return value;
5994
5961
  const visited = new Set();
5995
5962
  const childTarget = (t, child, key) => {
5996
5963
  let ct = lookupTarget(child, t.fam);
@@ -6232,8 +6199,8 @@ const storePath = /* @__PURE__ */ Object.assign(
6232
6199
  */
6233
6200
  function reconcileState(value, state, key, replace = false) {
6234
6201
  if (state == null) throw new Error("Cannot reconcile null or undefined state");
6235
- const t = state?.[$TARGET];
6236
- if (t === undefined || t.px !== state) throw new Error("reconcile target is not a store proxy");
6202
+ const t = state[$TARGET];
6203
+ if (t?.px !== state) throw new Error("reconcile target is not a store proxy");
6237
6204
  const keyFn = key === null ? null : typeof key === "string" ? item => item?.[key] : key;
6238
6205
  // A user's reconcile on an optimistic family (S4): not an adoption of
6239
6206
  // truth but an optimistic edit — the keyed diff is written into the draft
@@ -6258,9 +6225,8 @@ function reconcileState(value, state, key, replace = false) {
6258
6225
  if (!replace) throw new Error("Cannot reconcile states with different identity");
6259
6226
  // A different entity at the root: the old backing is disowned (wraps
6260
6227
  // fresh if re-handed) and the new one adopted whole.
6261
- const out = t.pb ?? t.v;
6262
- if (isOwned(out)) delete out[$OWNER];
6263
- else (t.fam?.map ?? storeLookup).delete(out);
6228
+ if (isOwned(prev)) delete prev[$OWNER];
6229
+ else (t.fam?.map ?? storeLookup).delete(prev);
6264
6230
  adoptPB(t, incoming);
6265
6231
  return;
6266
6232
  }
@@ -6276,50 +6242,42 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6276
6242
  // as present through its prototype; materialized first — `adoptPB` does
6277
6243
  // so anyway, after the base was taken — so a deletion the draft made is
6278
6244
  // a deletion here (#3743: the presence and value diffs both read `old`).
6279
- const prev = t.ovl ? materializePB(t) : (t.pb ?? t.v);
6280
- if (incoming === prev && !isOwned(prev)) return;
6281
- const fam = t.fam;
6245
+ const old = t.ovl ? materializePB(t) : (t.pb ?? t.v);
6246
+ if (incoming === old && !isOwned(old)) return;
6282
6247
  // Q-D: a landing on an optimistic family is reconciled against the VIEW
6283
6248
  // — key matching from the arrangement the guesses made (R28), so a row
6284
6249
  // the guess added keeps its proxy when the server returns it.
6285
- const prevView = fam?.opt === true ? optHooks.view(t, prev) : prev;
6250
+ const prevView = t.fam?.opt === true ? optHooks.view(t, old) : old;
6286
6251
  const nextArr = Array.isArray(incoming);
6287
6252
  const shallow = t.s === true;
6288
- const old = prev;
6289
6253
  adoptPB(t, incoming, false);
6290
6254
  if (shallow) markRawIngest(incoming);
6255
+ // The deep witness is bumped once per adoption (none to bump: done).
6256
+ let bumped = t.dk === null;
6291
6257
  if (Array.isArray(prevView) !== nextArr) {
6292
6258
  notifyFold(t, old, incoming);
6293
6259
  return;
6294
6260
  }
6295
6261
  if (nextArr) {
6296
- const prevRows = prevView;
6297
- const nextRows = incoming;
6298
6262
  const nodes = t.n;
6299
6263
  let nodesHit = 0;
6300
6264
  if (keyFn && !shallow) {
6301
- const plen = prevRows.length;
6302
- const nlen = nextRows.length;
6303
- let dkBumpedA = false;
6265
+ const plen = prevView.length;
6266
+ const nlen = incoming.length;
6304
6267
  let i = 0;
6305
6268
  // Aligned prefix: same row (by reference or key) at the same index.
6306
6269
  for (const end = Math.min(plen, nlen); i < end; i++) {
6307
- const nv = nextRows[i];
6308
- const pvRaw = prevRows[i];
6270
+ const nv = incoming[i];
6271
+ const pvRaw = prevView[i];
6309
6272
  if (pvRaw !== nv && !sameRow$1(pvRaw, nv, keyFn)) break; // misaligned: fall to the keyed remainder below
6273
+ if (nv !== null && typeof nv === "object" && (pvRaw !== nv || isOwned(nv)))
6274
+ descend(unwrapValue(pvRaw), nv, keyFn, proj, t, i);
6310
6275
  if (
6311
- (pvRaw !== nv || (nv !== null && typeof nv === "object" && isOwned(nv))) &&
6312
- nv !== null &&
6313
- typeof nv === "object"
6314
- )
6315
- descend(unwrapValue(pvRaw), nv, keyFn, fam, proj);
6316
- if (
6317
- t.dk !== null &&
6318
- !dkBumpedA &&
6276
+ !bumped &&
6319
6277
  !(nv !== null && typeof nv === "object" ? targetsEqual(pvRaw, nv) : isEqual(pvRaw, nv))
6320
6278
  ) {
6321
6279
  bumpDeep(t);
6322
- dkBumpedA = true;
6280
+ bumped = true;
6323
6281
  }
6324
6282
  if (nodes !== null) {
6325
6283
  const node = nodes[i];
@@ -6329,19 +6287,19 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6329
6287
  }
6330
6288
  }
6331
6289
  }
6332
- if (t.dk !== null && !dkBumpedA && i < nextRows.length) bumpDeep(t);
6290
+ if (!bumped && i < incoming.length) bumpDeep(t);
6333
6291
  const structStart = i; // misalignment point (== nlen on aligned ticks)
6334
6292
  let prevByKey = null;
6335
- for (; i < nextRows.length; i++) {
6336
- const nv = nextRows[i];
6293
+ for (; i < incoming.length; i++) {
6294
+ const nv = incoming[i];
6337
6295
  if (nv !== null && typeof nv === "object") {
6338
6296
  const nk = keyFn(nv);
6339
6297
  let pv;
6340
6298
  if (nk !== undefined) {
6341
6299
  if (prevByKey === null) {
6342
6300
  prevByKey = new Map();
6343
- for (let j = structStart; j < prevRows.length; j++) {
6344
- const p = unwrapValue(prevRows[j]);
6301
+ for (let j = structStart; j < prevView.length; j++) {
6302
+ const p = unwrapValue(prevView[j]);
6345
6303
  if (p !== null && typeof p === "object") {
6346
6304
  const pk = keyFn(p);
6347
6305
  if (pk === undefined) continue;
@@ -6355,16 +6313,16 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6355
6313
  const m = prevByKey.get(nk);
6356
6314
  if (m === undefined) pv = undefined;
6357
6315
  else if (Array.isArray(m)) {
6358
- pv = unwrapValue(prevRows[m.shift()]);
6316
+ pv = unwrapValue(prevView[m.shift()]);
6359
6317
  if (m.length === 1) prevByKey.set(nk, m[0]);
6360
6318
  } else {
6361
- pv = unwrapValue(prevRows[m]);
6319
+ pv = unwrapValue(prevView[m]);
6362
6320
  prevByKey.delete(nk);
6363
6321
  }
6364
6322
  } else {
6365
- pv = unwrapValue(prevRows[i]); // keyless item: positional fallback
6323
+ pv = unwrapValue(prevView[i]); // keyless item: positional fallback
6366
6324
  }
6367
- descend(pv, nv, keyFn, fam, proj);
6325
+ descend(pv, nv, keyFn, proj, t, i);
6368
6326
  }
6369
6327
  if (nodes !== null) {
6370
6328
  const node = nodes[i];
@@ -6375,22 +6333,20 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6375
6333
  }
6376
6334
  }
6377
6335
  } else {
6378
- const dlen = Math.min(prevRows.length, nextRows.length);
6379
- const nlen = nextRows.length;
6380
- let dkBumpedP = false;
6336
+ const dlen = Math.min(prevView.length, incoming.length);
6337
+ const nlen = incoming.length;
6381
6338
  for (let i = 0; i < nlen; i++) {
6382
- const nvP = nextRows[i];
6339
+ const nvP = incoming[i];
6383
6340
  if (!shallow && i < dlen && nvP !== null && typeof nvP === "object")
6384
- descend(unwrapValue(prevRows[i]), nvP, keyFn, fam, proj);
6341
+ descend(unwrapValue(prevView[i]), nvP, keyFn, proj, t, i);
6385
6342
  if (
6386
- t.dk !== null &&
6387
- !dkBumpedP &&
6343
+ !bumped &&
6388
6344
  !(nvP !== null && typeof nvP === "object"
6389
- ? targetsEqual(prevRows[i], nvP)
6390
- : isEqual(prevRows[i], nvP))
6345
+ ? targetsEqual(prevView[i], nvP)
6346
+ : isEqual(prevView[i], nvP))
6391
6347
  ) {
6392
6348
  bumpDeep(t);
6393
- dkBumpedP = true;
6349
+ bumped = true;
6394
6350
  }
6395
6351
  if (nodes !== null) {
6396
6352
  const node = nodes[i];
@@ -6405,7 +6361,7 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6405
6361
  if (nodes !== null && nodesHit < t.nc) {
6406
6362
  for (const key of Reflect.ownKeys(nodes)) {
6407
6363
  const idx = typeof key === "string" ? +key : NaN;
6408
- if (!(idx >= 0 && idx < nextRows.length))
6364
+ if (!(idx >= 0 && idx < incoming.length))
6409
6365
  notifyKeyDiff(nodes[key], key, old, incoming, false);
6410
6366
  }
6411
6367
  }
@@ -6414,7 +6370,6 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6414
6370
  }
6415
6371
  const nodes = t.n;
6416
6372
  let nodesHit = 0;
6417
- let dkBumped = false;
6418
6373
  for (const k in incoming) {
6419
6374
  const nv = incoming[k];
6420
6375
  const ov = old[k];
@@ -6424,13 +6379,13 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6424
6379
  (!isObj || !isOwned(nv)) &&
6425
6380
  (nodes === null || nodes[k] === undefined || !hasAccessorFlag(nodes[k]))
6426
6381
  ) {
6427
- if (nodes !== null && nodes[k] !== undefined) nodesHit++;
6382
+ if (nodes?.[k] !== undefined) nodesHit++;
6428
6383
  continue;
6429
6384
  }
6430
- if (isObj && !shallow) descend(unwrapValue(prevView[k]), nv, keyFn, fam, proj);
6431
- if (t.dk !== null && !dkBumped && !(isObj ? targetsEqual(ov, nv) : isEqual(ov, nv))) {
6385
+ if (isObj && !shallow) descend(unwrapValue(prevView[k]), nv, keyFn, proj, t, k);
6386
+ if (!bumped && !(isObj ? targetsEqual(ov, nv) : isEqual(ov, nv))) {
6432
6387
  bumpDeep(t);
6433
- dkBumped = true;
6388
+ bumped = true;
6434
6389
  }
6435
6390
  if (nodes !== null) {
6436
6391
  const node = nodes[k];
@@ -6446,7 +6401,7 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6446
6401
  if (k === $OWNER) continue;
6447
6402
  const nv = incoming[k];
6448
6403
  if (!shallow && nv !== null && typeof nv === "object")
6449
- descend(unwrapValue(old[k]), nv, keyFn, fam, proj);
6404
+ descend(unwrapValue(old[k]), nv, keyFn, proj, t, k);
6450
6405
  if (nodes !== null) {
6451
6406
  const node = nodes[k];
6452
6407
  if (node !== undefined) {
@@ -6466,16 +6421,39 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
6466
6421
  const hasOwnP = Object.prototype.hasOwnProperty;
6467
6422
  /** Descend into a changed child pair — only where something is proxied
6468
6423
  * below (§6d), never into a raw-marked leaf, never across array/object or a
6469
- * key mismatch (that is a replacement, handled by the parent's slot). */
6470
- function descend(pv, nv, keyFn, fam, proj = false) {
6471
- if (pv === null || typeof pv !== "object" || nv === null || typeof nv !== "object") return;
6472
- const ct = lookupTarget(pv, fam);
6424
+ * key mismatch (that is a replacement, handled by the parent's slot).
6425
+ *
6426
+ * A raw in both frames (the same objects swapped, reordered or shifted,
6427
+ * #4001) must not leave two slots on one row. A raw already a live row keeps
6428
+ * it at the slot it moved to; a row's `pk` follows the slot it sits at (a
6429
+ * move or a pairing writes it), so a row whose raw is at that slot of the
6430
+ * incoming frame is spoken for and not paired again here; a raw still
6431
+ * resolving to a row that moved on drops the lookup (it wraps fresh, or is
6432
+ * paired here). One lookup of `nv` on the common path, no allocation. */
6433
+ function descend(pv, nv, keyFn, proj, t, k) {
6434
+ // `nv` is an object at every call site.
6435
+ if (pv === null || typeof pv !== "object") return;
6436
+ const fam = t.fam;
6437
+ nv = unwrapValue(nv);
6438
+ let ct = lookupTarget(pv, fam);
6439
+ const nt = lookupTarget(nv, fam);
6440
+ if (ct !== undefined && ct.pk !== k) {
6441
+ if (ct.pk != k && t.v[ct.pk] === ct.v) ct = undefined;
6442
+ else if (ct.u === t) ct.pk = k;
6443
+ }
6444
+ if (nt !== undefined && nt !== ct) {
6445
+ if (nt.v === nv) {
6446
+ if (nt.u === t) nt.pk = k;
6447
+ return;
6448
+ }
6449
+ (fam?.map ?? storeLookup).delete(nv);
6450
+ }
6473
6451
  if (ct === undefined) return; // nothing proxied below this pair
6474
6452
  if (!isWrappable(nv)) return;
6475
6453
  if (rawValuesUsed && isRawValue(nv)) return;
6476
- nv = unwrapValue(nv);
6477
6454
  if (Array.isArray(pv) !== Array.isArray(nv)) return;
6478
- if (keyFn) {
6455
+ // An array slot was paired by key already (or both rows are keyless).
6456
+ if (typeof k !== "number" && keyFn) {
6479
6457
  const pk = keyFn(pv);
6480
6458
  const nk = keyFn(nv);
6481
6459
  if (pk !== undefined && nk !== undefined && !sameKey(pk, nk)) return;
@@ -8911,6 +8889,7 @@ function createDeferred(fn, options) {
8911
8889
  // The wrapping form: an unready source parks the node in the window,
8912
8890
  // and the wait is its flight until the source's settle re-runs it.
8913
8891
  if (open && e instanceof NotReadyError) ask(e);
8892
+ else land(flight);
8914
8893
  throw e;
8915
8894
  }
8916
8895
  if (open && typeof r === "object" && r !== null) {
@@ -8931,7 +8910,11 @@ function createDeferred(fn, options) {
8931
8910
  // promise, as for a plain flight: verdict and value in one flush.
8932
8911
  next: v => {
8933
8912
  const p = it.next(v);
8934
- if (first) ((first = false), p.then(done, done));
8913
+ if (first) {
8914
+ first = false;
8915
+ if (isThenable(p)) p.then(done, done);
8916
+ else done();
8917
+ }
8935
8918
  return p;
8936
8919
  },
8937
8920
  return: v => it.return?.(v) ?? Promise.resolve({ done: true, value: v })
@@ -8946,6 +8929,238 @@ function createDeferred(fn, options) {
8946
8929
  );
8947
8930
  }
8948
8931
 
8932
+ // `from` — a push source (callback subscription, Observable, AsyncIterable)
8933
+ // as an AsyncIterable a computation can return or `for await` over:
8934
+ //
8935
+ // const value = createMemo(() => from(source));
8936
+ //
8937
+ // A subscription keeps the latest value: values that arrive between pulls
8938
+ // coalesce. An AsyncIterable streams through unchanged. Either way the source
8939
+ // opens when the result is iterated and closes on return(), on supersede (the
8940
+ // owning computation re-runs) and on owner disposal, whichever comes first.
8941
+ function toSubscribe(source) {
8942
+ if (typeof source === "function") return source;
8943
+ // TC39 / RxJS interop: `Symbol.observable` (or "@@observable") returns the observable.
8944
+ // Read per call, not at module scope, so the module stays side-effect free.
8945
+ const $$observable = Symbol.observable;
8946
+ const interop = ($$observable && source?.[$$observable]) ?? source?.["@@observable"];
8947
+ const obs = typeof interop === "function" ? interop.call(source) : source;
8948
+ if (obs && typeof obs.subscribe === "function")
8949
+ return (next, error, complete) => obs.subscribe({ next, error, complete });
8950
+ return null;
8951
+ }
8952
+ const DONE = { value: undefined, done: true };
8953
+ /**
8954
+ * Ties an iterator's `finish` to the computation iterating it. Solid reaches
8955
+ * an iterator's return() only when the compute returns the iterable directly.
8956
+ * Inside an async generator the generator's own return() is queued behind its
8957
+ * pending `await`, and owner cleanups wait for the replacing pass to commit,
8958
+ * so close on supersede, with owner disposal as the backstop. No owner (an
8959
+ * async compute iterating it after its first `await`, or plain iteration):
8960
+ * no registration and no diagnostic, so only return() closes it. Scopes that
8961
+ * forbid cleanups rely on supersede and return().
8962
+ */
8963
+ function bindToOwner(finish) {
8964
+ const owner = getOwner();
8965
+ if (!owner) return;
8966
+ onSupersede(finish);
8967
+ if (!(owner._config & CONFIG_CHILDREN_FORBIDDEN)) cleanup(finish);
8968
+ }
8969
+ /**
8970
+ * An AsyncIterable as a stream bound to the iterating computation: every
8971
+ * value passes through (no coalescing). `finish` calls the inner return()
8972
+ * once and settles pending pulls as done, so a `for await` over it exits as
8973
+ * soon as the computation re-runs — even when the inner iterator is an async
8974
+ * generator whose own return() the language queues behind its pending
8975
+ * `await`.
8976
+ */
8977
+ function wrapIterable(source) {
8978
+ return {
8979
+ [Symbol.asyncIterator]() {
8980
+ const it = source[Symbol.asyncIterator]();
8981
+ let closed = false;
8982
+ const pending = new Set();
8983
+ const finish = () => {
8984
+ if (closed) return DONE;
8985
+ closed = true;
8986
+ for (const resolve of pending) resolve(DONE);
8987
+ pending.clear();
8988
+ try {
8989
+ const returned = it.return?.();
8990
+ if (isThenable(returned)) returned.then(undefined, () => {});
8991
+ } catch {}
8992
+ return DONE;
8993
+ };
8994
+ bindToOwner(finish);
8995
+ return {
8996
+ next(...args) {
8997
+ if (closed) return Promise.resolve(DONE);
8998
+ const step = it.next(...args);
8999
+ // A bare result (the producer's buffered fast path) passes through.
9000
+ if (!isThenable(step)) return step;
9001
+ return new Promise((resolve, reject) => {
9002
+ pending.add(resolve);
9003
+ step.then(
9004
+ r => pending.delete(resolve) && resolve(r),
9005
+ e => pending.delete(resolve) && reject(e)
9006
+ );
9007
+ });
9008
+ },
9009
+ return() {
9010
+ return Promise.resolve(finish());
9011
+ }
9012
+ };
9013
+ }
9014
+ };
9015
+ }
9016
+ const toCleanup = t =>
9017
+ typeof t === "function"
9018
+ ? t
9019
+ : t && typeof t.unsubscribe === "function"
9020
+ ? () => t.unsubscribe()
9021
+ : () => {};
9022
+ /**
9023
+ * Converts a callback subscription, an Observable or an AsyncIterable into an
9024
+ * AsyncIterable for a Solid computation. A subscription keeps only the latest
9025
+ * value, and a value the source emits during `subscribe` is served without a
9026
+ * loading frame; an AsyncIterable streams every value through.
9027
+ *
9028
+ * The source closes on supersede — the owning computation re-running — and
9029
+ * on owner disposal. Inside an async generator memo that holds for a
9030
+ * `for await` that starts before the first `await`; one that starts after it
9031
+ * has no owner to bind to, so its source closes only when the loop ends.
9032
+ * Prefer returning `from(...)` from its own memo, with awaited inputs read
9033
+ * from upstream memos and transforms done in a downstream memo.
9034
+ *
9035
+ * Iterate the result with `for await` or return it from a computation: its
9036
+ * `next()` returns an already-buffered value as a bare result, not a promise.
9037
+ *
9038
+ * @example
9039
+ * ```ts
9040
+ * const position = createMemo(() =>
9041
+ * from<Position>((next, error) => {
9042
+ * const id = navigator.geolocation.watchPosition(next, error);
9043
+ * return () => navigator.geolocation.clearWatch(id);
9044
+ * })
9045
+ * );
9046
+ * const ticks = createMemo(() => from(interval$(rate())));
9047
+ * ```
9048
+ */
9049
+ function from(source) {
9050
+ const subscribe = toSubscribe(source);
9051
+ if (!subscribe) {
9052
+ if (source && typeof source[Symbol.asyncIterator] === "function") return wrapIterable(source);
9053
+ throw new TypeError("from(): expected a subscribe function, an Observable or an AsyncIterable");
9054
+ }
9055
+ return {
9056
+ [Symbol.asyncIterator]() {
9057
+ let latest;
9058
+ let has = false;
9059
+ let done = false;
9060
+ let failed = false;
9061
+ let failure;
9062
+ let waiting = null;
9063
+ let unsubscribe = null;
9064
+ let closed = false;
9065
+ const close = () => {
9066
+ if (closed) return;
9067
+ closed = true;
9068
+ done = true;
9069
+ const c = unsubscribe;
9070
+ unsubscribe = null;
9071
+ // A throwing unsubscribe must not escape: at supersede this runs
9072
+ // inside flush(), ahead of the replacing pass (the returned-iterator
9073
+ // path swallows it the same way).
9074
+ try {
9075
+ c?.();
9076
+ } catch {}
9077
+ };
9078
+ const settle = () => {
9079
+ if (!waiting) return;
9080
+ const w = waiting;
9081
+ if (has) {
9082
+ waiting = null;
9083
+ has = false;
9084
+ w.resolve({ value: latest, done: false });
9085
+ } else if (failed) {
9086
+ waiting = null;
9087
+ failed = false;
9088
+ w.reject(failure);
9089
+ } else if (done) {
9090
+ waiting = null;
9091
+ w.resolve({ value: undefined, done: true });
9092
+ }
9093
+ };
9094
+ const teardown = subscribe(
9095
+ value => {
9096
+ if (done) return;
9097
+ latest = value;
9098
+ has = true;
9099
+ settle();
9100
+ },
9101
+ err => {
9102
+ if (done) return;
9103
+ failed = true;
9104
+ failure = err;
9105
+ done = true;
9106
+ settle();
9107
+ close();
9108
+ },
9109
+ () => {
9110
+ if (done) return;
9111
+ done = true;
9112
+ settle();
9113
+ close();
9114
+ }
9115
+ );
9116
+ unsubscribe = toCleanup(teardown);
9117
+ // Completed or errored during subscribe: release what it just returned.
9118
+ if (closed) {
9119
+ const c = unsubscribe;
9120
+ unsubscribe = null;
9121
+ try {
9122
+ c();
9123
+ } catch {}
9124
+ }
9125
+ // Idempotent: return(), supersede and disposal may all reach it.
9126
+ const finish = () => {
9127
+ close();
9128
+ has = false;
9129
+ if (waiting) {
9130
+ const w = waiting;
9131
+ waiting = null;
9132
+ w.resolve(DONE);
9133
+ }
9134
+ return DONE;
9135
+ };
9136
+ bindToOwner(finish);
9137
+ return {
9138
+ next() {
9139
+ // A buffered value is returned as a bare result, not a promise: the
9140
+ // iterator consumer lands it in the same pass, so a source that
9141
+ // emits during subscribe (BehaviorSubject-style) shows its value
9142
+ // with no loading frame. `for await` accepts either form.
9143
+ if (has) {
9144
+ has = false;
9145
+ return { value: latest, done: false };
9146
+ }
9147
+ if (failed) {
9148
+ failed = false;
9149
+ return Promise.reject(failure);
9150
+ }
9151
+ if (done) return Promise.resolve({ value: undefined, done: true });
9152
+ return new Promise((resolve, reject) => {
9153
+ waiting = { resolve, reject };
9154
+ });
9155
+ },
9156
+ return() {
9157
+ return Promise.resolve(finish());
9158
+ }
9159
+ };
9160
+ }
9161
+ };
9162
+ }
9163
+
8949
9164
  // CARVE 4: `flatten` lived in boundaries.ts but is the general children
8950
9165
  // resolver the renderers (`web`, `universal`, `solid`'s `children()`) walk
8951
9166
  // through — not a boundary feature. Moved verbatim so the boundary carve
@@ -9984,6 +10199,7 @@ export {
9984
10199
  enableExternalSource,
9985
10200
  flatten,
9986
10201
  flush,
10202
+ from,
9987
10203
  getContext,
9988
10204
  getObserver,
9989
10205
  getOwner,