@solidjs/signals 2.0.0-rc.19 → 2.0.0-rc.20
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 +553 -264
- package/dist/dev.attribution.js +18 -18
- package/dist/dev.js +1030 -439
- package/dist/observe/affects.js +12 -2
- package/dist/observe/boundaries.js +168 -115
- package/dist/observe/core/async.js +57 -37
- package/dist/observe/core/attribution.js +6 -6
- package/dist/observe/core/constants.js +9 -1
- package/dist/observe/core/copies.js +10 -0
- package/dist/observe/core/core.js +261 -181
- package/dist/observe/core/effect.js +32 -27
- package/dist/observe/core/graph.js +7 -7
- package/dist/observe/core/heap.js +29 -29
- package/dist/observe/core/lanes.js +301 -101
- package/dist/observe/core/owner.js +13 -13
- package/dist/observe/core/scheduler.js +124 -69
- package/dist/observe/core/truth.js +9 -7
- package/dist/observe/core/verdict.js +160 -98
- package/dist/observe/deferred.js +1 -1
- package/dist/observe/index.js +2 -0
- package/dist/observe/map.js +120 -116
- package/dist/observe/reveal.js +48 -48
- package/dist/observe/signals.js +5 -5
- package/dist/observe/store/affects.js +2 -2
- package/dist/observe/store/optimistic.js +272 -176
- package/dist/observe/store/projection.js +21 -10
- package/dist/observe/store/reconcile.js +8 -2
- package/dist/observe/store/store.js +91 -55
- package/dist/observe/store/types.js +11 -1
- package/dist/observe/store/utils.js +1 -1
- package/dist/prod/affects.js +12 -2
- package/dist/prod/boundaries.js +164 -109
- package/dist/prod/core/async.js +201 -181
- package/dist/prod/core/constants.js +9 -1
- package/dist/prod/core/copies.js +10 -0
- package/dist/prod/core/core.js +244 -164
- package/dist/prod/core/dev.js +108 -3
- package/dist/prod/core/effect.js +28 -23
- package/dist/prod/core/graph.js +9 -9
- package/dist/prod/core/heap.js +16 -16
- package/dist/prod/core/identities.js +1 -1
- package/dist/prod/core/lanes.js +307 -95
- package/dist/prod/core/owner.js +22 -22
- package/dist/prod/core/scheduler.js +266 -211
- package/dist/prod/core/truth.js +9 -7
- package/dist/prod/core/verdict.js +161 -99
- package/dist/prod/deferred.js +1 -1
- package/dist/prod/index.js +2 -0
- package/dist/prod/map.js +78 -74
- package/dist/prod/reveal.js +34 -34
- package/dist/prod/signals.js +3 -3
- package/dist/prod/store/affects.js +15 -15
- package/dist/prod/store/optimistic.js +185 -89
- package/dist/prod/store/projection.js +49 -38
- package/dist/prod/store/reconcile.js +8 -2
- package/dist/prod/store/store.js +99 -63
- package/dist/prod/store/types.js +11 -1
- package/dist/types/affects.d.ts +3 -2
- package/dist/types/boundaries.d.ts +14 -2
- package/dist/types/core/async.d.ts +2 -1
- package/dist/types/core/constants.d.ts +8 -0
- package/dist/types/core/copies.d.ts +54 -0
- package/dist/types/core/core.d.ts +15 -0
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +8 -1
- package/dist/types/core/scheduler.d.ts +36 -4
- package/dist/types/core/truth.d.ts +3 -1
- package/dist/types/core/types.d.ts +6 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/store/hooks.d.ts +15 -1
- package/dist/types/store/store.d.ts +2 -1
- package/dist/types/store/target.d.ts +5 -0
- package/dist/types/store/types.d.ts +7 -0
- package/package.json +1 -1
|
@@ -14,6 +14,8 @@ import { getOwner, isDisposed } from "../core/owner.js";
|
|
|
14
14
|
|
|
15
15
|
import { question, setQuestion, insertSubs, schedule } from "../core/scheduler.js";
|
|
16
16
|
|
|
17
|
+
import { optHooks } from "./hooks.js";
|
|
18
|
+
|
|
17
19
|
import { reconcileState } from "./reconcile.js";
|
|
18
20
|
|
|
19
21
|
import { wrap, nameStore, storeSetter, runDirect } from "./store.js";
|
|
@@ -129,6 +131,7 @@ function cloneState(e, t) {
|
|
|
129
131
|
// reader the landing leaves unchanged does not re-run to clear it (#4305).
|
|
130
132
|
const r = new NotReadyError(t);
|
|
131
133
|
e.woke = true;
|
|
134
|
+
optHooks?.ask(e);
|
|
132
135
|
forEachFamilyNode(e, e => {
|
|
133
136
|
if (e.o === null) return;
|
|
134
137
|
// The leaf carries the derive's pending as a propagated copy, as a memo
|
|
@@ -164,6 +167,10 @@ function cloneState(e, t) {
|
|
|
164
167
|
forEachFamilyNode(e, e => {
|
|
165
168
|
if (e.o !== null) settlePendingSource(e, t);
|
|
166
169
|
});
|
|
170
|
+
// The landing is the derive's change for a reader parked on the derive
|
|
171
|
+
// itself, whatever status the landing pass began from (an errored derive
|
|
172
|
+
// re-asked: a reader that met the error has no leaf to learn it from).
|
|
173
|
+
insertSubs(t);
|
|
167
174
|
}
|
|
168
175
|
|
|
169
176
|
/** A source's status reached the derive by propagation (core
|
|
@@ -192,16 +199,16 @@ function forEachFamilyNode(e, t) {
|
|
|
192
199
|
const s = e[$TARGET];
|
|
193
200
|
const c = s.fam;
|
|
194
201
|
const a = c.run = (c.run || 0) + 1;
|
|
195
|
-
let
|
|
202
|
+
let u;
|
|
196
203
|
// Open loading window (seedLoadingValue): the observable store IS commit #0
|
|
197
204
|
// for the whole first flight — the derive works a detached shadow of the
|
|
198
205
|
// seed so draft writes cannot tear through to readers (#2988). Every
|
|
199
206
|
// commit point reconciles the shadow through the normal commit path.
|
|
200
|
-
const
|
|
207
|
+
const l = i.ce ? cloneState(s.v, s.s) : null;
|
|
201
208
|
// The question a write of this run answers: inside a slice, the slice's
|
|
202
209
|
// (the sync pass); in a continuation — the flight still registered — the
|
|
203
210
|
// flight's (`handleAsync` stamped it with the action that asked).
|
|
204
|
-
const asking = () => question || (i.T !== null && i.T.Ee !== null ? i.T.
|
|
211
|
+
const asking = () => question || (i.T !== null && i.T.Ee !== null ? i.T.Ie : 0);
|
|
205
212
|
const f = wrapDraft(e, () => c.run === a && !isDisposed(i), n, s.s,
|
|
206
213
|
// A write after the run returned (a continuation, a callback) arms the
|
|
207
214
|
// drain itself when no landing will.
|
|
@@ -213,12 +220,12 @@ function forEachFamilyNode(e, t) {
|
|
|
213
220
|
// — stages and commits with the flush like a memo's recompute.
|
|
214
221
|
const d = a === 1;
|
|
215
222
|
const pass = () => storeSetter(f, n => {
|
|
216
|
-
|
|
217
|
-
const commit = t => {
|
|
223
|
+
u = t(l ?? n);
|
|
224
|
+
const commit = (t, a = 1) => {
|
|
218
225
|
// Shadow run: commit a detached snapshot, never the shadow itself
|
|
219
226
|
// (adoption takes the value by identity — handing it the live
|
|
220
227
|
// shadow would fuse the draft to the observable store).
|
|
221
|
-
if (
|
|
228
|
+
if (l && (t === undefined || t === l)) t = cloneState(l, s.s);
|
|
222
229
|
if (t !== n && t !== undefined) {
|
|
223
230
|
const write = () => {
|
|
224
231
|
const o = question;
|
|
@@ -237,10 +244,13 @@ function forEachFamilyNode(e, t) {
|
|
|
237
244
|
// A commit is the derive's change for the readers that observed its
|
|
238
245
|
// flight (linked by `pullFamily`; a settled pull does not link), as
|
|
239
246
|
// a memo's landing is for its readers: one that caught the NotReady
|
|
240
|
-
// (a fallback value, not pending) re-runs too (#4070).
|
|
241
|
-
|
|
247
|
+
// (a fallback value, not pending) re-runs too (#4070). A pass of a
|
|
248
|
+
// settled derive has none (`wake` 0): a link left is a stale one a
|
|
249
|
+
// held frame keeps (A30), from a reader that re-ran since. Woken by
|
|
250
|
+
// every pass, a reader feeding a key the pass peeks at looped (#4342).
|
|
251
|
+
if (a) insertSubs(i);
|
|
242
252
|
};
|
|
243
|
-
const a = handleAsync(i,
|
|
253
|
+
const a = handleAsync(i, u, commit, (e, t) => {
|
|
244
254
|
// The flight rejected: the leaves' readers learn it from here —
|
|
245
255
|
// they do not subscribe to the derive. Pending again (a NotReady
|
|
246
256
|
// rejection): the wake, as for the pass. Errored: every live
|
|
@@ -250,7 +260,8 @@ function forEachFamilyNode(e, t) {
|
|
|
250
260
|
if (d && i.m & STATUS_UNINITIALIZED) return;
|
|
251
261
|
if (t) wakeFamily(c); else errorFamily(c);
|
|
252
262
|
});
|
|
253
|
-
|
|
263
|
+
// (The status is the one before this pass: `recompute` sets it after.)
|
|
264
|
+
if (!i.ce) commit(a, i.m & (STATUS_PENDING | STATUS_ERROR));
|
|
254
265
|
}, false);
|
|
255
266
|
try {
|
|
256
267
|
d ? runDirect(pass) : pass();
|
|
@@ -79,7 +79,10 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
|
|
|
79
79
|
if (r) markRawIngest(n);
|
|
80
80
|
// The deep witness is bumped once per adoption (none to bump: done).
|
|
81
81
|
let u = e.dk === null;
|
|
82
|
-
|
|
82
|
+
// A live chained base (a store proxy, §7b) reads the inner store's
|
|
83
|
+
// current frame, not what the nodes were last told — nothing to diff
|
|
84
|
+
// against: the fold writes every node (#4361).
|
|
85
|
+
if (Array.isArray(i) !== l || f[$TARGET]) {
|
|
83
86
|
notifyFold(e, f, n);
|
|
84
87
|
return;
|
|
85
88
|
}
|
|
@@ -186,7 +189,10 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
|
|
|
186
189
|
let a = 0;
|
|
187
190
|
for (const l in n) {
|
|
188
191
|
const c = n[l];
|
|
189
|
-
|
|
192
|
+
let d = f[l];
|
|
193
|
+
// A key under a guess is judged against the guess too (D-34): a truth
|
|
194
|
+
// equal to the value the guess covered still answers it.
|
|
195
|
+
if (optHooks && d === c && i !== f) d = i[l];
|
|
190
196
|
const p = c !== null && typeof c === "object";
|
|
191
197
|
if (d === c && (!p || !isOwned(c)) && (s === null || s[l] === undefined || !hasAccessorFlag(s[l]))) {
|
|
192
198
|
if (s?.[l] !== undefined) a++;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { attrHooks } from "../core/attribution-hooks.js";
|
|
2
2
|
|
|
3
|
-
import { CONFIG_MANUAL_WRITE, CONFIG_OVERRIDE, CONFIG_HELD, $REFRESH, NOT_PENDING, STATUS_PENDING, STATUS_ERROR, EFFECT_RENDER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_STAGED_READ, CONFIG_VERDICT, CONFIG_CHILDREN_FORBIDDEN, CONFIG_PROMOTED, REACTIVE_DIRTY, REACTIVE_CHECK } from "../core/constants.js";
|
|
3
|
+
import { CONFIG_MANUAL_WRITE, CONFIG_OVERRIDE, CONFIG_HELD, $REFRESH, NOT_PENDING, STATUS_PENDING, STATUS_ERROR, EFFECT_RENDER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_STAGED_READ, CONFIG_VERDICT, CONFIG_CHILDREN_FORBIDDEN, STATUS_UNINITIALIZED, CONFIG_PROMOTED, REACTIVE_DIRTY, REACTIVE_CHECK } from "../core/constants.js";
|
|
4
4
|
|
|
5
|
-
import { writeHook, setSignal, isEqual, read, verdict, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
5
|
+
import { writeHook, setSignal, isEqual, read, verdict, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead, tracking } from "../core/core.js";
|
|
6
6
|
|
|
7
7
|
import { registerGraph } from "../core/dev.js";
|
|
8
8
|
|
|
9
9
|
import { signalHook } from "../core/identities.js";
|
|
10
10
|
|
|
11
|
-
import { setSlotUnobserved } from "../core/graph.js";
|
|
11
|
+
import { link, setSlotUnobserved } from "../core/graph.js";
|
|
12
12
|
|
|
13
13
|
import { getOwner, getObserver } from "../core/owner.js";
|
|
14
14
|
|
|
@@ -20,7 +20,7 @@ import { lookupTarget, storeOwners, setLookup, $OWNER, isOwned, markDescendants
|
|
|
20
20
|
|
|
21
21
|
import { optHooks, affectsHooks } from "./hooks.js";
|
|
22
22
|
|
|
23
|
-
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK } from "./types.js";
|
|
23
|
+
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK, installShallowView } from "./types.js";
|
|
24
24
|
|
|
25
25
|
export { markRaw } from "./types.js";
|
|
26
26
|
|
|
@@ -224,7 +224,7 @@ function sameLogicalSlot(e, t, n) {
|
|
|
224
224
|
|
|
225
225
|
function releaseSlot(e) {
|
|
226
226
|
const t = e.O;
|
|
227
|
-
const n = e.
|
|
227
|
+
const n = e.gt;
|
|
228
228
|
if (n === $TRACK) {
|
|
229
229
|
if (t.k === e) t.k = null;
|
|
230
230
|
} else if (n === $DEEP) {
|
|
@@ -251,7 +251,7 @@ function releaseSlot(e) {
|
|
|
251
251
|
// holds: its release waits for the commit that resolves it. So does a leaf of
|
|
252
252
|
// a held container (#4146): a mainline write it carried is in the held
|
|
253
253
|
// staging too, where it would read as the hold's until the landing.
|
|
254
|
-
const slotKept = e => e.
|
|
254
|
+
const slotKept = e => e.ue !== NOT_PENDING || e.l & CONFIG_OVERRIDE || e.O.k?.l & CONFIG_HELD;
|
|
255
255
|
|
|
256
256
|
setSlotUnobserved(e => {
|
|
257
257
|
if (slotKept(e)) deferredReleases.add(e); else releaseSlot(e);
|
|
@@ -264,7 +264,7 @@ setSlotUnobserved(e => {
|
|
|
264
264
|
* unchanged stages nothing and holds no one). */ function bornStaged(e, t, n, r) {
|
|
265
265
|
if (t.je && t.je(t.le, n)) return;
|
|
266
266
|
queuePendingNode(t);
|
|
267
|
-
t.
|
|
267
|
+
t.ue = n;
|
|
268
268
|
const o = e.k;
|
|
269
269
|
if (o === null) return;
|
|
270
270
|
// a node-less staging holds nothing (`stageOn`)
|
|
@@ -344,7 +344,7 @@ function getHasNode(e, t) {
|
|
|
344
344
|
// would have read had the node been there from the write.
|
|
345
345
|
if (e.pb !== null && !directCommit) {
|
|
346
346
|
t.le = preBatch(e);
|
|
347
|
-
t.
|
|
347
|
+
t.ue = e.pb;
|
|
348
348
|
queuePendingNode(t);
|
|
349
349
|
}
|
|
350
350
|
}
|
|
@@ -361,10 +361,10 @@ function getHasNode(e, t) {
|
|
|
361
361
|
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
362
362
|
* flush parks, the seam materializes and holds the node then
|
|
363
363
|
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e, t) {
|
|
364
|
-
if (e.k === null && passLane === null && (context === null || globalQueue.
|
|
364
|
+
if (e.k === null && passLane === null && (context === null || globalQueue.Y) && !deriveHolds(e)) return;
|
|
365
365
|
const n = getContainerNode(e);
|
|
366
|
-
if (n.
|
|
367
|
-
n.
|
|
366
|
+
if (n.ue === NOT_PENDING) queuePendingNode(n);
|
|
367
|
+
n.ue = t;
|
|
368
368
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
369
369
|
// derive) is promoted at the pass's end — the batch with it.
|
|
370
370
|
if (context !== null) notePromotedWrite(n);
|
|
@@ -382,6 +382,7 @@ const storePark = e => {
|
|
|
382
382
|
const n = foldList[t];
|
|
383
383
|
if (n.k === null && n.pb !== null) holdNode(getContainerNode(n), e);
|
|
384
384
|
}
|
|
385
|
+
optHooks?.park(e);
|
|
385
386
|
};
|
|
386
387
|
|
|
387
388
|
function getDeepNode(e) {
|
|
@@ -468,16 +469,23 @@ function isOwnAccessor(e, t) {
|
|
|
468
469
|
n[$OWNER] = t;
|
|
469
470
|
return n;
|
|
470
471
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
472
|
+
// A plain array with no accessors and no named keys beyond `length`
|
|
473
|
+
// (`kc` counts own keys) clones by `slice()`: the descriptor path
|
|
474
|
+
// allocates one descriptor per element, O(n) per first draft write (10k
|
|
475
|
+
// rows: ~10 ms per push).
|
|
476
|
+
let n;
|
|
477
|
+
if (!t.a && t.kc <= e.length + 1 && Object.getPrototypeOf(e) === Array.prototype) n = e.slice(); else {
|
|
478
|
+
const r = Object.getOwnPropertyDescriptors(e);
|
|
479
|
+
for (const n of Reflect.ownKeys(r)) {
|
|
480
|
+
const o = r[n];
|
|
481
|
+
if (n === "length" && Array.isArray(e)) continue;
|
|
482
|
+
o.configurable = true;
|
|
483
|
+
if (!o.get && !o.set) o.writable = true; else t.a = true;
|
|
484
|
+
}
|
|
485
|
+
n = Array.isArray(e) ? Object.defineProperties([], r) : Object.create(Object.getPrototypeOf(e), r);
|
|
477
486
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
return r;
|
|
487
|
+
n[$OWNER] = t;
|
|
488
|
+
return n;
|
|
481
489
|
}
|
|
482
490
|
|
|
483
491
|
/** Wide plain clone (many keys): null-prototype build, then reset. */ function wideClone(e, t) {
|
|
@@ -543,7 +551,7 @@ const plainProto = e => {
|
|
|
543
551
|
const n = overlayOnto(e, e.sc === 2 ? wideClone(e.v, e) : cloneRaw(e.v, e), t);
|
|
544
552
|
e.pb = n;
|
|
545
553
|
// The staging IS the pending backing: re-point the container node.
|
|
546
|
-
if (e.k?.
|
|
554
|
+
if (e.k?.ue === t) e.k.ue = n;
|
|
547
555
|
return n;
|
|
548
556
|
}
|
|
549
557
|
|
|
@@ -564,7 +572,7 @@ const plainProto = e => {
|
|
|
564
572
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
565
573
|
t = e.pb = cloneRaw(t, e);
|
|
566
574
|
const n = e.k;
|
|
567
|
-
if (n !== null && !(n.l & CONFIG_HELD)) n.
|
|
575
|
+
if (n !== null && !(n.l & CONFIG_HELD)) n.ue = t;
|
|
568
576
|
} else if (t === null) {
|
|
569
577
|
const n = e.v;
|
|
570
578
|
if (!directCommit) queueFold(e);
|
|
@@ -741,7 +749,7 @@ let foldIndex = null;
|
|
|
741
749
|
if (r.pb === null) continue;
|
|
742
750
|
// folded already (a direct commit)
|
|
743
751
|
const o = r.k;
|
|
744
|
-
if (o !== null && o.
|
|
752
|
+
if (o !== null && o.ue !== NOT_PENDING) {
|
|
745
753
|
// held: commits with its hold
|
|
746
754
|
foldList.push(r);
|
|
747
755
|
foldOlds.push(t[n]);
|
|
@@ -771,10 +779,10 @@ let foldIndex = null;
|
|
|
771
779
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
772
780
|
* A chained link's committed value is the inner store's at the last
|
|
773
781
|
* read-through — refreshed here, since a guess served the reads since
|
|
774
|
-
* (#3672: the revert compares against the base's live value). */ GlobalQueue.
|
|
782
|
+
* (#3672: the revert compares against the base's live value). */ GlobalQueue.an = e => {
|
|
775
783
|
const t = e.O;
|
|
776
784
|
if (t.ch) {
|
|
777
|
-
const n = e.
|
|
785
|
+
const n = e.gt;
|
|
778
786
|
const r = t.v;
|
|
779
787
|
if (t.h?.[n] === e) e.le = untrack(() => n in r); else if (t.n?.[n] === e) e.le = untrack(() => unwrapValue(r[n]));
|
|
780
788
|
}
|
|
@@ -945,13 +953,18 @@ function heldDerivation(e, t, n) {
|
|
|
945
953
|
// Optimistic channel: a user's writes on an optimistic family become
|
|
946
954
|
// guesses on the nodes (lanes); the draft is discarded, the committed
|
|
947
955
|
// backing never touched.
|
|
948
|
-
if (optHooks &&
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
956
|
+
if (optHooks && e.fam?.opt === true) {
|
|
957
|
+
if (userWrite) {
|
|
958
|
+
// The draft is discarded first: the diff reads the target (its nodes
|
|
959
|
+
// are born from the committed frame, not the draft).
|
|
960
|
+
e.pb = null;
|
|
961
|
+
e.wk = null;
|
|
962
|
+
e.pb = optHooks.writes(e, t);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
// The derive's draft: new rows under guessed rows are paired with them
|
|
966
|
+
// first (D-34, #4329).
|
|
967
|
+
optHooks.pair(e, t);
|
|
955
968
|
}
|
|
956
969
|
const n = e.v;
|
|
957
970
|
const r = e.n;
|
|
@@ -1096,7 +1109,10 @@ function membershipChanged(e, t) {
|
|
|
1096
1109
|
} else {
|
|
1097
1110
|
const o = n[t];
|
|
1098
1111
|
const i = r[t];
|
|
1099
|
-
|
|
1112
|
+
// A live chained `old` (a store proxy, §7b) already reads the inner
|
|
1113
|
+
// store's next frame, not what the node was last told: written, the
|
|
1114
|
+
// node's own equality judging it (#4361).
|
|
1115
|
+
if (n[$TARGET] || !isEqual(o, i) && !targetsEqual(o, i)) setSignal(e, typeof i === "function" ? () => i : i);
|
|
1100
1116
|
}
|
|
1101
1117
|
}
|
|
1102
1118
|
|
|
@@ -1122,7 +1138,8 @@ function hasAccessorFlag(e) {
|
|
|
1122
1138
|
* unrelated `a.value` stayed stale until the action settled). A real
|
|
1123
1139
|
* change still writes, and on a held node still proposes. A live chained
|
|
1124
1140
|
* `old` (a store proxy, §7b) reflects the inner store, not what the node
|
|
1125
|
-
* was last told — written unconditionally, as before
|
|
1141
|
+
* was last told — written unconditionally, as before; its container too
|
|
1142
|
+
* (#4361).
|
|
1126
1143
|
*
|
|
1127
1144
|
* The container is told of an arrangement change when it has structural
|
|
1128
1145
|
* subscribers OR carries an arrangement guess (S4): for a guessed
|
|
@@ -1133,14 +1150,14 @@ function hasAccessorFlag(e) {
|
|
|
1133
1150
|
* answers the arrangement — before, only the presence write reached the
|
|
1134
1151
|
* lane, by accident of being unconditional. */ function notifyFoldTail(e, t, n) {
|
|
1135
1152
|
const r = e.h;
|
|
1153
|
+
const o = t[$TARGET];
|
|
1136
1154
|
if (r !== null) {
|
|
1137
|
-
const e
|
|
1138
|
-
for (const o of Reflect.ownKeys(r)) if (e || o in t !== o in n) setSignal(r[o], o in n);
|
|
1155
|
+
for (const e of Reflect.ownKeys(r)) if (o || e in t !== e in n) setSignal(r[e], e in n);
|
|
1139
1156
|
}
|
|
1140
|
-
const
|
|
1141
|
-
if (
|
|
1142
|
-
const e = Array.isArray(n) && Array.isArray(t) ? arrayStructureChanged(t, n) : membershipChanged(t, n);
|
|
1143
|
-
if (e) notifyContainer(
|
|
1157
|
+
const i = e.k;
|
|
1158
|
+
if (i !== null && (i.o !== null || i.l & CONFIG_OVERRIDE)) {
|
|
1159
|
+
const e = o || (Array.isArray(n) && Array.isArray(t) ? arrayStructureChanged(t, n) : membershipChanged(t, n));
|
|
1160
|
+
if (e) notifyContainer(i, n);
|
|
1144
1161
|
}
|
|
1145
1162
|
}
|
|
1146
1163
|
|
|
@@ -1195,7 +1212,7 @@ function inDraft(e) {
|
|
|
1195
1212
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1196
1213
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1197
1214
|
const t = e.k;
|
|
1198
|
-
if (t !== null) return t.
|
|
1215
|
+
if (t !== null) return t.ue !== NOT_PENDING ? asBacking(t.le) : e.v;
|
|
1199
1216
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1200
1217
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1201
1218
|
}
|
|
@@ -1207,7 +1224,7 @@ function inDraft(e) {
|
|
|
1207
1224
|
* committed frame. */ function stagingReader() {
|
|
1208
1225
|
let e = context;
|
|
1209
1226
|
if (e === null) return null;
|
|
1210
|
-
if (e.Pt) e = e.
|
|
1227
|
+
if (e.Pt) e = e.Ht ?? null;
|
|
1211
1228
|
return e === null || e.l & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1212
1229
|
}
|
|
1213
1230
|
|
|
@@ -1314,7 +1331,7 @@ function inDraft(e) {
|
|
|
1314
1331
|
|
|
1315
1332
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1316
1333
|
* it) change `key`? */ function heldKeyChanged(e, t, n = true) {
|
|
1317
|
-
return changedBetween(e, e.k.le, e.k.
|
|
1334
|
+
return changedBetween(e, e.k.le, e.k.ue, t, n) && !adoptChild(e, t);
|
|
1318
1335
|
}
|
|
1319
1336
|
|
|
1320
1337
|
/** The values of a held frame, built once per frame: neither the committed
|
|
@@ -1343,12 +1360,12 @@ function rawsOf(e) {
|
|
|
1343
1360
|
const n = e.k;
|
|
1344
1361
|
const r = asBacking(n.le);
|
|
1345
1362
|
const o = r[t];
|
|
1346
|
-
const i = n.
|
|
1363
|
+
const i = n.ue[t];
|
|
1347
1364
|
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1348
1365
|
if (e.s || l === undefined || !sameRow(o, i, l) || !isWrappable(o) || !isWrappable(i) || Array.isArray(o) !== Array.isArray(i) || lookupTarget(o, e.fam) !== undefined || lookupTarget(i, e.fam) !== undefined ||
|
|
1349
1366
|
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1350
1367
|
// own row: pairing it with another would alias the two after the commit.
|
|
1351
|
-
rawsOf(r).has(i) || rawsOf(n.
|
|
1368
|
+
rawsOf(r).has(i) || rawsOf(n.ue).has(o)) return false;
|
|
1352
1369
|
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1353
1370
|
// is a fresh target around `ov`.
|
|
1354
1371
|
const u = wrap(o, e, t)[$TARGET];
|
|
@@ -1391,9 +1408,16 @@ function changedBetween(e, t, n, r, o) {
|
|
|
1391
1408
|
|
|
1392
1409
|
/** Value of a key with a leaf: core's — `read()` links a tracked reader and,
|
|
1393
1410
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1394
|
-
* projection's unheld staging is ahead for a handler (`familyAhead`)
|
|
1395
|
-
|
|
1396
|
-
|
|
1411
|
+
* projection's unheld staging is ahead for a handler (`familyAhead`), and is
|
|
1412
|
+
* the only value while its derive has never committed: the committed side
|
|
1413
|
+
* is the seed, never observable (A25) — a reader core serves the screen (a
|
|
1414
|
+
* verdict reader, #4355) is served the staging, as for a memo born staged
|
|
1415
|
+
* (core `serve`). */ function nodeValue(e, t, n) {
|
|
1416
|
+
let r;
|
|
1417
|
+
if (e.ue !== NOT_PENDING && !(e.l & CONFIG_HELD) && n.fam !== null && (context === null || n.fam.node?.m & STATUS_UNINITIALIZED)) {
|
|
1418
|
+
if (context !== null && tracking) link(e, context);
|
|
1419
|
+
r = e.ue;
|
|
1420
|
+
} else r = read(e);
|
|
1397
1421
|
return r === FORCE ? t : r;
|
|
1398
1422
|
}
|
|
1399
1423
|
|
|
@@ -1471,9 +1495,9 @@ function changedBetween(e, t, n, r, o) {
|
|
|
1471
1495
|
// (committed plus the guesses) and does not suspend on the derive; a
|
|
1472
1496
|
// verdict window still asks it (`isPending` is true while the flight is
|
|
1473
1497
|
// up). The family's, not the target's: the edit stands in for the one
|
|
1474
|
-
// flight the family has
|
|
1475
|
-
|
|
1476
|
-
|
|
1498
|
+
// flight the family has — not over a new question they do not answer
|
|
1499
|
+
// (optimistic.ts `stands`, #4336).
|
|
1500
|
+
if (verdict === null && t.m & STATUS_PENDING && optHooks?.stands(e)) return;
|
|
1477
1501
|
read(t);
|
|
1478
1502
|
} else untrack(() => read(t));
|
|
1479
1503
|
}
|
|
@@ -1486,7 +1510,7 @@ function changedBetween(e, t, n, r, o) {
|
|
|
1486
1510
|
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, t) {
|
|
1487
1511
|
const n = e.fam?.node;
|
|
1488
1512
|
if (n !== undefined && n !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1489
|
-
n.l & CONFIG_HELD && t.
|
|
1513
|
+
n.l & CONFIG_HELD && t.ue !== NOT_PENDING &&
|
|
1490
1514
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1491
1515
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1492
1516
|
!(t.l & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
@@ -1655,7 +1679,8 @@ const traps = {
|
|
|
1655
1679
|
// Reading a currently-absent own key subscribes to it (R12) — for any
|
|
1656
1680
|
// target OUTSIDE its own draft scope, even mid-setter (#3037, above).
|
|
1657
1681
|
if (u === undefined && !inDraft(e)) {
|
|
1658
|
-
|
|
1682
|
+
// (`nodeValue` reads — and links — the node it creates.)
|
|
1683
|
+
if (getObserver() !== null) getNode(e, t, i);
|
|
1659
1684
|
const n = e.n?.[t];
|
|
1660
1685
|
if (n) {
|
|
1661
1686
|
const r = nodeValue(n, undefined, e);
|
|
@@ -1834,8 +1859,8 @@ const traps = {
|
|
|
1834
1859
|
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e, t) {
|
|
1835
1860
|
const n = e.k;
|
|
1836
1861
|
if (n === null) return false;
|
|
1837
|
-
const r = n.
|
|
1838
|
-
return r !== null && r.u === t && (!(t.R & REACTIVE_RECOMPUTING_DEPS) || r.
|
|
1862
|
+
const r = n.Ot;
|
|
1863
|
+
return r !== null && r.u === t && (!(t.R & REACTIVE_RECOMPUTING_DEPS) || r.de === t.Ne);
|
|
1839
1864
|
}
|
|
1840
1865
|
|
|
1841
1866
|
function storeSetter(e, t, n = true) {
|
|
@@ -1914,6 +1939,17 @@ function createStore(e, t) {
|
|
|
1914
1939
|
return [ r, setter ];
|
|
1915
1940
|
}
|
|
1916
1941
|
|
|
1942
|
+
/** The list driver's slot view of a shallow store (types.ts `shallowView`):
|
|
1943
|
+
* one `readSource` for the container instead of a trap per slot. A shallow
|
|
1944
|
+
* store's slots hold raw records, so the backing the container read serves
|
|
1945
|
+
* is what each slot read would — except where a slot's own leaf can answer
|
|
1946
|
+
* differently: an optimistic family's guesses, a chained backing, accessor
|
|
1947
|
+
* keys, or a read inside a draft or write scope. Those keep the trap. */ installShallowView(e => {
|
|
1948
|
+
// Any open write scope keeps the trap (it covers `inDraft`).
|
|
1949
|
+
if (e?.s && !(e.ch || e.a || writeScopes || writeOverride || e.fam?.opt)) return e.fam && pullFamily(e),
|
|
1950
|
+
readSource(e);
|
|
1951
|
+
});
|
|
1952
|
+
|
|
1917
1953
|
/** True when `proxy` is a SHALLOW store (children served verbatim, slots
|
|
1918
1954
|
* replaced by reference — #2932). The list driver uses this to choose the
|
|
1919
1955
|
* slot-patch channel (collected row bodies) over per-record registration. */ function storeIsShallow(e) {
|
|
@@ -70,4 +70,14 @@ function setWriteOverride(e) {
|
|
|
70
70
|
writeOverride = e;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
/** The list driver's view of a shallow store (map.ts): the backing a read of
|
|
74
|
+
* every slot serves this reader, or `undefined` when the list is no shallow
|
|
75
|
+
* store or a slot read could differ from it. Installed by store.ts's top
|
|
76
|
+
* level, so a `<For>` in an app without stores carries none of it; it lives
|
|
77
|
+
* beside `$TRACK` so map.ts reaches no other store module. */ let shallowView = null;
|
|
78
|
+
|
|
79
|
+
function installShallowView(e) {
|
|
80
|
+
shallowView = e;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { $PROXY, $RECORD, $TARGET, $TRACK, installShallowView, isRawValue, isWrappable, markRaw, markRawIngest, markRawOne, ownEnumerableKeys, rawValuesUsed, setWriteOverride, shallowView, writeOverride };
|
package/dist/prod/affects.js
CHANGED
|
@@ -16,7 +16,8 @@ import { $TARGET } from "./store/types.js";
|
|
|
16
16
|
// A mark is a declared in-flight change: the marked node — and everything
|
|
17
17
|
// derived from it — reads pending (`isPending` → true) from the declaration
|
|
18
18
|
// until the surrounding transaction lands (an ambient mark, declared outside
|
|
19
|
-
// one, releases at the next seam
|
|
19
|
+
// one, releases at the next seam; either, on a node whose own flight is
|
|
20
|
+
// still up then, at the seam after that flight lands). The marked values stay readable: no reader
|
|
20
21
|
// suspends on a mark, a mark holds nothing and entangles nothing — it is not
|
|
21
22
|
// a node of its transaction (`blocked` never sees it), and a reader of marked
|
|
22
23
|
// data joins nothing through it. A mark is never a re-ask, so a declared
|
|
@@ -71,7 +72,16 @@ function affects(e, t) {
|
|
|
71
72
|
schedule();
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
installMarkHooks(
|
|
75
|
+
installMarkHooks(e => {
|
|
76
|
+
// A landing's mark on a node whose own flight is up (a declared reload's
|
|
77
|
+
// quiet re-ask the action did not wait for) stays to the seam after
|
|
78
|
+
// that flight lands, as an ambient one does: the reload's window is the
|
|
79
|
+
// flight's (#4349). The rest release with the transaction.
|
|
80
|
+
const t = [];
|
|
81
|
+
for (let r = 0; r < e.length; r++) (inFlight(e[r]) ? ambient : t).push(e[r]);
|
|
82
|
+
e.length = 0;
|
|
83
|
+
release(t);
|
|
84
|
+
}, e => {
|
|
75
85
|
if (ambient.length === 0) return;
|
|
76
86
|
// The flush parked: the declaration's window is the frame's — the marks
|
|
77
87
|
// are its transaction's ("nothing async below ⇒ no window").
|