@solidjs/signals 2.0.0-rc.8 → 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 +1399 -285
- package/dist/dev.attribution.js +459 -307
- package/dist/dev.js +1864 -435
- package/dist/observe/affects.js +3 -1
- package/dist/observe/attribution.js +7 -1
- package/dist/observe/boundaries.js +209 -154
- package/dist/observe/core/action.js +18 -8
- package/dist/observe/core/async.js +220 -110
- 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 +23 -1
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +262 -485
- package/dist/observe/core/constants.js +34 -1
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +867 -367
- package/dist/observe/core/dev.js +78 -17
- package/dist/observe/core/effect.js +67 -51
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/external.js +4 -4
- package/dist/observe/core/graph.js +37 -37
- package/dist/observe/core/heap.js +45 -45
- package/dist/observe/core/invariants.js +2 -0
- package/dist/observe/core/lanes.js +86 -49
- package/dist/observe/core/optimistic.js +242 -95
- package/dist/observe/core/owner.js +98 -84
- package/dist/observe/core/scheduler.js +543 -305
- package/dist/observe/core/verdict.js +247 -129
- package/dist/observe/index.js +7 -3
- package/dist/observe/map.js +126 -124
- package/dist/observe/signals.js +33 -17
- package/dist/observe/store/index.js +2 -0
- package/dist/observe/store/next/optimistic.js +141 -132
- package/dist/observe/store/next/projection.js +34 -21
- package/dist/observe/store/next/reconcile.js +58 -56
- package/dist/observe/store/next/store.js +260 -146
- package/dist/observe/store/store.js +5 -3
- package/dist/observe/store/utils.js +948 -135
- package/dist/prod/attribution.js +26 -17
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +16 -8
- package/dist/prod/core/async.js +251 -143
- package/dist/prod/core/constants.js +34 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +843 -347
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +48 -34
- 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 +45 -45
- package/dist/prod/core/lanes.js +90 -53
- package/dist/prod/core/optimistic.js +247 -100
- package/dist/prod/core/owner.js +57 -45
- package/dist/prod/core/scheduler.js +543 -305
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +112 -112
- package/dist/prod/signals.js +28 -12
- package/dist/prod/store/next/optimistic.js +135 -128
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/store.js +325 -252
- package/dist/prod/store/store.js +2 -2
- package/dist/prod/store/utils.js +946 -135
- package/dist/types/attribution.d.ts +7 -2
- package/dist/types/attribution.prod.d.ts +10 -1
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- 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 +28 -3
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +51 -172
- package/dist/types/core/constants.d.ts +33 -0
- package/dist/types/core/core.d.ts +186 -5
- package/dist/types/core/dev.d.ts +129 -9
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +3 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +31 -4
- package/dist/types/core/scheduler.d.ts +95 -2
- package/dist/types/core/types.d.ts +3 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -1
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +6 -5
- package/dist/types/store/next/target.d.ts +1 -1
- package/dist/types/store/utils.d.ts +177 -6
- package/package.json +1 -1
|
@@ -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,7 +18,7 @@ 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
|
|
|
@@ -55,7 +55,7 @@ import { setOptHooks, $OWNER, lookupTarget } 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,24 +99,24 @@ 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
|
-
const
|
|
107
|
+
const r = i?.node;
|
|
105
108
|
// The hold exists to keep optimistic state alive until the store's own
|
|
106
109
|
// truth lands (#2951). Once the family carries NO live overrides (a
|
|
107
110
|
// landing consumed them, or they never existed), a pending firewall is
|
|
108
111
|
// no reason to park the transaction — blocking then leaks it forever
|
|
109
112
|
// when the in-flight question is never answered (undisposed fixtures).
|
|
110
|
-
if (
|
|
113
|
+
if (r == null || !(r.S & STATUS_PENDING)) continue;
|
|
111
114
|
// Ownership is declared (#3146): only the flight's OWN transaction
|
|
112
115
|
// parks on the flight (the #2951 anchor routed the bare write there).
|
|
113
116
|
// A transaction that merely brushed the store never waits for truth
|
|
114
117
|
// it does not carry.
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
118
|
+
const o = i.ft != null ? liveTransition(i.ft) : null;
|
|
119
|
+
if (o !== null && o !== currentTransition(t)) continue;
|
|
117
120
|
if (familyHasLiveOverrides(i)) return true;
|
|
118
121
|
}
|
|
119
122
|
return e(t);
|
|
@@ -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
|
|
@@ -144,23 +147,23 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
144
147
|
installOptimisticEngine();
|
|
145
148
|
installNextBlockedHalf();
|
|
146
149
|
const i = typeof e === "function";
|
|
147
|
-
const
|
|
148
|
-
const
|
|
150
|
+
const r = i ? n : t;
|
|
151
|
+
const o = i ? t : e;
|
|
149
152
|
const s = {
|
|
150
153
|
map: new WeakMap,
|
|
151
154
|
node: null,
|
|
152
|
-
shallow: !!
|
|
155
|
+
shallow: !!r?.shallow,
|
|
153
156
|
opt: true
|
|
154
157
|
};
|
|
155
|
-
const l = wrapNext(
|
|
158
|
+
const l = wrapNext(o, null, null, s);
|
|
156
159
|
s.px = l;
|
|
157
160
|
// Same key resolution the projection channels use ("id" default) — replay's
|
|
158
161
|
// satisfaction rule reads it off the family.
|
|
159
|
-
const u =
|
|
162
|
+
const u = r?.key === undefined ? "id" : r.key;
|
|
160
163
|
s.key = typeof u === "function" ? u : u === null ? null : e => isWrappable(e) ? e[u] : undefined;
|
|
161
164
|
if (s.shallow) {
|
|
162
165
|
l[$TARGET].s = true;
|
|
163
|
-
markRawIngest(
|
|
166
|
+
markRawIngest(o);
|
|
164
167
|
}
|
|
165
168
|
if (i) {
|
|
166
169
|
const t = e;
|
|
@@ -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,23 +239,23 @@ 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;
|
|
249
|
-
if (
|
|
252
|
+
if (r?.seedLoadingValue) n = {
|
|
250
253
|
loadingValue: undefined
|
|
251
254
|
};
|
|
252
255
|
const i = computed(() => {
|
|
253
256
|
const e = getOwner();
|
|
254
257
|
try {
|
|
255
|
-
runAuthoritative(() => runProjectionComputedNext(l, t,
|
|
258
|
+
runAuthoritative(() => runProjectionComputedNext(l, t, r?.key === undefined ? "id" : r.key, wrapCommit, aroundDraftWrite));
|
|
256
259
|
} finally {
|
|
257
260
|
declareFlight(e);
|
|
258
261
|
}
|
|
@@ -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
|
}
|
|
@@ -340,15 +343,15 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
340
343
|
if (n !== null) {
|
|
341
344
|
// Occurrence-aware key queues (parity with the adoption window):
|
|
342
345
|
// duplicate keys match per occurrence, each current row consumed once.
|
|
343
|
-
let
|
|
344
|
-
const
|
|
345
|
-
for (let t = 0; t <
|
|
346
|
+
let r = null;
|
|
347
|
+
const o = e.length;
|
|
348
|
+
for (let t = 0; t < o; t++) {
|
|
346
349
|
const i = unwrapValue(e[t]);
|
|
347
350
|
if (!isWrappable(i)) continue;
|
|
348
|
-
const
|
|
349
|
-
if (
|
|
350
|
-
const s = (
|
|
351
|
-
if (s === undefined)
|
|
351
|
+
const o = n(i);
|
|
352
|
+
if (o === undefined) continue;
|
|
353
|
+
const s = (r ??= new Map).get(o);
|
|
354
|
+
if (s === undefined) r.set(o, [ i ]); else s.push(i);
|
|
352
355
|
}
|
|
353
356
|
// Echo adoption: an optimistic structural add whose key the landing
|
|
354
357
|
// confirms must keep its raw (and so its proxy — list drivers keep the
|
|
@@ -363,42 +366,42 @@ 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
|
-
const
|
|
369
|
-
if (
|
|
370
|
-
const l = (
|
|
371
|
-
if (l === undefined)
|
|
371
|
+
const o = n(i);
|
|
372
|
+
if (o === undefined) continue;
|
|
373
|
+
const l = (r ??= new Map).get(o);
|
|
374
|
+
if (l === undefined) r.set(o, [ i ]); else l.push(i);
|
|
372
375
|
}
|
|
373
376
|
}
|
|
374
|
-
for (let
|
|
375
|
-
const i = t[
|
|
377
|
+
for (let o = 0; o < i; o++) {
|
|
378
|
+
const i = t[o];
|
|
376
379
|
let s;
|
|
377
|
-
if (isWrappable(i) &&
|
|
380
|
+
if (isWrappable(i) && r !== null) {
|
|
378
381
|
const e = n(i);
|
|
379
382
|
if (e !== undefined) {
|
|
380
|
-
for (const [t, n] of
|
|
383
|
+
for (const [t, n] of r) {
|
|
381
384
|
if (!sameKey(t, e)) continue;
|
|
382
385
|
s = n.shift();
|
|
383
|
-
if (n.length === 0)
|
|
386
|
+
if (n.length === 0) r.delete(t);
|
|
384
387
|
break;
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
390
|
}
|
|
388
391
|
if (s !== undefined) {
|
|
389
|
-
if (unwrapValue(e[
|
|
390
|
-
stagedApply(e[
|
|
392
|
+
if (unwrapValue(e[o]) !== s) e[o] = s;
|
|
393
|
+
stagedApply(e[o], i, n);
|
|
391
394
|
} else {
|
|
392
|
-
const t = unwrapValue(e[
|
|
393
|
-
if (!isEqual(t, i) && !targetsEqual(t, i)) e[
|
|
395
|
+
const t = unwrapValue(e[o]);
|
|
396
|
+
if (!isEqual(t, i) && !targetsEqual(t, i)) e[o] = i;
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
399
|
} else {
|
|
397
|
-
for (let
|
|
398
|
-
const i = t[
|
|
399
|
-
const
|
|
400
|
-
if (
|
|
401
|
-
if (isWrappable(i) && isWrappable(
|
|
400
|
+
for (let r = 0; r < i; r++) {
|
|
401
|
+
const i = t[r];
|
|
402
|
+
const o = unwrapValue(e[r]);
|
|
403
|
+
if (o === i) continue;
|
|
404
|
+
if (isWrappable(i) && isWrappable(o) && Array.isArray(i) === Array.isArray(o)) stagedApply(e[r], i, n); else if (!isEqual(o, i) && !targetsEqual(o, i)) e[r] = i;
|
|
402
405
|
}
|
|
403
406
|
}
|
|
404
407
|
if (e.length !== i) e.length = i;
|
|
@@ -406,25 +409,25 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
406
409
|
}
|
|
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
|
-
for (const
|
|
410
|
-
if (i &&
|
|
411
|
-
const
|
|
412
|
-
const s = unwrapValue(e[
|
|
413
|
-
if (s ===
|
|
414
|
-
if (isWrappable(
|
|
412
|
+
for (const r of Reflect.ownKeys(t)) {
|
|
413
|
+
if (i && r === "length" || r === $OWNER) continue;
|
|
414
|
+
const o = t[r];
|
|
415
|
+
const s = unwrapValue(e[r]);
|
|
416
|
+
if (s === o) continue;
|
|
417
|
+
if (isWrappable(o) && isWrappable(s) && Array.isArray(o) === Array.isArray(s)) {
|
|
415
418
|
// Different-keyed entities never merge (tentative-channel parity):
|
|
416
419
|
// the incoming object replaces the slot wholesale.
|
|
417
420
|
if (n !== null) {
|
|
418
421
|
const t = n(s);
|
|
419
|
-
const i = n(
|
|
422
|
+
const i = n(o);
|
|
420
423
|
if (t !== undefined && i !== undefined && !sameKey(t, i)) {
|
|
421
|
-
e[
|
|
424
|
+
e[r] = o;
|
|
422
425
|
continue;
|
|
423
426
|
}
|
|
424
427
|
}
|
|
425
|
-
stagedApply(e[
|
|
426
|
-
} else if (!isEqual(s,
|
|
427
|
-
e[
|
|
428
|
+
stagedApply(e[r], o, n);
|
|
429
|
+
} else if (!isEqual(s, o) && !targetsEqual(s, o)) {
|
|
430
|
+
e[r] = o;
|
|
428
431
|
}
|
|
429
432
|
}
|
|
430
433
|
for (const n of Reflect.ownKeys(e)) {
|
|
@@ -468,46 +471,46 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
468
471
|
// churned for the life of the action and snapped back at settle (#3323).
|
|
469
472
|
const visible = (t, n) => {
|
|
470
473
|
const i = e.n?.[t];
|
|
471
|
-
return unwrapValue(i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.
|
|
474
|
+
return unwrapValue(i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Ce) : n);
|
|
472
475
|
};
|
|
473
476
|
const visiblePresent = t => {
|
|
474
477
|
const n = e.h?.[t];
|
|
475
|
-
return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.
|
|
478
|
+
return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Ce) : t in i;
|
|
476
479
|
};
|
|
477
|
-
let
|
|
478
|
-
const
|
|
480
|
+
let r = false;
|
|
481
|
+
const o = Array.isArray(t);
|
|
479
482
|
for (const n of Reflect.ownKeys(t)) {
|
|
480
|
-
if (
|
|
483
|
+
if (o && n === "length" || n === $OWNER) continue;
|
|
481
484
|
const s = unwrapValue(t[n]);
|
|
482
485
|
if (!visiblePresent(n)) {
|
|
483
486
|
// Optimistic add: value node + presence node + membership bump.
|
|
484
487
|
setSignal(getNode(e, n, i[n]), () => s);
|
|
485
488
|
setSignal(getHasNode(e, n, n in i), true);
|
|
486
|
-
|
|
489
|
+
r = true;
|
|
487
490
|
} else {
|
|
488
491
|
const t = visible(n, i[n]);
|
|
489
492
|
if (!isEqual(t, s) && !targetsEqual(t, s)) {
|
|
490
493
|
setSignal(getNode(e, n, t), () => s);
|
|
491
|
-
if (
|
|
494
|
+
if (o) r = true;
|
|
492
495
|
}
|
|
493
496
|
}
|
|
494
497
|
}
|
|
495
498
|
for (const n of Reflect.ownKeys(i)) {
|
|
496
|
-
if (
|
|
499
|
+
if (o && n === "length" || n === $OWNER) continue;
|
|
497
500
|
if (n in t || !visiblePresent(n)) continue;
|
|
498
501
|
// Optimistic delete: node reads undefined, presence flips, membership bumps.
|
|
499
502
|
setSignal(getNode(e, n, i[n]), () => undefined);
|
|
500
503
|
setSignal(getHasNode(e, n, true), false);
|
|
501
|
-
|
|
504
|
+
r = true;
|
|
502
505
|
}
|
|
503
|
-
if (
|
|
506
|
+
if (o) {
|
|
504
507
|
const n = visible("length", i.length);
|
|
505
508
|
if (n !== t.length) {
|
|
506
509
|
setSignal(getNode(e, "length", n), () => t.length);
|
|
507
|
-
|
|
510
|
+
r = true;
|
|
508
511
|
}
|
|
509
512
|
}
|
|
510
|
-
if (
|
|
513
|
+
if (r) setSignal(getKeySetNode(e), e => e + 1);
|
|
511
514
|
// Deep-witness: optimistic value writes notify deep() subscribers too
|
|
512
515
|
// (structural ones already ride the key-set bump above).
|
|
513
516
|
bumpDeep(e);
|
|
@@ -518,7 +521,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
518
521
|
e.pb = stagedTruthPB.get(e) ?? null;
|
|
519
522
|
if (e.pb !== null) stagedTruthPB.delete(e);
|
|
520
523
|
(e.fam.overlaid ??= new Set).add(e);
|
|
521
|
-
GlobalQueue.
|
|
524
|
+
GlobalQueue.Dn?.(e.fam.px ?? e.px);
|
|
522
525
|
}
|
|
523
526
|
|
|
524
527
|
/** Optimistic-view composition for snapshot/deep (O1: snapshot is the CURRENT
|
|
@@ -526,18 +529,20 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
526
529
|
* RUL-12). Returns `src` untouched when no override is active on `t`.
|
|
527
530
|
* Authoritative-view reads (until()'s predicate) skip composition entirely:
|
|
528
531
|
* the predicate observes authoritative truth, never the caller's tentative
|
|
529
|
-
* 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) {
|
|
530
533
|
if (e.fam?.opt !== true || authoritativeRead()) return t;
|
|
531
|
-
let
|
|
532
|
-
const ensure = () =>
|
|
534
|
+
let i = null;
|
|
535
|
+
const ensure = () => i ??= Array.isArray(t) ? [ ...t ] : {
|
|
533
536
|
...t
|
|
534
537
|
};
|
|
535
|
-
const
|
|
536
|
-
if (
|
|
537
|
-
for (const e of Reflect.ownKeys(
|
|
538
|
-
const
|
|
539
|
-
|
|
540
|
-
|
|
538
|
+
const r = e.n;
|
|
539
|
+
if (r !== null) {
|
|
540
|
+
for (const e of Reflect.ownKeys(r)) {
|
|
541
|
+
const i = r[e];
|
|
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);
|
|
541
546
|
if (e === "length" && Array.isArray(t)) {
|
|
542
547
|
if (t.length !== o) ensure().length = o;
|
|
543
548
|
} else if (!isEqual(t[e], o)) ensure()[e] = o;
|
|
@@ -546,21 +551,23 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
546
551
|
const o = e.h;
|
|
547
552
|
if (o !== null) {
|
|
548
553
|
for (const e of Reflect.ownKeys(o)) {
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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];
|
|
553
560
|
}
|
|
554
561
|
}
|
|
555
|
-
return
|
|
562
|
+
return i ?? t;
|
|
556
563
|
}
|
|
557
564
|
|
|
558
565
|
function applyTentative(e, t, n) {
|
|
559
566
|
const i = e.pb ?? e.v;
|
|
560
|
-
const
|
|
561
|
-
const
|
|
567
|
+
const r = optimisticView(e, i);
|
|
568
|
+
const o = e.fam;
|
|
562
569
|
const s = Array.isArray(t);
|
|
563
|
-
if (Array.isArray(
|
|
570
|
+
if (Array.isArray(r) !== s) return;
|
|
564
571
|
// kind change at root: flat overrides below
|
|
565
572
|
const l = [];
|
|
566
573
|
let u;
|
|
@@ -574,22 +581,22 @@ function applyTentative(e, t, n) {
|
|
|
574
581
|
if (Array.isArray(e) !== Array.isArray(t)) return null;
|
|
575
582
|
if (n) {
|
|
576
583
|
const i = n(e);
|
|
577
|
-
const
|
|
584
|
+
const r = n(t);
|
|
578
585
|
// SameValueZero (re-audit 3, P1-3): parity with the plain reconcile
|
|
579
586
|
// channel — NaN keys are self-equal.
|
|
580
|
-
if (i !== undefined &&
|
|
587
|
+
if (i !== undefined && r !== undefined && !sameKey(i, r)) return null;
|
|
581
588
|
}
|
|
582
|
-
return lookupTarget(unwrapValue(e),
|
|
589
|
+
return lookupTarget(unwrapValue(e), o) ?? null;
|
|
583
590
|
};
|
|
584
591
|
if (s) {
|
|
585
|
-
const e =
|
|
592
|
+
const e = r;
|
|
586
593
|
let i = null;
|
|
587
|
-
for (let
|
|
588
|
-
const
|
|
589
|
-
if (!isWrappable(
|
|
594
|
+
for (let r = 0; r < t.length; r++) {
|
|
595
|
+
const o = t[r];
|
|
596
|
+
if (!isWrappable(o)) continue;
|
|
590
597
|
let s;
|
|
591
598
|
if (n) {
|
|
592
|
-
const t = n(
|
|
599
|
+
const t = n(o);
|
|
593
600
|
if (t !== undefined) {
|
|
594
601
|
if (i === null) {
|
|
595
602
|
// Occurrence-aware index queues (re-audit 3, P1-3): parity with
|
|
@@ -597,41 +604,41 @@ function applyTentative(e, t, n) {
|
|
|
597
604
|
// occurrence, each view row consumed once.
|
|
598
605
|
i = new Map;
|
|
599
606
|
for (let t = 0; t < e.length; t++) {
|
|
600
|
-
const
|
|
601
|
-
if (isWrappable(
|
|
602
|
-
const e = n(
|
|
607
|
+
const r = unwrapValue(e[t]);
|
|
608
|
+
if (isWrappable(r)) {
|
|
609
|
+
const e = n(r);
|
|
603
610
|
if (e === undefined) continue;
|
|
604
|
-
const
|
|
605
|
-
if (
|
|
611
|
+
const o = i.get(e);
|
|
612
|
+
if (o === undefined) i.set(e, t); else if (Array.isArray(o)) o.push(t); else i.set(e, [ o, t ]);
|
|
606
613
|
}
|
|
607
614
|
}
|
|
608
615
|
}
|
|
609
|
-
const
|
|
610
|
-
if (
|
|
611
|
-
s = unwrapValue(e[
|
|
612
|
-
if (
|
|
616
|
+
const r = i.get(t);
|
|
617
|
+
if (r === undefined) s = undefined; else if (Array.isArray(r)) {
|
|
618
|
+
s = unwrapValue(e[r.shift()]);
|
|
619
|
+
if (r.length === 1) i.set(t, r[0]);
|
|
613
620
|
} else {
|
|
614
|
-
s = unwrapValue(e[
|
|
621
|
+
s = unwrapValue(e[r]);
|
|
615
622
|
i.delete(t);
|
|
616
623
|
}
|
|
617
|
-
} else s = unwrapValue(e[
|
|
618
|
-
} else s = unwrapValue(e[
|
|
619
|
-
const a = match(s,
|
|
624
|
+
} else s = unwrapValue(e[r]);
|
|
625
|
+
} else s = unwrapValue(e[r]);
|
|
626
|
+
const a = match(s, o);
|
|
620
627
|
if (a !== null) {
|
|
621
628
|
// Keep the existing row in the slot (identity preserved); recurse.
|
|
622
|
-
u[
|
|
623
|
-
l.push([ a,
|
|
629
|
+
u[r] = unwrapValue(s);
|
|
630
|
+
l.push([ a, o ]);
|
|
624
631
|
}
|
|
625
632
|
}
|
|
626
633
|
} else {
|
|
627
634
|
for (const e of Reflect.ownKeys(t)) {
|
|
628
635
|
if (e === $OWNER) continue;
|
|
629
|
-
const n = unwrapValue(
|
|
636
|
+
const n = unwrapValue(r[e]);
|
|
630
637
|
const i = t[e];
|
|
631
|
-
const
|
|
632
|
-
if (
|
|
638
|
+
const o = match(n, i);
|
|
639
|
+
if (o !== null) {
|
|
633
640
|
u[e] = n;
|
|
634
|
-
l.push([
|
|
641
|
+
l.push([ o, i ]);
|
|
635
642
|
}
|
|
636
643
|
}
|
|
637
644
|
}
|
|
@@ -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
|
}
|