@solidjs/signals 2.0.0-rc.4 → 2.0.0-rc.5

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 (53) hide show
  1. package/dist/dev.js +1169 -186
  2. package/dist/node.cjs +2041 -1167
  3. package/dist/prod/boundaries.js +4 -1
  4. package/dist/prod/core/async.js +124 -95
  5. package/dist/prod/core/constants.js +55 -1
  6. package/dist/prod/core/core.js +297 -222
  7. package/dist/prod/core/effect.js +28 -28
  8. package/dist/prod/core/error.js +13 -1
  9. package/dist/prod/core/external.js +2 -2
  10. package/dist/prod/core/graph.js +27 -27
  11. package/dist/prod/core/heap.js +30 -30
  12. package/dist/prod/core/lanes.js +32 -32
  13. package/dist/prod/core/optimistic.js +54 -54
  14. package/dist/prod/core/owner.js +34 -34
  15. package/dist/prod/core/scheduler.js +318 -137
  16. package/dist/prod/core/verdict.js +112 -59
  17. package/dist/prod/index.js +3 -3
  18. package/dist/prod/map.js +106 -106
  19. package/dist/prod/signals.js +253 -25
  20. package/dist/prod/store/next/optimistic.js +262 -123
  21. package/dist/prod/store/next/patch.js +6 -6
  22. package/dist/prod/store/next/projection.js +23 -19
  23. package/dist/prod/store/next/store.js +129 -35
  24. package/dist/prod/store/store.js +2 -2
  25. package/dist/types/core/async.d.ts +2 -0
  26. package/dist/types/core/attribution.d.ts +9 -4
  27. package/dist/types/core/constants.d.ts +54 -0
  28. package/dist/types/core/core.d.ts +19 -20
  29. package/dist/types/core/error.d.ts +9 -0
  30. package/dist/types/core/index.d.ts +2 -2
  31. package/dist/types/core/scheduler.d.ts +34 -0
  32. package/dist/types/core/types.d.ts +12 -0
  33. package/dist/types/index.d.ts +3 -3
  34. package/dist/types/signals.d.ts +108 -0
  35. package/dist/types/store/next/optimistic.d.ts +13 -10
  36. package/dist/types/store/next/projection.d.ts +1 -1
  37. package/dist/types/store/next/store.d.ts +22 -0
  38. package/dist/types/store/next/target.d.ts +15 -0
  39. package/dist/types-cjs/core/async.d.cts +2 -0
  40. package/dist/types-cjs/core/attribution.d.cts +9 -4
  41. package/dist/types-cjs/core/constants.d.cts +54 -0
  42. package/dist/types-cjs/core/core.d.cts +19 -20
  43. package/dist/types-cjs/core/error.d.cts +9 -0
  44. package/dist/types-cjs/core/index.d.cts +2 -2
  45. package/dist/types-cjs/core/scheduler.d.cts +34 -0
  46. package/dist/types-cjs/core/types.d.cts +12 -0
  47. package/dist/types-cjs/index.d.cts +3 -3
  48. package/dist/types-cjs/signals.d.cts +108 -0
  49. package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
  50. package/dist/types-cjs/store/next/projection.d.cts +1 -1
  51. package/dist/types-cjs/store/next/store.d.cts +22 -0
  52. package/dist/types-cjs/store/next/target.d.cts +15 -0
  53. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- import { computed, ext, setSignal, isEqual } from "../../core/core.js";
1
+ import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, NOT_PENDING, CONFIG_OPTIMISTIC, CONFIG_HELD_TRUTH, unwrapOverride } from "../../core/constants.js";
2
2
 
3
- import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, NOT_PENDING, unwrapOverride, CONFIG_OPTIMISTIC } from "../../core/constants.js";
3
+ import { computed, setSignal, isEqual } from "../../core/core.js";
4
4
 
5
- import { GlobalQueue, insertSubs, schedule, globalQueue } from "../../core/scheduler.js";
5
+ import { activeTransition, GlobalQueue, runAsTransitionBatch, globalQueue, currentTransition } from "../../core/scheduler.js";
6
6
 
7
7
  import "../../core/invariants.js";
8
8
 
@@ -16,11 +16,11 @@ import { $TARGET, markRawIngest, setNextOptimisticViewResolver, isWrappable, raw
16
16
 
17
17
  import { runProjectionComputedNext } from "./projection.js";
18
18
 
19
- import { wrapNext, runAuthoritative, storeSetterNext, hasActiveOverride, unwrapValue, getNode, getHasNode, targetsEqual, getKeySetNode, bumpDeep } from "./store.js";
19
+ import { wrapNext, runAuthoritative, storeSetterNext, hasActiveOverride, unwrapValue, targetsEqual, getNode, getHasNode, getKeySetNode, bumpDeep, stagedTruthPB, authoritativeRead } from "./store.js";
20
20
 
21
21
  import { patchHooks, rowHooks } from "./patch-hooks.js";
22
22
 
23
- import { buildIdentityRowOps, sameKey } from "./reconcile.js";
23
+ import { sameKey, buildIdentityRowOps } from "./reconcile.js";
24
24
 
25
25
  import { setOptHooks } from "./target.js";
26
26
 
@@ -33,12 +33,32 @@ import { setOptHooks } from "./target.js";
33
33
  * armed presence nodes (the §6 overlay), so structural optimism reverts with
34
34
  * the same per-transaction granularity (FINDING-2's fix by construction).
35
35
  *
36
- * Derived form = an optimistic projection: the derive's recompute and its
37
- * async commits run under projectionWriteActive (authoritative landings
38
- * commit silently beneath any active overrides). The transitionBlocked
39
- * store-half (#2951) is installed here for next-shaped targets, chaining the
36
+ * Derived form = an optimistic projection. Landings follow the fold rule
37
+ * (#3164, RUL-2 as re-ruled): while a transaction retains optimistic edits
38
+ * on the family, truth that lands STAGES into that transaction — a keyed
39
+ * identity-preserving walk written through the ordinary staged setter
40
+ * channel — and reveals atomically at settle, exactly like a signal landing
41
+ * under an active override (asyncWrite's held branch). Optimistic edits are
42
+ * never consumed by landings; they live exactly as long as their transaction
43
+ * and die by engine-native revert. With no retainer, landings commit
44
+ * immediately under projectionWriteActive (authoritative, silently beneath
45
+ * any bare-write overrides — those ride the flight's own transition, #2951).
46
+ * Authoritative readers (until()'s predicate) tunnel into staged truth via
47
+ * the node read path's pending-value arm. The transitionBlocked store-half
48
+ * (#2951) is installed here for next-shaped targets, chaining the
40
49
  * legacy/engine checks.
41
- */ let blockedInstalled = false;
50
+ */
51
+ /** #3164 fold: a stamped truth is HELD (masked from ordinary readers until
52
+ * the reveal) only while its transition is live AND retaining optimism —
53
+ * overrides are what make partial-coverage composition a tear. A plain
54
+ * async transition carries no overrides, so downstream computes must see
55
+ * staged values to converge (normal speculation). Resolves merges first:
56
+ * merge unions optimistic nodes/stores into the target. */ function transitionHoldsOptimism(e) {
57
+ const t = currentTransition(e);
58
+ return t.sn !== true && (t.ze.length !== 0 || t.En.size !== 0);
59
+ }
60
+
61
+ let blockedInstalled = false;
42
62
 
43
63
  function installNextBlockedHalf() {
44
64
  if (blockedInstalled) return;
@@ -49,14 +69,15 @@ function installNextBlockedHalf() {
49
69
  setOptHooks({
50
70
  notifyOptimisticWrites: notifyOptimisticWrites,
51
71
  optimisticView: optimisticView,
52
- applyTentative: applyTentative
72
+ applyTentative: applyTentative,
73
+ retainsOptimism: transitionHoldsOptimism
53
74
  });
54
75
  setNextOptimisticViewResolver((e, t) => optimisticView(e, t));
55
76
  // Scheduler flush tails call _clearOptimisticStores whenever tracked
56
77
  // stores exist; next has no layer to clear — reverts are engine-native —
57
78
  // so the hook only empties the batch set.
58
- if (!GlobalQueue.Bt) {
59
- GlobalQueue.Bt = e => {
79
+ if (!GlobalQueue.qt) {
80
+ GlobalQueue.qt = e => {
60
81
  // Patch channel (revert site): engine-native reverts flip node values
61
82
  // back to committed; patched records need a forced DOM re-apply from
62
83
  // the post-revert view. Emission only — next keeps no layer to clear.
@@ -71,15 +92,22 @@ function installNextBlockedHalf() {
71
92
  // row identity against the post-revert view (resolved from the
72
93
  // target at drain — overrides are gone by then).
73
94
  if (e.pc !== null && e.pc.ro !== null) rowHooks.emitRowOpsOptimistic(e, null, null);
95
+ // Keyset resync (classic channel twin): the keyset node's own
96
+ // revert can compare EQUAL (a landing's bump matched the
97
+ // tentative bump) while the arrangement underneath changed —
98
+ // mapArray/ownKeys subscribers must re-read the post-revert
99
+ // view. Authoritative bump: never re-arm the node we are
100
+ // clearing.
101
+ if (e.k !== null) runAuthoritative(() => setSignal(e.k, e => e + 1));
74
102
  }
75
103
  }
76
104
  }
77
105
  e.clear();
78
106
  };
79
107
  }
80
- const e = GlobalQueue.In;
81
- GlobalQueue.In = t => {
82
- for (const e of t.Tn) {
108
+ const e = GlobalQueue.dn;
109
+ GlobalQueue.dn = t => {
110
+ for (const e of t.En) {
83
111
  const t = e?.[$TARGET];
84
112
  const n = t?.fam?.node;
85
113
  // The hold exists to keep optimistic state alive until the store's own
@@ -101,10 +129,10 @@ function familyHasLiveOverrides(e) {
101
129
  if (t === null) continue;
102
130
  for (const e of Reflect.ownKeys(t)) {
103
131
  const n = t[e];
104
- if (n.o?.De !== undefined && n.o?.De !== NOT_PENDING) return true;
132
+ if (n.o?.Oe !== undefined && n.o?.Oe !== NOT_PENDING) return true;
105
133
  }
106
134
  }
107
- if (e.k !== null && e.k.o?.De !== undefined && e.k.o?.De !== NOT_PENDING) return true;
135
+ if (e.k !== null && e.k.o?.Oe !== undefined && e.k.o?.Oe !== NOT_PENDING) return true;
108
136
  }
109
137
  t.clear();
110
138
  // nothing live — drop the bookkeeping
@@ -127,32 +155,216 @@ function createOptimisticStoreNext(e, t, n) {
127
155
  };
128
156
  const s = wrapNext(o, null, null, r);
129
157
  r.px = s;
158
+ // Same key resolution the projection channels use ("id" default) — replay's
159
+ // satisfaction rule reads it off the family.
160
+ const l = n?.key === undefined ? "id" : n.key;
161
+ r.key = typeof l === "function" ? l : l === null ? null : e => isWrappable(e) ? e[l] : undefined;
130
162
  if (r.shallow) {
131
163
  s[$TARGET].s = true;
132
164
  markRawIngest(o);
133
165
  }
134
166
  if (i) {
135
167
  const t = e;
136
- // Async commits land outside the computed's sync body — re-apply the
137
- // authoritative-write posture there too. Landings consume the family's
138
- // tentative overrides (RUL-2: visible landed truth replaces optimism)
139
- // both the reconcile-channel commit and per-op post-await draft writes.
140
- const consume = () => consumeOverridesNext(r);
141
- const wrapCommit = e => {
142
- runAuthoritative(e);
143
- consume();
168
+ // Landing router (#3164 fold ruling): while a transaction retains
169
+ // optimistic edits on this family, truth landings stage INTO it and
170
+ // reveal atomically at settle; with no retainer they commit immediately
171
+ // under the authoritative posture (async commits land outside the
172
+ // computed's sync body, so the posture is re-applied here).
173
+ const wrapCommit = (e, t) => {
174
+ const n = retainingTransition(r);
175
+ if (n === null) runAuthoritative(e); else stageLanding(r, n, t);
176
+ };
177
+ // Draft writes (the derive mutating its draft, sync body and post-await
178
+ // continuations alike) are the same truth channel per-operation: bind
179
+ // each op to the retaining transaction so its node writes stage and its
180
+ // backing fold defers (ensurePB stamps foldBatches with the swapped-in
181
+ // batch; the write-override eager-commit branch in notifyWrites yields
182
+ // to any active transaction).
183
+ const aroundDraftWrite = e => {
184
+ const t = retainingTransition(r);
185
+ if (t === null) e(); else runFolded(t, e);
144
186
  };
145
187
  let i;
146
188
  if (n?.seedLoadingValue) i = {
147
189
  loadingValue: undefined
148
190
  };
149
191
  const o = computed(() => {
150
- runAuthoritative(() => runProjectionComputedNext(s, t, n?.key === undefined ? "id" : n.key, wrapCommit, consume));
192
+ runAuthoritative(() => runProjectionComputedNext(s, t, n?.key === undefined ? "id" : n.key, wrapCommit, aroundDraftWrite));
151
193
  }, i);
152
194
  o.T &= ~CONFIG_AUTO_DISPOSE;
153
195
  r.node = o;
154
196
  }
155
- return [ s, e => storeSetterNext(s, e) ];
197
+ return [ s, e => {
198
+ // Retention ledger (#3164): record the owning transaction so landings
199
+ // know to fold. Captured at entry — the action machinery has the
200
+ // transaction ambient while user code runs; a bare write with no
201
+ // transaction retains nothing (it rides the flight's own transition
202
+ // per #2951 and dies with it).
203
+ const t = activeTransition;
204
+ storeSetterNext(s, e);
205
+ if (t !== null) (r.rt ??= new Set).add(t);
206
+ } ];
207
+ }
208
+
209
+ /** Resolve a retained transition through its merge chain (`_done` holds the
210
+ * merge target while merged, `true` once settled). Null = dead. */ function liveTransition(e) {
211
+ while (typeof e.sn === "object") e = e.sn;
212
+ return e.sn === true ? null : e;
213
+ }
214
+
215
+ /** The transaction truth landings fold into: the first live member of the
216
+ * family's retention ledger (dead members prune here). Multiple live
217
+ * retainers entangle through their shared family writes and settle
218
+ * together, so folding into the first reaches all of them. */ function retainingTransition(e) {
219
+ const t = e.rt;
220
+ if (t === undefined || t.size === 0) return null;
221
+ let n = null;
222
+ for (const e of t) {
223
+ const i = liveTransition(e);
224
+ if (i === null) t.delete(e); else n ??= i;
225
+ }
226
+ return n;
227
+ }
228
+
229
+ /** Fold a landing into the retaining transaction (#3164): the landed value
230
+ * is written through the ORDINARY staged setter channel — node writes park
231
+ * as `_pendingValue` registered with the transaction's batch (speculation
232
+ * and until()'s authoritative tunnel see them; live view stays coherent),
233
+ * and the backing fold defers via the foldBatches stamp — under the
234
+ * authoritative posture, so armed nodes take the engine bypass and no
235
+ * override is created. The engine's own commit machinery reveals everything
236
+ * atomically when the transaction settles (transitions never abort: failed
237
+ * actions still commit — only optimistic overrides revert). */ function stageLanding(e, t, n) {
238
+ runFolded(t, () => runAuthoritative(() => storeSetterNext(e.px, t => {
239
+ stagedApply(t, unwrapValue(n), e.key ?? null);
240
+ }, false)));
241
+ }
242
+
243
+ /** Run a fold write inside the retaining transaction's batch, then
244
+ * transition-stamp its staged nodes NOW (parity with the parked-transition
245
+ * flush path's reassignPendingTransition): the stamp is what routes stale
246
+ * (render) readers to the committed value — core read's cross-transaction
247
+ * guard — and what makes foldHeld defer the backing for context-free
248
+ * readers. A microtask staging never crosses that flush path, so without
249
+ * the stamp a render effect's speculative recompute would compose staged
250
+ * truth with live overrides — the #3164 tear, one window later. Armed
251
+ * nodes additionally raise CONFIG_HELD_TRUTH: their staged value is
252
+ * confirming truth masked from ordinary readers until the reveal (plain
253
+ * staged nodes stay visible — normal speculation; override-covered nodes
254
+ * stay unarmed — the override is their display and its revert their
255
+ * notification, A17). */ function runFolded(e, t) {
256
+ runAsTransitionBatch(e, t);
257
+ const n = e.Lt;
258
+ for (let t = 0; t < n.length; t++) {
259
+ const i = n[t];
260
+ i.Ae = e;
261
+ if (i.T & CONFIG_OPTIMISTIC && !hasActiveOverride(i)) i.T |= CONFIG_HELD_TRUTH;
262
+ }
263
+ }
264
+
265
+ /** Keyed identity-preserving deep merge through live draft proxies — the
266
+ * staged twin of the adoption walk. Reads see the pending backing (staged
267
+ * view), so consecutive landings during one hold compose; key-matched rows
268
+ * keep their raw (and so their proxy) in the slot with only changed leaves
269
+ * written; unmatched rows land wholesale. Runs inside stageLanding's
270
+ * authoritative bracket: drafts seed from committed truth, never overlays. */ function stagedApply(e, t, n) {
271
+ const i = Array.isArray(e);
272
+ if (i && Array.isArray(t)) {
273
+ const i = t.length;
274
+ if (n !== null) {
275
+ // Occurrence-aware key queues (parity with the adoption window):
276
+ // duplicate keys match per occurrence, each current row consumed once.
277
+ let o = null;
278
+ const r = e.length;
279
+ for (let t = 0; t < r; t++) {
280
+ const i = unwrapValue(e[t]);
281
+ if (!isWrappable(i)) continue;
282
+ const r = n(i);
283
+ if (r === undefined) continue;
284
+ const s = (o ??= new Map).get(r);
285
+ if (s === undefined) o.set(r, [ i ]); else s.push(i);
286
+ }
287
+ // Echo adoption: an optimistic structural add whose key the landing
288
+ // confirms must keep its raw (and so its proxy — list drivers keep the
289
+ // DOM row). Tentative rows never reach committed truth (they live in
290
+ // node overrides), so key-match the draft target's active override
291
+ // rows as a secondary pool. Committed rows queued first own their
292
+ // keys; overlay rows only extend coverage. Adopted raws enter staged
293
+ // truth; at settle the override reverts and the reveal re-seats the
294
+ // same raw.
295
+ const s = e[$TARGET]?.n;
296
+ if (s != null) {
297
+ for (const e of Reflect.ownKeys(s)) {
298
+ const t = s[e];
299
+ if (!hasActiveOverride(t)) continue;
300
+ const i = unwrapValue(unwrapOverride(t.o.Oe));
301
+ if (!isWrappable(i)) continue;
302
+ const r = n(i);
303
+ if (r === undefined) continue;
304
+ const l = (o ??= new Map).get(r);
305
+ if (l === undefined) o.set(r, [ i ]); else l.push(i);
306
+ }
307
+ }
308
+ for (let r = 0; r < i; r++) {
309
+ const i = t[r];
310
+ let s;
311
+ if (isWrappable(i) && o !== null) {
312
+ const e = n(i);
313
+ if (e !== undefined) {
314
+ for (const [t, n] of o) {
315
+ if (!sameKey(t, e)) continue;
316
+ s = n.shift();
317
+ if (n.length === 0) o.delete(t);
318
+ break;
319
+ }
320
+ }
321
+ }
322
+ if (s !== undefined) {
323
+ if (unwrapValue(e[r]) !== s) e[r] = s;
324
+ stagedApply(e[r], i, n);
325
+ } else {
326
+ const t = unwrapValue(e[r]);
327
+ if (!isEqual(t, i) && !targetsEqual(t, i)) e[r] = i;
328
+ }
329
+ }
330
+ } else {
331
+ for (let o = 0; o < i; o++) {
332
+ const i = t[o];
333
+ const r = unwrapValue(e[o]);
334
+ if (r === i) continue;
335
+ if (isWrappable(i) && isWrappable(r) && Array.isArray(i) === Array.isArray(r)) stagedApply(e[o], i, n); else if (!isEqual(r, i) && !targetsEqual(r, i)) e[o] = i;
336
+ }
337
+ }
338
+ if (e.length !== i) e.length = i;
339
+ return;
340
+ }
341
+ // Object merge; also the degenerate root-kind-change shape (arrays accept
342
+ // keyed writes/deletes, so a wholesale restatement still lands staged).
343
+ for (const o of Reflect.ownKeys(t)) {
344
+ if (i && o === "length") continue;
345
+ const r = t[o];
346
+ const s = unwrapValue(e[o]);
347
+ if (s === r) continue;
348
+ if (isWrappable(r) && isWrappable(s) && Array.isArray(r) === Array.isArray(s)) {
349
+ // Different-keyed entities never merge (tentative-channel parity):
350
+ // the incoming object replaces the slot wholesale.
351
+ if (n !== null) {
352
+ const t = n(s);
353
+ const i = n(r);
354
+ if (t !== undefined && i !== undefined && !sameKey(t, i)) {
355
+ e[o] = r;
356
+ continue;
357
+ }
358
+ }
359
+ stagedApply(e[o], r, n);
360
+ } else if (!isEqual(s, r) && !targetsEqual(s, r)) {
361
+ e[o] = r;
362
+ }
363
+ }
364
+ for (const n of Reflect.ownKeys(e)) {
365
+ if (i && n === "length" || n in t) continue;
366
+ delete e[n];
367
+ }
156
368
  }
157
369
 
158
370
  // ---- optimistic-only store machinery (moved from next/store.ts /
@@ -167,7 +379,7 @@ function createOptimisticStoreNext(e, t, n) {
167
379
  // at plain flush end. The blocked-check store-half keeps that transaction
168
380
  // from settling while the firewall is pending.
169
381
  const n = e.fam?.node;
170
- if (n?._e) globalQueue.initTransition(n._e);
382
+ if (n?.Ae) globalQueue.initTransition(n.Ae);
171
383
  const i = e.v;
172
384
  // Patch channel (override-application site): the draft IS the intended
173
385
  // visible state; prev is the view before these overrides apply. Bypasses
@@ -186,11 +398,11 @@ function createOptimisticStoreNext(e, t, n) {
186
398
  }
187
399
  const visible = (t, n) => {
188
400
  const i = e.n?.[t];
189
- return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.De) : n;
401
+ return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Oe) : n;
190
402
  };
191
403
  const visiblePresent = t => {
192
404
  const n = e.h?.[t];
193
- return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.De) : t in i;
405
+ return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Oe) : t in i;
194
406
  };
195
407
  let o = false;
196
408
  const r = Array.isArray(t);
@@ -230,95 +442,22 @@ function createOptimisticStoreNext(e, t, n) {
230
442
  // (structural ones already ride the key-set bump above).
231
443
  bumpDeep(e);
232
444
  // Discard the draft — committed raw is untouched (revert target by
233
- // construction). Register the root store for the scheduler's settle hooks
234
- // and the target for landing consumption (RUL-2).
235
- e.pb = null;
445
+ // construction) restoring any truth-staged backing this draft displaced
446
+ // (#3164 fold: ensurePB parked it so tentative writes could not pollute
447
+ // staged truth). Register the root store for the scheduler's settle hooks.
448
+ e.pb = stagedTruthPB.get(e) ?? null;
449
+ if (e.pb !== null) stagedTruthPB.delete(e);
236
450
  (e.fam.overlaid ??= new Set).add(e);
237
- GlobalQueue.An?.(e.fam.px ?? e.px);
238
- }
239
-
240
- /**
241
- * Landing consumption (RUL-2): fresh authoritative data supersedes every
242
- * tentative override in the family. Mirrors legacy clearProjectionOverride —
243
- * drop the override, clear lane/ownership, notify subscribers whose visible
244
- * value changes (reversion effects go to regular queues via the projection
245
- * write posture the caller holds).
246
- */ function consumeOverridesNext(e) {
247
- const t = e.overlaid;
248
- if (t === undefined || t.size === 0) return;
249
- runAuthoritative(() => {
250
- for (const e of t) {
251
- const drop = (e, t) => {
252
- if (!hasActiveOverride(e)) return;
253
- const n = unwrapOverride(e.o?.De);
254
- // Full legacy reset (clearOptimisticOverride parity): the landing is
255
- // authoritative NOW — fold committed into the node directly instead
256
- // of riding a transaction's commit (whose queues may be stashed with
257
- // the transaction parked; the wake would strand until it settles).
258
- ext(e).De = NOT_PENDING;
259
- e.T |= CONFIG_OPTIMISTIC;
260
- const i = e.o;
261
- if (i) {
262
- i.Nt = null;
263
- i.Be = undefined;
264
- }
265
- e.Pe = NOT_PENDING;
266
- e.be = t;
267
- if (!e.Ue || !e.Ue(n, t)) {
268
- insertSubs(e, true);
269
- schedule();
270
- }
271
- };
272
- // Landing consumes STRUCTURAL optimism only (legacy layer parity):
273
- // membership edits, array length, and the value overrides written WITH
274
- // them (a key carrying an active presence override is an add/delete —
275
- // classified BEFORE the adoption may have made the key exist in landed
276
- // data). A pure value override on a key the landing carries stays with
277
- // its owning transaction (rapid-toggle contract: a live action's edit
278
- // of an existing entity rides on top of landed truth).
279
- const t = Array.isArray(e.v);
280
- const n = e.h;
281
- let i = null;
282
- if (n !== null) {
283
- for (const e of Reflect.ownKeys(n)) {
284
- if (hasActiveOverride(n[e])) (i ??= new Set).add(e);
285
- }
286
- }
287
- const o = e.n;
288
- if (o !== null) {
289
- for (const n of Reflect.ownKeys(o)) {
290
- const r = i?.has(n) || !(n in e.v) || t && n === "length";
291
- if (!r) continue;
292
- drop(o[n], t && n === "length" ? e.v.length : e.v[n]);
293
- }
294
- }
295
- if (n !== null) {
296
- for (const t of Reflect.ownKeys(n)) drop(n[t], t in e.v);
297
- }
298
- if (e.k !== null && hasActiveOverride(e.k)) {
299
- ext(e.k).De = NOT_PENDING;
300
- e.k.T |= CONFIG_OPTIMISTIC;
301
- const t = e.k.o;
302
- if (t) {
303
- t.Nt = null;
304
- t.Be = undefined;
305
- }
306
- insertSubs(e.k, true);
307
- schedule();
308
- }
309
- // Patch channel (override-consumption site): visible truth flipped to
310
- // committed for the consumed keys — force a re-apply from the live
311
- // view so the DOM leaves the override state.
312
- if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, null, null);
313
- }
314
- t.clear();
315
- });
451
+ GlobalQueue._n?.(e.fam.px ?? e.px);
316
452
  }
317
453
 
318
454
  /** Optimistic-view composition for snapshot/deep (O1: snapshot is the CURRENT
319
455
  * view, lane values included; a fresh copy per call during pending windows —
320
- * RUL-12). Returns `src` untouched when no override is active on `t`. */ function optimisticView(e, t) {
321
- if (e.fam?.opt !== true) return t;
456
+ * RUL-12). Returns `src` untouched when no override is active on `t`.
457
+ * Authoritative-view reads (until()'s predicate) skip composition entirely:
458
+ * the predicate observes authoritative truth, never the caller's tentative
459
+ * overlay. (Write-side emission callers never run under such a compute.) */ function optimisticView(e, t) {
460
+ if (e.fam?.opt !== true || authoritativeRead()) return t;
322
461
  let n = null;
323
462
  const ensure = () => n ??= Array.isArray(t) ? [ ...t ] : {
324
463
  ...t
@@ -328,7 +467,7 @@ function createOptimisticStoreNext(e, t, n) {
328
467
  for (const e of Reflect.ownKeys(i)) {
329
468
  const n = i[e];
330
469
  if (!hasActiveOverride(n)) continue;
331
- const o = unwrapOverride(n.o?.De);
470
+ const o = unwrapOverride(n.o?.Oe);
332
471
  if (e === "length" && Array.isArray(t)) {
333
472
  if (t.length !== o) ensure().length = o;
334
473
  } else if (!isEqual(t[e], o)) ensure()[e] = o;
@@ -339,7 +478,7 @@ function createOptimisticStoreNext(e, t, n) {
339
478
  for (const e of Reflect.ownKeys(o)) {
340
479
  const i = o[e];
341
480
  if (!hasActiveOverride(i)) continue;
342
- const r = !!unwrapOverride(i.o?.De);
481
+ const r = !!unwrapOverride(i.o?.Oe);
343
482
  if (!r && e in (n ?? t)) delete ensure()[e];
344
483
  }
345
484
  }
@@ -403,11 +542,11 @@ function applyTentative(e, t, n) {
403
542
  }
404
543
  } else s = unwrapValue(e[o]);
405
544
  } else s = unwrapValue(e[o]);
406
- const c = match(s, r);
407
- if (c !== null) {
545
+ const a = match(s, r);
546
+ if (a !== null) {
408
547
  // Keep the existing row in the slot (identity preserved); recurse.
409
548
  u[o] = unwrapValue(s);
410
- l.push([ c, r ]);
549
+ l.push([ a, r ]);
411
550
  }
412
551
  }
413
552
  } else {
@@ -423,10 +562,10 @@ function applyTentative(e, t, n) {
423
562
  }
424
563
  // Flat overrides for this level (adds, removals, moved slots, length, leaf
425
564
  // values) — preserve any live user draft backing across the call.
426
- const c = e.pb;
565
+ const a = e.pb;
427
566
  e.pb = null;
428
567
  notifyOptimisticWrites(e, u);
429
- e.pb = c;
568
+ e.pb = a;
430
569
  for (let e = 0; e < l.length; e++) applyTentative(l[e][0], unwrapValue(l[e][1]), n);
431
570
  }
432
571
 
@@ -436,4 +575,4 @@ function shallowWithSymbols(e) {
436
575
  return t;
437
576
  }
438
577
 
439
- export { consumeOverridesNext, createOptimisticStoreNext, notifyOptimisticWrites, optimisticView };
578
+ export { createOptimisticStoreNext, notifyOptimisticWrites, optimisticView, transitionHoldsOptimism };
@@ -127,9 +127,9 @@ const UNSET = Symbol();
127
127
  let commitHookInstalled = false;
128
128
 
129
129
  function releaseBatch(e) {
130
- const t = e.Mt;
130
+ const t = e.wt;
131
131
  if (t === undefined) return;
132
- e.Mt = undefined;
132
+ e.wt = undefined;
133
133
  for (let e = 0; e < t.length; e++) pushLive(t[e]);
134
134
  }
135
135
 
@@ -145,8 +145,8 @@ function pushLive(e) {
145
145
  function push(e) {
146
146
  const t = activeTransition;
147
147
  if (t !== null) {
148
- let n = t.Mt;
149
- if (n === undefined) t.Mt = n = [];
148
+ let n = t.wt;
149
+ if (n === undefined) t.wt = n = [];
150
150
  n.push(e);
151
151
  return;
152
152
  }
@@ -165,8 +165,8 @@ function push(e) {
165
165
  const n = activeTransition;
166
166
  let l;
167
167
  if (n !== null) {
168
- let e = n.Mt;
169
- if (e === undefined) n.Mt = e = [];
168
+ let e = n.wt;
169
+ if (e === undefined) n.wt = e = [];
170
170
  l = e;
171
171
  } else {
172
172
  if (queue === null) queue = [];
@@ -56,6 +56,7 @@ import { wrapNext, storeSetterNext } from "./store.js";
56
56
  * driven from inside an enclosing authoritative-write scope (next-store
57
57
  * optimistic derives), and a hard `false` would clobber it mid-derive.
58
58
  */ function wrapDraft(e, t, r) {
59
+ const write = e => r ? r(e) : e();
59
60
  const i = {
60
61
  get(i, o) {
61
62
  let n;
@@ -84,31 +85,33 @@ import { wrapNext, storeSetterNext } from "./store.js";
84
85
  }
85
86
  return i;
86
87
  },
87
- set(i, o, n) {
88
+ set(r, i, o) {
88
89
  if (t && !t()) return true;
89
- const c = projectionWriteActive;
90
+ const n = projectionWriteActive;
90
91
  setWriteOverride(true);
91
92
  setProjectionWriteActive(true);
92
93
  try {
93
- e[o] = n;
94
- r?.();
94
+ write(() => {
95
+ e[i] = o;
96
+ });
95
97
  } finally {
96
98
  setWriteOverride(false);
97
- setProjectionWriteActive(c);
99
+ setProjectionWriteActive(n);
98
100
  }
99
101
  return true;
100
102
  },
101
- deleteProperty(i, o) {
103
+ deleteProperty(r, i) {
102
104
  if (t && !t()) return true;
103
- const n = projectionWriteActive;
105
+ const o = projectionWriteActive;
104
106
  setWriteOverride(true);
105
107
  setProjectionWriteActive(true);
106
108
  try {
107
- delete e[o];
108
- r?.();
109
+ write(() => {
110
+ delete e[i];
111
+ });
109
112
  } finally {
110
113
  setWriteOverride(false);
111
- setProjectionWriteActive(n);
114
+ setProjectionWriteActive(o);
112
115
  }
113
116
  return true;
114
117
  },
@@ -140,17 +143,18 @@ import { wrapNext, storeSetterNext } from "./store.js";
140
143
  if (i) i.configurable = true;
141
144
  return i;
142
145
  },
143
- defineProperty(i, o, n) {
146
+ defineProperty(r, i, o) {
144
147
  if (t && !t()) return true;
145
- const c = projectionWriteActive;
148
+ const n = projectionWriteActive;
146
149
  setWriteOverride(true);
147
150
  setProjectionWriteActive(true);
148
151
  try {
149
- Reflect.defineProperty(e, o, n);
150
- r?.();
152
+ write(() => {
153
+ Reflect.defineProperty(e, i, o);
154
+ });
151
155
  } finally {
152
156
  setWriteOverride(false);
153
- setProjectionWriteActive(c);
157
+ setProjectionWriteActive(n);
154
158
  }
155
159
  return true;
156
160
  }
@@ -211,8 +215,8 @@ function runProjectionComputedNext(e, t, r, i, o) {
211
215
  // for the whole first flight — the derive works a detached shadow of the
212
216
  // seed so draft writes cannot tear through to readers (#2988). Every commit
213
217
  // point reconciles the shadow through the normal commit path.
214
- const u = n.Ie ? JSON.parse(JSON.stringify(e[$TARGET][STORE_VALUE])) : null;
215
- const l = wrapDraft(e, () => !c || n.o?.Ee === s, o);
218
+ const u = n.Ne ? JSON.parse(JSON.stringify(e[$TARGET][STORE_VALUE])) : null;
219
+ const l = wrapDraft(e, () => !c || n.o?.Ie === s, o);
216
220
  storeSetterNext(l, o => {
217
221
  s = t(u ?? o);
218
222
  c = true;
@@ -223,10 +227,10 @@ function runProjectionComputedNext(e, t, r, i, o) {
223
227
  if (u && (t === undefined || t === u)) t = JSON.parse(JSON.stringify(u));
224
228
  if (t === o || t === undefined) return;
225
229
  const write = () => storeSetterNext(e, e => reconcileNextState(t, e, r, true), false);
226
- i ? i(write) : write();
230
+ i ? i(write, t) : write();
227
231
  };
228
232
  const l = handleAsync(n, s, commit);
229
- if (!n.Ie) commit(l);
233
+ if (!n.Ne) commit(l);
230
234
  }, false);
231
235
  return n;
232
236
  }