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