@solidjs/signals 2.0.0-rc.7 → 2.0.0-rc.9
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.
- package/dist/dev-shared.js +6357 -0
- package/dist/dev.attribution.js +2485 -0
- package/dist/dev.js +2851 -7027
- package/dist/observe/affects.js +129 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +641 -0
- package/dist/observe/core/action.js +177 -0
- package/dist/observe/core/async.js +846 -0
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +282 -0
- package/dist/observe/core/attribution-hooks.js +82 -0
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +2095 -0
- package/dist/observe/core/constants.js +248 -0
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1789 -0
- package/dist/observe/core/dev.js +293 -0
- package/dist/observe/core/effect.js +195 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +80 -0
- package/dist/observe/core/external.js +98 -0
- package/dist/observe/core/graph.js +155 -0
- package/dist/observe/core/heap.js +147 -0
- package/dist/observe/core/invariants.js +45 -0
- package/dist/observe/core/lanes.js +205 -0
- package/dist/observe/core/optimistic.js +585 -0
- package/dist/observe/core/owner.js +322 -0
- package/dist/observe/core/scheduler.js +1428 -0
- package/dist/observe/core/verdict.js +669 -0
- package/dist/observe/index.js +59 -0
- package/dist/observe/map.js +484 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
- package/dist/observe/store/index.js +40 -0
- package/dist/observe/store/next/optimistic.js +660 -0
- package/dist/observe/store/next/projection.js +255 -0
- package/dist/observe/store/next/reconcile.js +375 -0
- package/dist/observe/store/next/store.js +2327 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +325 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +1023 -0
- package/dist/prod/attribution.js +41 -0
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +65 -37
- package/dist/prod/core/async.js +324 -172
- package/dist/prod/core/constants.js +57 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +1003 -364
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +76 -42
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +37 -37
- package/dist/prod/core/heap.js +61 -56
- package/dist/prod/core/lanes.js +107 -63
- package/dist/prod/core/optimistic.js +374 -93
- package/dist/prod/core/owner.js +62 -46
- package/dist/prod/core/scheduler.js +653 -286
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +15 -5
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +29 -16
- package/dist/prod/store/next/optimistic.js +77 -55
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +718 -401
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +950 -130
- package/dist/types/attribution.d.ts +20 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +147 -9
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +216 -182
- package/dist/types/core/constants.d.ts +56 -0
- package/dist/types/core/core.d.ts +190 -2
- package/dist/types/core/dev.d.ts +246 -28
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +4 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +39 -5
- package/dist/types/core/scheduler.d.ts +153 -0
- package/dist/types/core/types.d.ts +32 -9
- package/dist/types/index.d.ts +10 -3
- package/dist/types/map.d.ts +2 -0
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +11 -5
- package/dist/types/store/next/target.d.ts +37 -10
- package/dist/types/store/utils.d.ts +178 -0
- package/package.json +21 -17
- package/dist/node.cjs +0 -10541
- package/dist/node.dev.cjs +0 -13724
- package/dist/types-cjs/affects.d.cts +0 -47
- package/dist/types-cjs/boundaries.d.cts +0 -175
- package/dist/types-cjs/core/action.d.cts +0 -64
- package/dist/types-cjs/core/async.d.cts +0 -23
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
- package/dist/types-cjs/core/attribution.d.cts +0 -495
- package/dist/types-cjs/core/constants.d.cts +0 -159
- package/dist/types-cjs/core/core.d.cts +0 -185
- package/dist/types-cjs/core/dev.d.cts +0 -136
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -56
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -28
- package/dist/types-cjs/core/heap.d.cts +0 -24
- package/dist/types-cjs/core/index.d.cts +0 -13
- package/dist/types-cjs/core/invariants.d.cts +0 -59
- package/dist/types-cjs/core/lanes.d.cts +0 -54
- package/dist/types-cjs/core/optimistic.d.cts +0 -6
- package/dist/types-cjs/core/owner.d.cts +0 -124
- package/dist/types-cjs/core/scheduler.d.cts +0 -236
- package/dist/types-cjs/core/types.d.cts +0 -204
- package/dist/types-cjs/core/verdict.d.cts +0 -2
- package/dist/types-cjs/index.d.cts +0 -10
- package/dist/types-cjs/map.d.cts +0 -69
- package/dist/types-cjs/package.json +0 -3
- package/dist/types-cjs/store/index.d.cts +0 -18
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
- package/dist/types-cjs/store/next/store.d.cts +0 -121
- package/dist/types-cjs/store/next/target.d.cts +0 -166
- package/dist/types-cjs/store/store.d.cts +0 -143
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
package/dist/prod/signals.js
CHANGED
|
@@ -4,7 +4,7 @@ import { computed, optimisticComputed, setSignal, optimisticSignal, runWithOwner
|
|
|
4
4
|
|
|
5
5
|
import { cleanup, createRoot, getOwner, dispose, getObserver } from "./core/owner.js";
|
|
6
6
|
|
|
7
|
-
import { globalQueue, Queue, entangleConfirmingTransitions, activeTransition } from "./core/scheduler.js";
|
|
7
|
+
import { globalQueue, dirtyQueue, Queue, entangleConfirmingTransitions, activeTransition } from "./core/scheduler.js";
|
|
8
8
|
|
|
9
9
|
import { CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_DIRECT_COMMIT, CONFIG_AUTHORITATIVE_READ, CONFIG_FRESH_READ } from "./core/constants.js";
|
|
10
10
|
|
|
@@ -281,10 +281,7 @@ function createMemo(e, t) {
|
|
|
281
281
|
n = r;
|
|
282
282
|
dispose(t);
|
|
283
283
|
}, e.error, {
|
|
284
|
-
...
|
|
285
|
-
...t,
|
|
286
|
-
name: t?.name ?? "effect"
|
|
287
|
-
} : t,
|
|
284
|
+
...t,
|
|
288
285
|
user: true,
|
|
289
286
|
defer: true
|
|
290
287
|
});
|
|
@@ -330,7 +327,7 @@ function createMemo(e, t) {
|
|
|
330
327
|
// route through the inherited queue.
|
|
331
328
|
const i = getOwner();
|
|
332
329
|
const o = new MicrotaskQueue;
|
|
333
|
-
o.
|
|
330
|
+
o._parent = i.C;
|
|
334
331
|
// notify() forwards up the normal chain
|
|
335
332
|
i.C = o;
|
|
336
333
|
// A user effect rather than a bare computed: computeds are pull-based and
|
|
@@ -352,7 +349,7 @@ function createMemo(e, t) {
|
|
|
352
349
|
// stale mainline value and resolves with old data.
|
|
353
350
|
{
|
|
354
351
|
user: true,
|
|
355
|
-
|
|
352
|
+
kt: CONFIG_DIRECT_COMMIT
|
|
356
353
|
});
|
|
357
354
|
});
|
|
358
355
|
});
|
|
@@ -445,7 +442,7 @@ function createMemo(e, t) {
|
|
|
445
442
|
if (i === null) {
|
|
446
443
|
i = getOwner();
|
|
447
444
|
const e = new MicrotaskQueue;
|
|
448
|
-
e.
|
|
445
|
+
e._parent = i.C;
|
|
449
446
|
i.C = e;
|
|
450
447
|
}
|
|
451
448
|
return read(t);
|
|
@@ -457,7 +454,7 @@ function createMemo(e, t) {
|
|
|
457
454
|
dispose(i);
|
|
458
455
|
}, {
|
|
459
456
|
user: true,
|
|
460
|
-
|
|
457
|
+
kt: CONFIG_DIRECT_COMMIT | CONFIG_AUTHORITATIVE_READ | CONFIG_FRESH_READ
|
|
461
458
|
});
|
|
462
459
|
make();
|
|
463
460
|
});
|
|
@@ -508,12 +505,20 @@ function createMemo(e, t) {
|
|
|
508
505
|
*
|
|
509
506
|
* Must be called *outside* a tracking scope.
|
|
510
507
|
*
|
|
508
|
+
* Inside an action, call it from a step: after an `await`, put a bare `yield`
|
|
509
|
+
* before `yield until(...)`. The runtime cannot hook an async generator's
|
|
510
|
+
* `await` continuation, so the `until(...)` expression — which CREATES the
|
|
511
|
+
* predicate's reader — would otherwise run outside the transaction; created
|
|
512
|
+
* there it is born held (A29) and replays only at the commit its own promise
|
|
513
|
+
* holds open (#3482). See {@link action}.
|
|
514
|
+
*
|
|
511
515
|
* @example
|
|
512
516
|
* ```ts
|
|
513
517
|
* const send = action(async function* (text: string) {
|
|
514
518
|
* const clientId = crypto.randomUUID();
|
|
515
519
|
* setMessages(m => { m.push({ clientId, text, pending: true }); }); // optimistic
|
|
516
520
|
* await socket.send({ clientId, text }); // fire-and-forget transport
|
|
521
|
+
* yield; // re-enter the transaction after the await
|
|
517
522
|
* // Hold until the live source echoes the write (authoritative view —
|
|
518
523
|
* // the optimistic row above cannot satisfy this):
|
|
519
524
|
* yield until(() => messages.some(m => m.clientId === clientId), { timeout: 10_000 });
|
|
@@ -540,10 +545,10 @@ function createMemo(e, t) {
|
|
|
540
545
|
// microtask so the promise can settle while the transaction the caller
|
|
541
546
|
// yielded it into is still open — that transaction being open is the
|
|
542
547
|
// entire point of the hold.
|
|
543
|
-
const
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
548
|
+
const u = getOwner();
|
|
549
|
+
const s = new MicrotaskQueue;
|
|
550
|
+
s._parent = u.C;
|
|
551
|
+
u.C = s;
|
|
547
552
|
let f;
|
|
548
553
|
let a;
|
|
549
554
|
const settle = e => {
|
|
@@ -572,7 +577,7 @@ function createMemo(e, t) {
|
|
|
572
577
|
// the hold itself is keeping uncommitted.
|
|
573
578
|
{
|
|
574
579
|
user: true,
|
|
575
|
-
|
|
580
|
+
kt: CONFIG_AUTHORITATIVE_READ | CONFIG_DIRECT_COMMIT
|
|
576
581
|
});
|
|
577
582
|
if (t?.timeout !== undefined) f = setTimeout(() => settle(() => i(new TimeoutError)), t.timeout);
|
|
578
583
|
if (o !== undefined) {
|
|
@@ -694,12 +699,20 @@ function createOptimistic(e, t) {
|
|
|
694
699
|
* on owner disposal
|
|
695
700
|
*/ function onSettled(e) {
|
|
696
701
|
const t = getOwner();
|
|
697
|
-
t && !(t.T & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), undefined) : globalQueue.enqueue(EFFECT_USER, ()
|
|
702
|
+
t && !(t.T & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), undefined) : globalQueue.enqueue(EFFECT_USER, function fire() {
|
|
703
|
+
// Settled means derived. A settle that reverts optimism (or replays
|
|
704
|
+
// gated reads) only enqueues the affected subscribers; the pass after
|
|
705
|
+
// the commit re-derives them. Fired in the commit pass, the callback
|
|
706
|
+
// read the optimistic source already reverted beside a sync memo of it
|
|
707
|
+
// still holding the optimistic value — reads do not pull (#3411). Fall
|
|
708
|
+
// to the next pass while the heap has work; `run` swapped the queue,
|
|
709
|
+
// so this lands there, and `enqueue` keeps the drain alive.
|
|
710
|
+
if (dirtyQueue.EE >= dirtyQueue.et) return globalQueue.enqueue(EFFECT_USER, fire);
|
|
698
711
|
// Unowned, out-of-band fire (no owner, or a children-forbidden one this
|
|
699
712
|
// one-shot must not bind to): a returned cleanup has no lifecycle to
|
|
700
713
|
// attach to. Reject it in dev; in production the return is simply
|
|
701
714
|
// dropped — never bound to an unrelated owner or run eagerly.
|
|
702
|
-
|
|
715
|
+
e();
|
|
703
716
|
});
|
|
704
717
|
}
|
|
705
718
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, STATUS_UNINITIALIZED, NOT_PENDING, CONFIG_OPTIMISTIC, CONFIG_HELD_TRUTH, unwrapOverride } from "../../core/constants.js";
|
|
2
2
|
|
|
3
|
-
import { computed, setSignal, isEqual } from "../../core/core.js";
|
|
3
|
+
import { computed, hasActiveOverride, setSignal, isEqual, visibleOverride } from "../../core/core.js";
|
|
4
4
|
|
|
5
5
|
import { getOwner } from "../../core/owner.js";
|
|
6
6
|
|
|
@@ -18,11 +18,11 @@ import { $TARGET, markRawIngest, setNextOptimisticViewResolver, isWrappable, raw
|
|
|
18
18
|
|
|
19
19
|
import { runProjectionComputedNext } from "./projection.js";
|
|
20
20
|
|
|
21
|
-
import { wrapNext, runAuthoritative, storeSetterNext,
|
|
21
|
+
import { wrapNext, runAuthoritative, storeSetterNext, unwrapValue, targetsEqual, heldMaskView, getNode, getHasNode, getKeySetNode, bumpDeep, stagedTruthPB, authoritativeRead } from "./store.js";
|
|
22
22
|
|
|
23
23
|
import { sameKey } from "./reconcile.js";
|
|
24
24
|
|
|
25
|
-
import { setOptHooks } from "./target.js";
|
|
25
|
+
import { setOptHooks, $OWNER, lookupTarget } from "./target.js";
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Store rewrite — optimistic stores (§3/§7, RUL-3): no store-side layer, no
|
|
@@ -55,7 +55,7 @@ import { setOptHooks } from "./target.js";
|
|
|
55
55
|
* staged values to converge (normal speculation). Resolves merges first:
|
|
56
56
|
* merge unions optimistic nodes/stores into the target. */ function transitionHoldsOptimism(e) {
|
|
57
57
|
const t = currentTransition(e);
|
|
58
|
-
return t.
|
|
58
|
+
return t.Tt !== true && (t.rt.length !== 0 || t.dn.size !== 0);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
let blockedInstalled = false;
|
|
@@ -72,12 +72,15 @@ function installNextBlockedHalf() {
|
|
|
72
72
|
applyTentative: applyTentative,
|
|
73
73
|
retainsOptimism: transitionHoldsOptimism
|
|
74
74
|
});
|
|
75
|
-
|
|
75
|
+
// The affects() declaration walk is a WRITER channel (A28 (5)): tagging a
|
|
76
|
+
// parent covers the record as the writer sees it, this tick's optimistic
|
|
77
|
+
// writes included — the draft view, not the reader view.
|
|
78
|
+
setNextOptimisticViewResolver((e, t) => optimisticView(e, t, true));
|
|
76
79
|
// Scheduler flush tails call _clearOptimisticStores whenever tracked
|
|
77
80
|
// stores exist; next has no layer to clear — reverts are engine-native —
|
|
78
81
|
// so the hook only empties the batch set.
|
|
79
|
-
if (!GlobalQueue.
|
|
80
|
-
GlobalQueue.
|
|
82
|
+
if (!GlobalQueue.Cn) {
|
|
83
|
+
GlobalQueue.Cn = e => {
|
|
81
84
|
for (const t of e) {
|
|
82
85
|
const e = t?.[$TARGET];
|
|
83
86
|
const n = e?.fam?.overlaid;
|
|
@@ -96,9 +99,9 @@ function installNextBlockedHalf() {
|
|
|
96
99
|
e.clear();
|
|
97
100
|
};
|
|
98
101
|
}
|
|
99
|
-
const e = GlobalQueue.
|
|
100
|
-
GlobalQueue.
|
|
101
|
-
for (const e of t.
|
|
102
|
+
const e = GlobalQueue.Nn;
|
|
103
|
+
GlobalQueue.Nn = t => {
|
|
104
|
+
for (const e of t.dn) {
|
|
102
105
|
const n = e?.[$TARGET];
|
|
103
106
|
const i = n?.fam;
|
|
104
107
|
const r = i?.node;
|
|
@@ -128,10 +131,10 @@ function familyHasLiveOverrides(e) {
|
|
|
128
131
|
if (t === null) continue;
|
|
129
132
|
for (const e of Reflect.ownKeys(t)) {
|
|
130
133
|
const n = t[e];
|
|
131
|
-
if (n.o?.
|
|
134
|
+
if (n.o?.Ce !== undefined && n.o?.Ce !== NOT_PENDING) return true;
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
|
-
if (e.k !== null && e.k.o?.
|
|
137
|
+
if (e.k !== null && e.k.o?.Ce !== undefined && e.k.o?.Ce !== NOT_PENDING) return true;
|
|
135
138
|
}
|
|
136
139
|
t.clear();
|
|
137
140
|
// nothing live — drop the bookkeeping
|
|
@@ -178,7 +181,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
178
181
|
if (e == null) return;
|
|
179
182
|
let t = liveTransition(e);
|
|
180
183
|
if (t === null) s.ft = t = createTransition();
|
|
181
|
-
s.node.
|
|
184
|
+
s.node.Ge = t;
|
|
182
185
|
globalQueue.initTransition(t);
|
|
183
186
|
};
|
|
184
187
|
// Landing router (#3164 fold ruling): while a transaction retains
|
|
@@ -223,8 +226,8 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
223
226
|
// (#2933: the loading rail is transaction-invisible); a sync run clears
|
|
224
227
|
// the declaration.
|
|
225
228
|
const declareFlight = e => {
|
|
226
|
-
if (e.o?.
|
|
227
|
-
if (!e.
|
|
229
|
+
if (e.o?.Re == null) {
|
|
230
|
+
if (!e.ge) s.ft = null;
|
|
228
231
|
return;
|
|
229
232
|
}
|
|
230
233
|
// First flight (#3146 carve-out): nothing has ever committed, so there
|
|
@@ -236,13 +239,13 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
236
239
|
// fallback never showed and the whole page stayed blank. The loading
|
|
237
240
|
// window (#2933) already declares nothing for the same reason; once
|
|
238
241
|
// the first truth lands, every refetch flight declares as before.
|
|
239
|
-
if (e.
|
|
242
|
+
if (e.ge || e.S & STATUS_UNINITIALIZED) return;
|
|
240
243
|
let t = activeTransition;
|
|
241
244
|
if (t === null) globalQueue.initTransition(t = createTransition());
|
|
242
245
|
s.ft = t;
|
|
243
|
-
e.
|
|
244
|
-
let n = t.
|
|
245
|
-
if (n === undefined) t.
|
|
246
|
+
e.Ge = t;
|
|
247
|
+
let n = t.oe.get(e);
|
|
248
|
+
if (n === undefined) t.oe.set(e, n = new Set);
|
|
246
249
|
n.add(e);
|
|
247
250
|
};
|
|
248
251
|
let n;
|
|
@@ -274,8 +277,8 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
274
277
|
|
|
275
278
|
/** Resolve a retained transition through its merge chain (`_done` holds the
|
|
276
279
|
* merge target while merged, `true` once settled). Null = dead. */ function liveTransition(e) {
|
|
277
|
-
while (typeof e.
|
|
278
|
-
return e.
|
|
280
|
+
while (typeof e.Tt === "object") e = e.Tt;
|
|
281
|
+
return e.Tt === true ? null : e;
|
|
279
282
|
}
|
|
280
283
|
|
|
281
284
|
/** The transaction truth landings fold into: the first live member of the
|
|
@@ -320,10 +323,10 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
320
323
|
* stay unarmed — the override is their display and its revert their
|
|
321
324
|
* notification, A17). */ function runFolded(e, t) {
|
|
322
325
|
runAsTransitionBatch(e, t);
|
|
323
|
-
const n = e.
|
|
326
|
+
const n = e.Ot;
|
|
324
327
|
for (let t = 0; t < n.length; t++) {
|
|
325
328
|
const i = n[t];
|
|
326
|
-
i.
|
|
329
|
+
i.Ge = e;
|
|
327
330
|
if (i.T & CONFIG_OPTIMISTIC && !hasActiveOverride(i)) i.T |= CONFIG_HELD_TRUTH;
|
|
328
331
|
}
|
|
329
332
|
}
|
|
@@ -363,7 +366,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
363
366
|
for (const e of Reflect.ownKeys(s)) {
|
|
364
367
|
const t = s[e];
|
|
365
368
|
if (!hasActiveOverride(t)) continue;
|
|
366
|
-
const i = unwrapValue(unwrapOverride(t.o.
|
|
369
|
+
const i = unwrapValue(unwrapOverride(t.o.Ce));
|
|
367
370
|
if (!isWrappable(i)) continue;
|
|
368
371
|
const o = n(i);
|
|
369
372
|
if (o === undefined) continue;
|
|
@@ -407,7 +410,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
407
410
|
// Object merge; also the degenerate root-kind-change shape (arrays accept
|
|
408
411
|
// keyed writes/deletes, so a wholesale restatement still lands staged).
|
|
409
412
|
for (const r of Reflect.ownKeys(t)) {
|
|
410
|
-
if (i && r === "length") continue;
|
|
413
|
+
if (i && r === "length" || r === $OWNER) continue;
|
|
411
414
|
const o = t[r];
|
|
412
415
|
const s = unwrapValue(e[r]);
|
|
413
416
|
if (s === o) continue;
|
|
@@ -428,7 +431,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
428
431
|
}
|
|
429
432
|
}
|
|
430
433
|
for (const n of Reflect.ownKeys(e)) {
|
|
431
|
-
if (i && n === "length" || n in t) continue;
|
|
434
|
+
if (i && n === "length" || n === $OWNER || n in t) continue;
|
|
432
435
|
delete e[n];
|
|
433
436
|
}
|
|
434
437
|
}
|
|
@@ -451,19 +454,33 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
451
454
|
const e = liveTransition(n);
|
|
452
455
|
if (e !== null) globalQueue.initTransition(e);
|
|
453
456
|
}
|
|
454
|
-
|
|
455
|
-
|
|
457
|
+
// The write is judged against what ordinary readers SEE, not against the
|
|
458
|
+
// committed backing slot: under an adoption hold (#3074) `t.v` is already
|
|
459
|
+
// the truth a live transaction is holding, and an optimistic write equal
|
|
460
|
+
// to it compared as a no-op — no override, no lane, and the screen kept
|
|
461
|
+
// the pre-hold value until the transaction committed (#3330's store
|
|
462
|
+
// twin: `s.v = 1` after a `yield` while the derive had already staged
|
|
463
|
+
// `v: 1`). Signal parity: the override reveals on its lane now, with its
|
|
464
|
+
// derivations; the held truth reveals on the transaction's schedule.
|
|
465
|
+
const i = heldMaskView(e) ?? e.v;
|
|
466
|
+
// Compare RAWS on both sides (`nv` below is unwrapped already). A chained
|
|
467
|
+
// target's `old` is the inner store's proxy, whose reads hand back inner
|
|
468
|
+
// child PROXIES; the draft's clone holds the inner raws. Comparing the two
|
|
469
|
+
// as-is marked every untouched row changed, and the overlay then served
|
|
470
|
+
// each from an override as a fresh non-chained target — row identities
|
|
471
|
+
// churned for the life of the action and snapped back at settle (#3323).
|
|
472
|
+
const visible = (t, n) => {
|
|
456
473
|
const i = e.n?.[t];
|
|
457
|
-
return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.
|
|
474
|
+
return unwrapValue(i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Ce) : n);
|
|
458
475
|
};
|
|
459
476
|
const visiblePresent = t => {
|
|
460
477
|
const n = e.h?.[t];
|
|
461
|
-
return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.
|
|
478
|
+
return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Ce) : t in i;
|
|
462
479
|
};
|
|
463
480
|
let r = false;
|
|
464
481
|
const o = Array.isArray(t);
|
|
465
482
|
for (const n of Reflect.ownKeys(t)) {
|
|
466
|
-
if (o && n === "length") continue;
|
|
483
|
+
if (o && n === "length" || n === $OWNER) continue;
|
|
467
484
|
const s = unwrapValue(t[n]);
|
|
468
485
|
if (!visiblePresent(n)) {
|
|
469
486
|
// Optimistic add: value node + presence node + membership bump.
|
|
@@ -479,7 +496,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
479
496
|
}
|
|
480
497
|
}
|
|
481
498
|
for (const n of Reflect.ownKeys(i)) {
|
|
482
|
-
if (o && n === "length") continue;
|
|
499
|
+
if (o && n === "length" || n === $OWNER) continue;
|
|
483
500
|
if (n in t || !visiblePresent(n)) continue;
|
|
484
501
|
// Optimistic delete: node reads undefined, presence flips, membership bumps.
|
|
485
502
|
setSignal(getNode(e, n, i[n]), () => undefined);
|
|
@@ -504,7 +521,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
504
521
|
e.pb = stagedTruthPB.get(e) ?? null;
|
|
505
522
|
if (e.pb !== null) stagedTruthPB.delete(e);
|
|
506
523
|
(e.fam.overlaid ??= new Set).add(e);
|
|
507
|
-
GlobalQueue.
|
|
524
|
+
GlobalQueue.Dn?.(e.fam.px ?? e.px);
|
|
508
525
|
}
|
|
509
526
|
|
|
510
527
|
/** Optimistic-view composition for snapshot/deep (O1: snapshot is the CURRENT
|
|
@@ -512,39 +529,43 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
512
529
|
* RUL-12). Returns `src` untouched when no override is active on `t`.
|
|
513
530
|
* Authoritative-view reads (until()'s predicate) skip composition entirely:
|
|
514
531
|
* the predicate observes authoritative truth, never the caller's tentative
|
|
515
|
-
* overlay. (Write-side emission callers never run under such a compute.) */ function optimisticView(e, t) {
|
|
532
|
+
* overlay. (Write-side emission callers never run under such a compute.) */ function optimisticView(e, t, n = false) {
|
|
516
533
|
if (e.fam?.opt !== true || authoritativeRead()) return t;
|
|
517
|
-
let
|
|
518
|
-
const ensure = () =>
|
|
534
|
+
let i = null;
|
|
535
|
+
const ensure = () => i ??= Array.isArray(t) ? [ ...t ] : {
|
|
519
536
|
...t
|
|
520
537
|
};
|
|
521
|
-
const
|
|
522
|
-
if (i !== null) {
|
|
523
|
-
for (const e of Reflect.ownKeys(i)) {
|
|
524
|
-
const n = i[e];
|
|
525
|
-
if (!hasActiveOverride(n)) continue;
|
|
526
|
-
const r = unwrapOverride(n.o?.Pe);
|
|
527
|
-
if (e === "length" && Array.isArray(t)) {
|
|
528
|
-
if (t.length !== r) ensure().length = r;
|
|
529
|
-
} else if (!isEqual(t[e], r)) ensure()[e] = r;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const r = e.h;
|
|
538
|
+
const r = e.n;
|
|
533
539
|
if (r !== null) {
|
|
534
540
|
for (const e of Reflect.ownKeys(r)) {
|
|
535
541
|
const i = r[e];
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
542
|
+
// A28 (5): readers see an optimistic write once a flush carried it;
|
|
543
|
+
// the draft (writer channel) composes on it now.
|
|
544
|
+
if (!(n ? hasActiveOverride(i) : visibleOverride(i))) continue;
|
|
545
|
+
const o = unwrapOverride(i.o?.Ce);
|
|
546
|
+
if (e === "length" && Array.isArray(t)) {
|
|
547
|
+
if (t.length !== o) ensure().length = o;
|
|
548
|
+
} else if (!isEqual(t[e], o)) ensure()[e] = o;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const o = e.h;
|
|
552
|
+
if (o !== null) {
|
|
553
|
+
for (const e of Reflect.ownKeys(o)) {
|
|
554
|
+
const r = o[e];
|
|
555
|
+
// A28 (5): readers see an optimistic write once a flush carried it;
|
|
556
|
+
// the draft (writer channel) composes on it now.
|
|
557
|
+
if (!(n ? hasActiveOverride(r) : visibleOverride(r))) continue;
|
|
558
|
+
const s = !!unwrapOverride(r.o?.Ce);
|
|
559
|
+
if (!s && e in (i ?? t)) delete ensure()[e];
|
|
539
560
|
}
|
|
540
561
|
}
|
|
541
|
-
return
|
|
562
|
+
return i ?? t;
|
|
542
563
|
}
|
|
543
564
|
|
|
544
565
|
function applyTentative(e, t, n) {
|
|
545
566
|
const i = e.pb ?? e.v;
|
|
546
567
|
const r = optimisticView(e, i);
|
|
547
|
-
const o = e.fam
|
|
568
|
+
const o = e.fam;
|
|
548
569
|
const s = Array.isArray(t);
|
|
549
570
|
if (Array.isArray(r) !== s) return;
|
|
550
571
|
// kind change at root: flat overrides below
|
|
@@ -552,7 +573,7 @@ function applyTentative(e, t, n) {
|
|
|
552
573
|
let u;
|
|
553
574
|
if (s) u = [ ...t ]; else {
|
|
554
575
|
u = {};
|
|
555
|
-
for (const e of Reflect.ownKeys(t)) u[e] = t[e];
|
|
576
|
+
for (const e of Reflect.ownKeys(t)) if (e !== $OWNER) u[e] = t[e];
|
|
556
577
|
}
|
|
557
578
|
const match = (e, t) => {
|
|
558
579
|
if (!isWrappable(e) || !isWrappable(t)) return null;
|
|
@@ -565,7 +586,7 @@ function applyTentative(e, t, n) {
|
|
|
565
586
|
// channel — NaN keys are self-equal.
|
|
566
587
|
if (i !== undefined && r !== undefined && !sameKey(i, r)) return null;
|
|
567
588
|
}
|
|
568
|
-
return
|
|
589
|
+
return lookupTarget(unwrapValue(e), o) ?? null;
|
|
569
590
|
};
|
|
570
591
|
if (s) {
|
|
571
592
|
const e = r;
|
|
@@ -611,6 +632,7 @@ function applyTentative(e, t, n) {
|
|
|
611
632
|
}
|
|
612
633
|
} else {
|
|
613
634
|
for (const e of Reflect.ownKeys(t)) {
|
|
635
|
+
if (e === $OWNER) continue;
|
|
614
636
|
const n = unwrapValue(r[e]);
|
|
615
637
|
const i = t[e];
|
|
616
638
|
const o = match(n, i);
|
|
@@ -55,22 +55,23 @@ import { storeSetterNext, wrapNext } from "./store.js";
|
|
|
55
55
|
* Save/restore projectionWriteActive, never hard-reset: the draft can be
|
|
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
|
-
*/ function wrapDraft(e, t, r) {
|
|
58
|
+
*/ function wrapDraft(e, t, r, i) {
|
|
59
59
|
const write = e => r ? r(e) : e();
|
|
60
|
-
const
|
|
61
|
-
get(
|
|
62
|
-
let
|
|
63
|
-
const
|
|
60
|
+
const o = {
|
|
61
|
+
get(o, n) {
|
|
62
|
+
let c;
|
|
63
|
+
const s = projectionWriteActive;
|
|
64
64
|
setWriteOverride(true);
|
|
65
65
|
setProjectionWriteActive(true);
|
|
66
66
|
try {
|
|
67
|
-
|
|
67
|
+
c = e[n];
|
|
68
68
|
} finally {
|
|
69
69
|
setWriteOverride(false);
|
|
70
|
-
setProjectionWriteActive(
|
|
70
|
+
setProjectionWriteActive(s);
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
// A shallow store's leaves are raw by contract (#3498): no draft proxy
|
|
73
|
+
// over them, so identity holds and a frozen leaf is never trapped.
|
|
74
|
+
return !i && typeof c === "object" && c !== null && n !== $TARGET ? wrapDraft(c, t, r) : c;
|
|
74
75
|
},
|
|
75
76
|
has(t, r) {
|
|
76
77
|
let i;
|
|
@@ -160,7 +161,7 @@ import { storeSetterNext, wrapNext } from "./store.js";
|
|
|
160
161
|
}
|
|
161
162
|
};
|
|
162
163
|
// Matching-kind dummy so Array.isArray(draft) answers like the store.
|
|
163
|
-
return new Proxy(Array.isArray(e) ? [] : {},
|
|
164
|
+
return new Proxy(Array.isArray(e) ? [] : {}, o);
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
function createProjectionNextInternal(e, t, r) {
|
|
@@ -207,30 +208,40 @@ function createProjectionNext(e, t, r) {
|
|
|
207
208
|
} ];
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
// A detached copy of projection state: the root container alone for a shallow
|
|
212
|
+
// store — its leaves are raw references by contract and stay so (#3498) — the
|
|
213
|
+
// whole tree otherwise.
|
|
214
|
+
function cloneState(e, t) {
|
|
215
|
+
return t ? Array.isArray(e) ? e.slice() : {
|
|
216
|
+
...e
|
|
217
|
+
} : JSON.parse(JSON.stringify(e));
|
|
218
|
+
}
|
|
219
|
+
|
|
210
220
|
function runProjectionComputedNext(e, t, r, i, o) {
|
|
211
221
|
const n = getOwner();
|
|
212
|
-
|
|
213
|
-
let s;
|
|
222
|
+
const c = e[$TARGET];
|
|
223
|
+
let s = false;
|
|
224
|
+
let u;
|
|
214
225
|
// Open loading window (seedLoadingValue): the observable store IS commit #0
|
|
215
226
|
// for the whole first flight — the derive works a detached shadow of the
|
|
216
227
|
// seed so draft writes cannot tear through to readers (#2988). Every commit
|
|
217
228
|
// point reconciles the shadow through the normal commit path.
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
storeSetterNext(
|
|
221
|
-
|
|
222
|
-
|
|
229
|
+
const l = n.ge ? cloneState(c[STORE_VALUE], c.s) : null;
|
|
230
|
+
const a = wrapDraft(e, () => !s || n.o?.Re === u, o, c.s);
|
|
231
|
+
storeSetterNext(a, o => {
|
|
232
|
+
u = t(l ?? o);
|
|
233
|
+
s = true;
|
|
223
234
|
const commit = t => {
|
|
224
235
|
// Shadow run: commit a detached snapshot, never the shadow itself
|
|
225
236
|
// (adoption takes the value by identity — handing it the live shadow
|
|
226
237
|
// would fuse the draft to the observable store).
|
|
227
|
-
if (
|
|
238
|
+
if (l && (t === undefined || t === l)) t = cloneState(l, c.s);
|
|
228
239
|
if (t === o || t === undefined) return;
|
|
229
240
|
const write = () => storeSetterNext(e, e => reconcileNextState(t, e, r, true), false);
|
|
230
241
|
i ? i(write, t) : write();
|
|
231
242
|
};
|
|
232
|
-
const
|
|
233
|
-
if (!n.
|
|
243
|
+
const a = handleAsync(n, u, commit);
|
|
244
|
+
if (!n.ge) commit(a);
|
|
234
245
|
}, false);
|
|
235
246
|
return n;
|
|
236
247
|
}
|