@solidjs/signals 2.0.0-rc.5 → 2.0.0-rc.7
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 +2467 -1322
- package/dist/node.cjs +2037 -2428
- package/dist/node.dev.cjs +13724 -0
- package/dist/prod/boundaries.js +3 -1
- package/dist/prod/core/async.js +47 -20
- package/dist/prod/core/attribution-hooks.js +3 -0
- package/dist/prod/core/constants.js +9 -1
- package/dist/prod/core/context.js +10 -16
- package/dist/prod/core/core.js +260 -188
- package/dist/prod/core/dev.js +2 -0
- package/dist/prod/core/effect.js +41 -32
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +35 -31
- package/dist/prod/core/heap.js +34 -40
- package/dist/prod/core/lanes.js +44 -30
- package/dist/prod/core/optimistic.js +55 -45
- package/dist/prod/core/owner.js +35 -35
- package/dist/prod/core/scheduler.js +176 -177
- package/dist/prod/core/verdict.js +53 -52
- package/dist/prod/index.js +0 -2
- package/dist/prod/map.js +104 -94
- package/dist/prod/signals.js +119 -35
- package/dist/prod/store/next/optimistic.js +217 -163
- package/dist/prod/store/next/projection.js +2 -2
- package/dist/prod/store/next/reconcile.js +140 -288
- package/dist/prod/store/next/store.js +338 -252
- package/dist/prod/store/store.js +2 -2
- package/dist/types/core/attribution-hooks.d.ts +75 -0
- package/dist/types/core/attribution.d.ts +313 -9
- package/dist/types/core/constants.d.ts +8 -0
- package/dist/types/core/core.d.ts +12 -3
- package/dist/types/core/dev.d.ts +56 -9
- package/dist/types/core/heap.d.ts +5 -3
- package/dist/types/core/invariants.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +10 -0
- package/dist/types/core/scheduler.d.ts +14 -4
- package/dist/types/signals.d.ts +10 -11
- package/dist/types/store/index.d.ts +3 -6
- package/dist/types/store/next/optimistic.d.ts +4 -2
- package/dist/types/store/next/reconcile.d.ts +5 -12
- package/dist/types/store/next/store.d.ts +3 -9
- package/dist/types/store/next/target.d.ts +30 -46
- package/dist/types/store/store.d.ts +14 -9
- package/dist/types-cjs/core/attribution-hooks.d.cts +75 -0
- package/dist/types-cjs/core/attribution.d.cts +313 -9
- package/dist/types-cjs/core/constants.d.cts +8 -0
- package/dist/types-cjs/core/core.d.cts +12 -3
- package/dist/types-cjs/core/dev.d.cts +56 -9
- package/dist/types-cjs/core/heap.d.cts +5 -3
- package/dist/types-cjs/core/invariants.d.cts +1 -1
- package/dist/types-cjs/core/lanes.d.cts +10 -0
- package/dist/types-cjs/core/scheduler.d.cts +14 -4
- package/dist/types-cjs/signals.d.cts +10 -11
- package/dist/types-cjs/store/index.d.cts +3 -6
- package/dist/types-cjs/store/next/optimistic.d.cts +4 -2
- package/dist/types-cjs/store/next/reconcile.d.cts +5 -12
- package/dist/types-cjs/store/next/store.d.cts +3 -9
- package/dist/types-cjs/store/next/target.d.cts +30 -46
- package/dist/types-cjs/store/store.d.cts +14 -9
- package/package.json +3 -2
- package/dist/prod/store/next/patch-hooks.js +0 -13
- package/dist/prod/store/next/patch.js +0 -614
- package/dist/types/store/next/patch-hooks.d.ts +0 -41
- package/dist/types/store/next/patch.d.ts +0 -91
- package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
- package/dist/types-cjs/store/next/patch.d.cts +0 -91
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, NOT_PENDING, CONFIG_OPTIMISTIC, CONFIG_HELD_TRUTH, unwrapOverride } from "../../core/constants.js";
|
|
1
|
+
import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, STATUS_UNINITIALIZED, NOT_PENDING, CONFIG_OPTIMISTIC, CONFIG_HELD_TRUTH, unwrapOverride } from "../../core/constants.js";
|
|
2
2
|
|
|
3
3
|
import { computed, setSignal, isEqual } from "../../core/core.js";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getOwner } from "../../core/owner.js";
|
|
6
|
+
|
|
7
|
+
import { activeTransition, GlobalQueue, currentTransition, globalQueue, createTransition, runAsTransitionBatch } from "../../core/scheduler.js";
|
|
6
8
|
|
|
7
9
|
import "../../core/invariants.js";
|
|
8
10
|
|
|
@@ -18,9 +20,7 @@ import { runProjectionComputedNext } from "./projection.js";
|
|
|
18
20
|
|
|
19
21
|
import { wrapNext, runAuthoritative, storeSetterNext, hasActiveOverride, unwrapValue, targetsEqual, getNode, getHasNode, getKeySetNode, bumpDeep, stagedTruthPB, authoritativeRead } from "./store.js";
|
|
20
22
|
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
import { sameKey, buildIdentityRowOps } from "./reconcile.js";
|
|
23
|
+
import { sameKey } from "./reconcile.js";
|
|
24
24
|
|
|
25
25
|
import { setOptHooks } from "./target.js";
|
|
26
26
|
|
|
@@ -55,7 +55,7 @@ import { setOptHooks } from "./target.js";
|
|
|
55
55
|
* staged values to converge (normal speculation). Resolves merges first:
|
|
56
56
|
* merge unions optimistic nodes/stores into the target. */ function transitionHoldsOptimism(e) {
|
|
57
57
|
const t = currentTransition(e);
|
|
58
|
-
return t.sn !== true && (t.
|
|
58
|
+
return t.sn !== true && (t.Xe.length !== 0 || t.En.size !== 0);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
let blockedInstalled = false;
|
|
@@ -76,29 +76,20 @@ function installNextBlockedHalf() {
|
|
|
76
76
|
// Scheduler flush tails call _clearOptimisticStores whenever tracked
|
|
77
77
|
// stores exist; next has no layer to clear — reverts are engine-native —
|
|
78
78
|
// so the hook only empties the batch set.
|
|
79
|
-
if (!GlobalQueue.
|
|
80
|
-
GlobalQueue.
|
|
81
|
-
// Patch channel (revert site): engine-native reverts flip node values
|
|
82
|
-
// back to committed; patched records need a forced DOM re-apply from
|
|
83
|
-
// the post-revert view. Emission only — next keeps no layer to clear.
|
|
79
|
+
if (!GlobalQueue.Wt) {
|
|
80
|
+
GlobalQueue.Wt = e => {
|
|
84
81
|
for (const t of e) {
|
|
85
82
|
const e = t?.[$TARGET];
|
|
86
83
|
const n = e?.fam?.overlaid;
|
|
87
84
|
if (n !== undefined) {
|
|
88
85
|
for (const e of n) {
|
|
89
|
-
if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, null, null);
|
|
90
|
-
// Row-ops resync (family increment 2): reverts flip node values
|
|
91
|
-
// back engine-natively; a driven list must rebuild retention by
|
|
92
|
-
// row identity against the post-revert view (resolved from the
|
|
93
|
-
// target at drain — overrides are gone by then).
|
|
94
|
-
if (e.pc !== null && e.pc.ro !== null) rowHooks.emitRowOpsOptimistic(e, null, null);
|
|
95
86
|
// Keyset resync (classic channel twin): the keyset node's own
|
|
96
87
|
// revert can compare EQUAL (a landing's bump matched the
|
|
97
88
|
// tentative bump) while the arrangement underneath changed —
|
|
98
89
|
// mapArray/ownKeys subscribers must re-read the post-revert
|
|
99
90
|
// view. Authoritative bump: never re-arm the node we are
|
|
100
91
|
// clearing.
|
|
101
|
-
|
|
92
|
+
if (e.k !== null) runAuthoritative(() => setSignal(e.k, e => e + 1));
|
|
102
93
|
}
|
|
103
94
|
}
|
|
104
95
|
}
|
|
@@ -108,14 +99,22 @@ function installNextBlockedHalf() {
|
|
|
108
99
|
const e = GlobalQueue.dn;
|
|
109
100
|
GlobalQueue.dn = t => {
|
|
110
101
|
for (const e of t.En) {
|
|
111
|
-
const
|
|
112
|
-
const
|
|
102
|
+
const n = e?.[$TARGET];
|
|
103
|
+
const i = n?.fam;
|
|
104
|
+
const r = i?.node;
|
|
113
105
|
// The hold exists to keep optimistic state alive until the store's own
|
|
114
106
|
// truth lands (#2951). Once the family carries NO live overrides (a
|
|
115
107
|
// landing consumed them, or they never existed), a pending firewall is
|
|
116
108
|
// no reason to park the transaction — blocking then leaks it forever
|
|
117
109
|
// when the in-flight question is never answered (undisposed fixtures).
|
|
118
|
-
if (
|
|
110
|
+
if (r == null || !(r.S & STATUS_PENDING)) continue;
|
|
111
|
+
// Ownership is declared (#3146): only the flight's OWN transaction
|
|
112
|
+
// parks on the flight (the #2951 anchor routed the bare write there).
|
|
113
|
+
// A transaction that merely brushed the store never waits for truth
|
|
114
|
+
// it does not carry.
|
|
115
|
+
const o = i.ft != null ? liveTransition(i.ft) : null;
|
|
116
|
+
if (o !== null && o !== currentTransition(t)) continue;
|
|
117
|
+
if (familyHasLiveOverrides(i)) return true;
|
|
119
118
|
}
|
|
120
119
|
return e(t);
|
|
121
120
|
};
|
|
@@ -129,10 +128,10 @@ function familyHasLiveOverrides(e) {
|
|
|
129
128
|
if (t === null) continue;
|
|
130
129
|
for (const e of Reflect.ownKeys(t)) {
|
|
131
130
|
const n = t[e];
|
|
132
|
-
if (n.o?.
|
|
131
|
+
if (n.o?.Pe !== undefined && n.o?.Pe !== NOT_PENDING) return true;
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
|
-
if (e.k !== null && e.k.o?.
|
|
134
|
+
if (e.k !== null && e.k.o?.Pe !== undefined && e.k.o?.Pe !== NOT_PENDING) return true;
|
|
136
135
|
}
|
|
137
136
|
t.clear();
|
|
138
137
|
// nothing live — drop the bookkeeping
|
|
@@ -145,34 +144,55 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
145
144
|
installOptimisticEngine();
|
|
146
145
|
installNextBlockedHalf();
|
|
147
146
|
const i = typeof e === "function";
|
|
148
|
-
|
|
147
|
+
const r = i ? n : t;
|
|
149
148
|
const o = i ? t : e;
|
|
150
|
-
const
|
|
149
|
+
const s = {
|
|
151
150
|
map: new WeakMap,
|
|
152
151
|
node: null,
|
|
153
|
-
shallow: !!
|
|
152
|
+
shallow: !!r?.shallow,
|
|
154
153
|
opt: true
|
|
155
154
|
};
|
|
156
|
-
const
|
|
157
|
-
|
|
155
|
+
const l = wrapNext(o, null, null, s);
|
|
156
|
+
s.px = l;
|
|
158
157
|
// Same key resolution the projection channels use ("id" default) — replay's
|
|
159
158
|
// satisfaction rule reads it off the family.
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
|
|
159
|
+
const u = r?.key === undefined ? "id" : r.key;
|
|
160
|
+
s.key = typeof u === "function" ? u : u === null ? null : e => isWrappable(e) ? e[u] : undefined;
|
|
161
|
+
if (s.shallow) {
|
|
162
|
+
l[$TARGET].s = true;
|
|
164
163
|
markRawIngest(o);
|
|
165
164
|
}
|
|
166
165
|
if (i) {
|
|
167
166
|
const t = e;
|
|
167
|
+
// #3146: an async settle event belongs to the flight's OWN transaction.
|
|
168
|
+
// A live declared one re-enters (a merge if the generic settle path
|
|
169
|
+
// already entered a graph-stamped stranger — the landing supersedes any
|
|
170
|
+
// recompute deriving from that stranger's world); a dead one renews (per
|
|
171
|
+
// A18(1) each arrival reveals on its own schedule, so per-yield
|
|
172
|
+
// transactions die with their commit and the next settle event opens the
|
|
173
|
+
// flight's next one — still declared, never anonymous). An UNDECLARED
|
|
174
|
+
// flight (loading window) keeps the ambient reveal (#2933: the loading
|
|
175
|
+
// rail is transaction-invisible).
|
|
176
|
+
const enterFlightTransition = () => {
|
|
177
|
+
const e = s.ft;
|
|
178
|
+
if (e == null) return;
|
|
179
|
+
let t = liveTransition(e);
|
|
180
|
+
if (t === null) s.ft = t = createTransition();
|
|
181
|
+
s.node.Ae = t;
|
|
182
|
+
globalQueue.initTransition(t);
|
|
183
|
+
};
|
|
168
184
|
// Landing router (#3164 fold ruling): while a transaction retains
|
|
169
185
|
// optimistic edits on this family, truth landings stage INTO it and
|
|
170
186
|
// reveal atomically at settle; with no retainer they commit immediately
|
|
171
187
|
// under the authoritative posture (async commits land outside the
|
|
172
|
-
// computed's sync body, so the posture is re-applied here)
|
|
188
|
+
// computed's sync body, so the posture is re-applied here) — inside the
|
|
189
|
+
// flight-owned transaction (#3146). Sync commits (the derive's body,
|
|
190
|
+
// owner is the firewall itself) reveal with their own recompute's flush.
|
|
173
191
|
const wrapCommit = (e, t) => {
|
|
174
|
-
const n = retainingTransition(
|
|
175
|
-
if (n
|
|
192
|
+
const n = retainingTransition(s);
|
|
193
|
+
if (n !== null) return void stageLanding(s, n, t);
|
|
194
|
+
if (getOwner() !== s.node) enterFlightTransition();
|
|
195
|
+
runAuthoritative(e);
|
|
176
196
|
};
|
|
177
197
|
// Draft writes (the derive mutating its draft, sync body and post-await
|
|
178
198
|
// continuations alike) are the same truth channel per-operation: bind
|
|
@@ -181,28 +201,74 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
181
201
|
// batch; the write-override eager-commit branch in notifyWrites yields
|
|
182
202
|
// to any active transaction).
|
|
183
203
|
const aroundDraftWrite = e => {
|
|
184
|
-
const t = retainingTransition(
|
|
204
|
+
const t = retainingTransition(s);
|
|
185
205
|
if (t === null) e(); else runFolded(t, e);
|
|
186
206
|
};
|
|
187
|
-
|
|
188
|
-
|
|
207
|
+
// Flight declaration (#3146): a recompute that registered a truth-flight
|
|
208
|
+
// OWNS its transaction. The ask's transaction is recorded on the family
|
|
209
|
+
// (created by the flight's own pending throw when none was ambient, the
|
|
210
|
+
// causing write's/refresh's when one was — graph-driven causality) and
|
|
211
|
+
// the firewall is stamped so every settle path resolves the flight's
|
|
212
|
+
// transaction by construction, not by whatever last brushed the node.
|
|
213
|
+
// When the ask took none (a dead stale stamp — the previous flight's,
|
|
214
|
+
// cleared nowhere — bare-returns the pre-throw entry), the flight opens
|
|
215
|
+
// its own here, same activation point as the pre-throw's creation: the
|
|
216
|
+
// ambient batch (the causing write, same-tick bare optimism) adopts into
|
|
217
|
+
// it exactly as it would have there, and the pending notification that
|
|
218
|
+
// follows this unwind registers observers against it. The flight
|
|
219
|
+
// also registers as its own async reporter: the transaction lives
|
|
220
|
+
// exactly as long as the question is unanswered, observed or not — the
|
|
221
|
+
// #2951 refetch-hold no longer depends on a tracked observer having
|
|
222
|
+
// happened to register one. Loading-window flights declare nothing
|
|
223
|
+
// (#2933: the loading rail is transaction-invisible); a sync run clears
|
|
224
|
+
// the declaration.
|
|
225
|
+
const declareFlight = e => {
|
|
226
|
+
if (e.o?.Ie == null) {
|
|
227
|
+
if (!e.Ne) s.ft = null;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// First flight (#3146 carve-out): nothing has ever committed, so there
|
|
231
|
+
// is no truth to keep on screen and no optimistic state to protect. An
|
|
232
|
+
// uninitialized ask suspends its readers into their Loading boundary
|
|
233
|
+
// exactly like a plain derived store's first flight — declaring a
|
|
234
|
+
// transaction here instead held the ROOT MOUNT (render()'s scheduled
|
|
235
|
+
// insert rides transitions) until the fetch landed, so the boundary's
|
|
236
|
+
// fallback never showed and the whole page stayed blank. The loading
|
|
237
|
+
// window (#2933) already declares nothing for the same reason; once
|
|
238
|
+
// the first truth lands, every refetch flight declares as before.
|
|
239
|
+
if (e.Ne || e.S & STATUS_UNINITIALIZED) return;
|
|
240
|
+
let t = activeTransition;
|
|
241
|
+
if (t === null) globalQueue.initTransition(t = createTransition());
|
|
242
|
+
s.ft = t;
|
|
243
|
+
e.Ae = t;
|
|
244
|
+
let n = t._e.get(e);
|
|
245
|
+
if (n === undefined) t._e.set(e, n = new Set);
|
|
246
|
+
n.add(e);
|
|
247
|
+
};
|
|
248
|
+
let n;
|
|
249
|
+
if (r?.seedLoadingValue) n = {
|
|
189
250
|
loadingValue: undefined
|
|
190
251
|
};
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
252
|
+
const i = computed(() => {
|
|
253
|
+
const e = getOwner();
|
|
254
|
+
try {
|
|
255
|
+
runAuthoritative(() => runProjectionComputedNext(l, t, r?.key === undefined ? "id" : r.key, wrapCommit, aroundDraftWrite));
|
|
256
|
+
} finally {
|
|
257
|
+
declareFlight(e);
|
|
258
|
+
}
|
|
259
|
+
}, n);
|
|
260
|
+
i.T &= ~CONFIG_AUTO_DISPOSE;
|
|
261
|
+
s.node = i;
|
|
196
262
|
}
|
|
197
|
-
return [
|
|
263
|
+
return [ l, e => {
|
|
198
264
|
// Retention ledger (#3164): record the owning transaction so landings
|
|
199
265
|
// know to fold. Captured at entry — the action machinery has the
|
|
200
266
|
// transaction ambient while user code runs; a bare write with no
|
|
201
267
|
// transaction retains nothing (it rides the flight's own transition
|
|
202
268
|
// per #2951 and dies with it).
|
|
203
269
|
const t = activeTransition;
|
|
204
|
-
storeSetterNext(
|
|
205
|
-
if (t !== null) (
|
|
270
|
+
storeSetterNext(l, e);
|
|
271
|
+
if (t !== null) (s.rt ??= new Set).add(t);
|
|
206
272
|
} ];
|
|
207
273
|
}
|
|
208
274
|
|
|
@@ -254,7 +320,7 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
254
320
|
* stay unarmed — the override is their display and its revert their
|
|
255
321
|
* notification, A17). */ function runFolded(e, t) {
|
|
256
322
|
runAsTransitionBatch(e, t);
|
|
257
|
-
const n = e.
|
|
323
|
+
const n = e.Mt;
|
|
258
324
|
for (let t = 0; t < n.length; t++) {
|
|
259
325
|
const i = n[t];
|
|
260
326
|
i.Ae = e;
|
|
@@ -274,15 +340,15 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
274
340
|
if (n !== null) {
|
|
275
341
|
// Occurrence-aware key queues (parity with the adoption window):
|
|
276
342
|
// duplicate keys match per occurrence, each current row consumed once.
|
|
277
|
-
let
|
|
278
|
-
const
|
|
279
|
-
for (let t = 0; t <
|
|
343
|
+
let r = null;
|
|
344
|
+
const o = e.length;
|
|
345
|
+
for (let t = 0; t < o; t++) {
|
|
280
346
|
const i = unwrapValue(e[t]);
|
|
281
347
|
if (!isWrappable(i)) continue;
|
|
282
|
-
const
|
|
283
|
-
if (
|
|
284
|
-
const s = (
|
|
285
|
-
if (s === undefined)
|
|
348
|
+
const o = n(i);
|
|
349
|
+
if (o === undefined) continue;
|
|
350
|
+
const s = (r ??= new Map).get(o);
|
|
351
|
+
if (s === undefined) r.set(o, [ i ]); else s.push(i);
|
|
286
352
|
}
|
|
287
353
|
// Echo adoption: an optimistic structural add whose key the landing
|
|
288
354
|
// confirms must keep its raw (and so its proxy — list drivers keep the
|
|
@@ -297,42 +363,42 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
297
363
|
for (const e of Reflect.ownKeys(s)) {
|
|
298
364
|
const t = s[e];
|
|
299
365
|
if (!hasActiveOverride(t)) continue;
|
|
300
|
-
const i = unwrapValue(unwrapOverride(t.o.
|
|
366
|
+
const i = unwrapValue(unwrapOverride(t.o.Pe));
|
|
301
367
|
if (!isWrappable(i)) continue;
|
|
302
|
-
const
|
|
303
|
-
if (
|
|
304
|
-
const l = (
|
|
305
|
-
if (l === undefined)
|
|
368
|
+
const o = n(i);
|
|
369
|
+
if (o === undefined) continue;
|
|
370
|
+
const l = (r ??= new Map).get(o);
|
|
371
|
+
if (l === undefined) r.set(o, [ i ]); else l.push(i);
|
|
306
372
|
}
|
|
307
373
|
}
|
|
308
|
-
for (let
|
|
309
|
-
const i = t[
|
|
374
|
+
for (let o = 0; o < i; o++) {
|
|
375
|
+
const i = t[o];
|
|
310
376
|
let s;
|
|
311
|
-
if (isWrappable(i) &&
|
|
377
|
+
if (isWrappable(i) && r !== null) {
|
|
312
378
|
const e = n(i);
|
|
313
379
|
if (e !== undefined) {
|
|
314
|
-
for (const [t, n] of
|
|
380
|
+
for (const [t, n] of r) {
|
|
315
381
|
if (!sameKey(t, e)) continue;
|
|
316
382
|
s = n.shift();
|
|
317
|
-
if (n.length === 0)
|
|
383
|
+
if (n.length === 0) r.delete(t);
|
|
318
384
|
break;
|
|
319
385
|
}
|
|
320
386
|
}
|
|
321
387
|
}
|
|
322
388
|
if (s !== undefined) {
|
|
323
|
-
if (unwrapValue(e[
|
|
324
|
-
stagedApply(e[
|
|
389
|
+
if (unwrapValue(e[o]) !== s) e[o] = s;
|
|
390
|
+
stagedApply(e[o], i, n);
|
|
325
391
|
} else {
|
|
326
|
-
const t = unwrapValue(e[
|
|
327
|
-
if (!isEqual(t, i) && !targetsEqual(t, i)) e[
|
|
392
|
+
const t = unwrapValue(e[o]);
|
|
393
|
+
if (!isEqual(t, i) && !targetsEqual(t, i)) e[o] = i;
|
|
328
394
|
}
|
|
329
395
|
}
|
|
330
396
|
} else {
|
|
331
|
-
for (let
|
|
332
|
-
const i = t[
|
|
333
|
-
const
|
|
334
|
-
if (
|
|
335
|
-
if (isWrappable(i) && isWrappable(
|
|
397
|
+
for (let r = 0; r < i; r++) {
|
|
398
|
+
const i = t[r];
|
|
399
|
+
const o = unwrapValue(e[r]);
|
|
400
|
+
if (o === i) continue;
|
|
401
|
+
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;
|
|
336
402
|
}
|
|
337
403
|
}
|
|
338
404
|
if (e.length !== i) e.length = i;
|
|
@@ -340,25 +406,25 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
340
406
|
}
|
|
341
407
|
// Object merge; also the degenerate root-kind-change shape (arrays accept
|
|
342
408
|
// keyed writes/deletes, so a wholesale restatement still lands staged).
|
|
343
|
-
for (const
|
|
344
|
-
if (i &&
|
|
345
|
-
const
|
|
346
|
-
const s = unwrapValue(e[
|
|
347
|
-
if (s ===
|
|
348
|
-
if (isWrappable(
|
|
409
|
+
for (const r of Reflect.ownKeys(t)) {
|
|
410
|
+
if (i && r === "length") continue;
|
|
411
|
+
const o = t[r];
|
|
412
|
+
const s = unwrapValue(e[r]);
|
|
413
|
+
if (s === o) continue;
|
|
414
|
+
if (isWrappable(o) && isWrappable(s) && Array.isArray(o) === Array.isArray(s)) {
|
|
349
415
|
// Different-keyed entities never merge (tentative-channel parity):
|
|
350
416
|
// the incoming object replaces the slot wholesale.
|
|
351
417
|
if (n !== null) {
|
|
352
418
|
const t = n(s);
|
|
353
|
-
const i = n(
|
|
419
|
+
const i = n(o);
|
|
354
420
|
if (t !== undefined && i !== undefined && !sameKey(t, i)) {
|
|
355
|
-
e[
|
|
421
|
+
e[r] = o;
|
|
356
422
|
continue;
|
|
357
423
|
}
|
|
358
424
|
}
|
|
359
|
-
stagedApply(e[
|
|
360
|
-
} else if (!isEqual(s,
|
|
361
|
-
e[
|
|
425
|
+
stagedApply(e[r], o, n);
|
|
426
|
+
} else if (!isEqual(s, o) && !targetsEqual(s, o)) {
|
|
427
|
+
e[r] = o;
|
|
362
428
|
}
|
|
363
429
|
}
|
|
364
430
|
for (const n of Reflect.ownKeys(e)) {
|
|
@@ -373,71 +439,61 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
373
439
|
* overrides — the same view the draft was seeded from) and emit engine writes
|
|
374
440
|
* for exactly the changed keys. Visible-view diffing keeps no-op writes from
|
|
375
441
|
* entangling lanes (RUL-10 / opt R38). */ function notifyOptimisticWrites(e, t) {
|
|
376
|
-
// A bare write while the store's own truth is in flight rides
|
|
377
|
-
// transaction (#2951
|
|
378
|
-
//
|
|
442
|
+
// A bare write while the store's own truth is in flight rides the FLIGHT'S
|
|
443
|
+
// OWN transaction (#2951 via the #3146 declaration): entangle it so the
|
|
444
|
+
// override survives until the refetch settles instead of flash-reverting
|
|
379
445
|
// at plain flush end. The blocked-check store-half keeps that transaction
|
|
380
|
-
// from settling while the firewall is pending.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, t, optimisticView(e, i));
|
|
388
|
-
// Row-ops channel (family increment 2): optimistic STRUCTURE on an array
|
|
389
|
-
// rides node overrides — it never enters the reconcile walk — so a driven
|
|
390
|
-
// list must get its structural ops here, lane-timed. Identity diff of the
|
|
391
|
-
// pre-write optimistic view against the draft; aligned writes emit nothing.
|
|
392
|
-
if (e.pc !== null && e.pc.ro !== null && Array.isArray(t)) {
|
|
393
|
-
const n = optimisticView(e, i);
|
|
394
|
-
if (Array.isArray(n)) {
|
|
395
|
-
const i = buildIdentityRowOps(n, t);
|
|
396
|
-
if (i !== null) rowHooks.emitRowOpsOptimistic(e, t, i);
|
|
397
|
-
}
|
|
446
|
+
// from settling while the firewall is pending. Declared ownership replaces
|
|
447
|
+
// the old circumstantial route through the firewall's `_transition` stamp,
|
|
448
|
+
// which was whatever last brushed the node.
|
|
449
|
+
const n = e.fam?.ft;
|
|
450
|
+
if (n != null) {
|
|
451
|
+
const e = liveTransition(n);
|
|
452
|
+
if (e !== null) globalQueue.initTransition(e);
|
|
398
453
|
}
|
|
454
|
+
const i = e.v;
|
|
399
455
|
const visible = (t, n) => {
|
|
400
456
|
const i = e.n?.[t];
|
|
401
|
-
return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.
|
|
457
|
+
return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Pe) : n;
|
|
402
458
|
};
|
|
403
459
|
const visiblePresent = t => {
|
|
404
460
|
const n = e.h?.[t];
|
|
405
|
-
return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.
|
|
461
|
+
return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Pe) : t in i;
|
|
406
462
|
};
|
|
407
|
-
let
|
|
408
|
-
const
|
|
463
|
+
let r = false;
|
|
464
|
+
const o = Array.isArray(t);
|
|
409
465
|
for (const n of Reflect.ownKeys(t)) {
|
|
410
|
-
if (
|
|
466
|
+
if (o && n === "length") continue;
|
|
411
467
|
const s = unwrapValue(t[n]);
|
|
412
468
|
if (!visiblePresent(n)) {
|
|
413
469
|
// Optimistic add: value node + presence node + membership bump.
|
|
414
470
|
setSignal(getNode(e, n, i[n]), () => s);
|
|
415
471
|
setSignal(getHasNode(e, n, n in i), true);
|
|
416
|
-
|
|
472
|
+
r = true;
|
|
417
473
|
} else {
|
|
418
474
|
const t = visible(n, i[n]);
|
|
419
475
|
if (!isEqual(t, s) && !targetsEqual(t, s)) {
|
|
420
476
|
setSignal(getNode(e, n, t), () => s);
|
|
421
|
-
if (
|
|
477
|
+
if (o) r = true;
|
|
422
478
|
}
|
|
423
479
|
}
|
|
424
480
|
}
|
|
425
481
|
for (const n of Reflect.ownKeys(i)) {
|
|
426
|
-
if (
|
|
482
|
+
if (o && n === "length") continue;
|
|
427
483
|
if (n in t || !visiblePresent(n)) continue;
|
|
428
484
|
// Optimistic delete: node reads undefined, presence flips, membership bumps.
|
|
429
485
|
setSignal(getNode(e, n, i[n]), () => undefined);
|
|
430
486
|
setSignal(getHasNode(e, n, true), false);
|
|
431
|
-
|
|
487
|
+
r = true;
|
|
432
488
|
}
|
|
433
|
-
if (
|
|
489
|
+
if (o) {
|
|
434
490
|
const n = visible("length", i.length);
|
|
435
491
|
if (n !== t.length) {
|
|
436
492
|
setSignal(getNode(e, "length", n), () => t.length);
|
|
437
|
-
|
|
493
|
+
r = true;
|
|
438
494
|
}
|
|
439
495
|
}
|
|
440
|
-
if (
|
|
496
|
+
if (r) setSignal(getKeySetNode(e), e => e + 1);
|
|
441
497
|
// Deep-witness: optimistic value writes notify deep() subscribers too
|
|
442
498
|
// (structural ones already ride the key-set bump above).
|
|
443
499
|
bumpDeep(e);
|
|
@@ -467,19 +523,19 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
467
523
|
for (const e of Reflect.ownKeys(i)) {
|
|
468
524
|
const n = i[e];
|
|
469
525
|
if (!hasActiveOverride(n)) continue;
|
|
470
|
-
const
|
|
526
|
+
const r = unwrapOverride(n.o?.Pe);
|
|
471
527
|
if (e === "length" && Array.isArray(t)) {
|
|
472
|
-
if (t.length !==
|
|
473
|
-
} else if (!isEqual(t[e],
|
|
528
|
+
if (t.length !== r) ensure().length = r;
|
|
529
|
+
} else if (!isEqual(t[e], r)) ensure()[e] = r;
|
|
474
530
|
}
|
|
475
531
|
}
|
|
476
|
-
const
|
|
477
|
-
if (
|
|
478
|
-
for (const e of Reflect.ownKeys(
|
|
479
|
-
const i =
|
|
532
|
+
const r = e.h;
|
|
533
|
+
if (r !== null) {
|
|
534
|
+
for (const e of Reflect.ownKeys(r)) {
|
|
535
|
+
const i = r[e];
|
|
480
536
|
if (!hasActiveOverride(i)) continue;
|
|
481
|
-
const
|
|
482
|
-
if (!
|
|
537
|
+
const o = !!unwrapOverride(i.o?.Pe);
|
|
538
|
+
if (!o && e in (n ?? t)) delete ensure()[e];
|
|
483
539
|
}
|
|
484
540
|
}
|
|
485
541
|
return n ?? t;
|
|
@@ -487,35 +543,39 @@ function createOptimisticStoreNext(e, t, n) {
|
|
|
487
543
|
|
|
488
544
|
function applyTentative(e, t, n) {
|
|
489
545
|
const i = e.pb ?? e.v;
|
|
490
|
-
const
|
|
491
|
-
const
|
|
546
|
+
const r = optimisticView(e, i);
|
|
547
|
+
const o = e.fam.map;
|
|
492
548
|
const s = Array.isArray(t);
|
|
493
|
-
if (Array.isArray(
|
|
549
|
+
if (Array.isArray(r) !== s) return;
|
|
494
550
|
// kind change at root: flat overrides below
|
|
495
551
|
const l = [];
|
|
496
|
-
|
|
552
|
+
let u;
|
|
553
|
+
if (s) u = [ ...t ]; else {
|
|
554
|
+
u = {};
|
|
555
|
+
for (const e of Reflect.ownKeys(t)) u[e] = t[e];
|
|
556
|
+
}
|
|
497
557
|
const match = (e, t) => {
|
|
498
558
|
if (!isWrappable(e) || !isWrappable(t)) return null;
|
|
499
559
|
if (rawValuesUsed && (isRawValue(e) || isRawValue(t))) return null;
|
|
500
560
|
if (Array.isArray(e) !== Array.isArray(t)) return null;
|
|
501
561
|
if (n) {
|
|
502
562
|
const i = n(e);
|
|
503
|
-
const
|
|
563
|
+
const r = n(t);
|
|
504
564
|
// SameValueZero (re-audit 3, P1-3): parity with the plain reconcile
|
|
505
565
|
// channel — NaN keys are self-equal.
|
|
506
|
-
if (i !== undefined &&
|
|
566
|
+
if (i !== undefined && r !== undefined && !sameKey(i, r)) return null;
|
|
507
567
|
}
|
|
508
|
-
return
|
|
568
|
+
return o.get(unwrapValue(e)) ?? null;
|
|
509
569
|
};
|
|
510
570
|
if (s) {
|
|
511
|
-
const e =
|
|
571
|
+
const e = r;
|
|
512
572
|
let i = null;
|
|
513
|
-
for (let
|
|
514
|
-
const
|
|
515
|
-
if (!isWrappable(
|
|
573
|
+
for (let r = 0; r < t.length; r++) {
|
|
574
|
+
const o = t[r];
|
|
575
|
+
if (!isWrappable(o)) continue;
|
|
516
576
|
let s;
|
|
517
577
|
if (n) {
|
|
518
|
-
const t = n(
|
|
578
|
+
const t = n(o);
|
|
519
579
|
if (t !== undefined) {
|
|
520
580
|
if (i === null) {
|
|
521
581
|
// Occurrence-aware index queues (re-audit 3, P1-3): parity with
|
|
@@ -523,40 +583,40 @@ function applyTentative(e, t, n) {
|
|
|
523
583
|
// occurrence, each view row consumed once.
|
|
524
584
|
i = new Map;
|
|
525
585
|
for (let t = 0; t < e.length; t++) {
|
|
526
|
-
const
|
|
527
|
-
if (isWrappable(
|
|
528
|
-
const e = n(
|
|
586
|
+
const r = unwrapValue(e[t]);
|
|
587
|
+
if (isWrappable(r)) {
|
|
588
|
+
const e = n(r);
|
|
529
589
|
if (e === undefined) continue;
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
590
|
+
const o = i.get(e);
|
|
591
|
+
if (o === undefined) i.set(e, t); else if (Array.isArray(o)) o.push(t); else i.set(e, [ o, t ]);
|
|
532
592
|
}
|
|
533
593
|
}
|
|
534
594
|
}
|
|
535
|
-
const
|
|
536
|
-
if (
|
|
537
|
-
s = unwrapValue(e[
|
|
538
|
-
if (
|
|
595
|
+
const r = i.get(t);
|
|
596
|
+
if (r === undefined) s = undefined; else if (Array.isArray(r)) {
|
|
597
|
+
s = unwrapValue(e[r.shift()]);
|
|
598
|
+
if (r.length === 1) i.set(t, r[0]);
|
|
539
599
|
} else {
|
|
540
|
-
s = unwrapValue(e[
|
|
600
|
+
s = unwrapValue(e[r]);
|
|
541
601
|
i.delete(t);
|
|
542
602
|
}
|
|
543
|
-
} else s = unwrapValue(e[
|
|
544
|
-
} else s = unwrapValue(e[
|
|
545
|
-
const a = match(s,
|
|
603
|
+
} else s = unwrapValue(e[r]);
|
|
604
|
+
} else s = unwrapValue(e[r]);
|
|
605
|
+
const a = match(s, o);
|
|
546
606
|
if (a !== null) {
|
|
547
607
|
// Keep the existing row in the slot (identity preserved); recurse.
|
|
548
|
-
u[
|
|
549
|
-
l.push([ a,
|
|
608
|
+
u[r] = unwrapValue(s);
|
|
609
|
+
l.push([ a, o ]);
|
|
550
610
|
}
|
|
551
611
|
}
|
|
552
612
|
} else {
|
|
553
613
|
for (const e of Reflect.ownKeys(t)) {
|
|
554
|
-
const n = unwrapValue(
|
|
614
|
+
const n = unwrapValue(r[e]);
|
|
555
615
|
const i = t[e];
|
|
556
|
-
const
|
|
557
|
-
if (
|
|
616
|
+
const o = match(n, i);
|
|
617
|
+
if (o !== null) {
|
|
558
618
|
u[e] = n;
|
|
559
|
-
l.push([
|
|
619
|
+
l.push([ o, i ]);
|
|
560
620
|
}
|
|
561
621
|
}
|
|
562
622
|
}
|
|
@@ -569,10 +629,4 @@ function applyTentative(e, t, n) {
|
|
|
569
629
|
for (let e = 0; e < l.length; e++) applyTentative(l[e][0], unwrapValue(l[e][1]), n);
|
|
570
630
|
}
|
|
571
631
|
|
|
572
|
-
function shallowWithSymbols(e) {
|
|
573
|
-
const t = {};
|
|
574
|
-
for (const n of Reflect.ownKeys(e)) t[n] = e[n];
|
|
575
|
-
return t;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
632
|
export { createOptimisticStoreNext, notifyOptimisticWrites, optimisticView, transitionHoldsOptimism };
|
|
@@ -12,11 +12,11 @@ import "../../core/effect.js";
|
|
|
12
12
|
|
|
13
13
|
import { CONFIG_AUTO_DISPOSE } from "../../core/constants.js";
|
|
14
14
|
|
|
15
|
-
import { $TARGET,
|
|
15
|
+
import { $TARGET, STORE_VALUE, markRawIngest, setWriteOverride } from "../store.js";
|
|
16
16
|
|
|
17
17
|
import { reconcileNextState } from "./reconcile.js";
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { storeSetterNext, wrapNext } from "./store.js";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Store rewrite — projections (§7/§7b): a projection is a computed store.
|