@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
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CONFIG_GUESS, CONFIG_OVERRIDE, EFFECT_RENDER, CONFIG_CHILDREN_FORBIDDEN, NOT_PENDING, CONFIG_SLOT_NODE, CONFIG_AUTO_DISPOSE, REACTIVE_REASK } from "../core/constants.js";
|
|
2
2
|
|
|
3
|
-
import { computed, joinPassAt, ext, untrack, read
|
|
3
|
+
import { computed, isEqual, joinPassAt, ext, untrack, read } from "../core/core.js";
|
|
4
4
|
|
|
5
|
-
import { pendingGuessOf, optimisticWrite } from "../core/lanes.js";
|
|
5
|
+
import { take, pendingGuessOf, optimisticWrite } from "../core/lanes.js";
|
|
6
6
|
|
|
7
7
|
import { getOwner } from "../core/owner.js";
|
|
8
8
|
|
|
9
|
-
import { GlobalQueue, txOf, insertSubs, joinFuture, resolveTx, passLane, list, laneDirty, notifyEpoch, schedule } from "../core/scheduler.js";
|
|
9
|
+
import { GlobalQueue, txOf, insertSubs, joinFuture, resolveTx, passLane, list, laneDirty, clock, merge, notifyEpoch, schedule } from "../core/scheduler.js";
|
|
10
10
|
|
|
11
11
|
import { runProjectionComputed } from "./projection.js";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { reconcileState } from "./reconcile.js";
|
|
14
|
+
|
|
15
|
+
import { wrap, nameStore, storeSetter, getContainerNode, getHasNode, getNode, LaneView, committed, unwrapValue, sameKey, resolveChainedRaw, targetsEqual, cloneRaw } from "./store.js";
|
|
14
16
|
|
|
15
17
|
import { installOptHooks } from "./hooks.js";
|
|
16
18
|
|
|
17
|
-
import { $OWNER } from "./target.js";
|
|
19
|
+
import { $OWNER, lookupTarget } from "./target.js";
|
|
18
20
|
|
|
19
21
|
import { $TARGET, markRawIngest, $TRACK, isWrappable } from "./types.js";
|
|
20
22
|
|
|
@@ -75,102 +77,104 @@ function notifyOptimisticWrites(e, n) {
|
|
|
75
77
|
// The view the user saw: the committed frame (a staging adopted eagerly
|
|
76
78
|
// is not it) with the lanes' values and the tick's own guesses over it.
|
|
77
79
|
const t = writerBacking(e);
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
+
const r = optimisticView(e, t, laneValueOf, true);
|
|
81
|
+
const i = Array.isArray(n);
|
|
80
82
|
// A chained target's nodes are links (§7b): their committed value is
|
|
81
83
|
// never served and never learns the inner store's commits — yet a guess
|
|
82
84
|
// is judged against it, and reverts to it (#3672). Hand the node the
|
|
83
85
|
// inner store's truth for its key before guessing over it.
|
|
84
86
|
const o = e.ch ? e.v : null;
|
|
85
|
-
const guess = (e, n,
|
|
86
|
-
if (o !== null) e.le =
|
|
87
|
+
const guess = (e, n, r, i) => {
|
|
88
|
+
if (o !== null) e.le = r ? n in t : unwrapValue(t[n]);
|
|
87
89
|
// By value: a function leaf is a value, not an updater (#3017).
|
|
88
|
-
optimisticWrite(e, () =>
|
|
90
|
+
optimisticWrite(e, () => i);
|
|
89
91
|
};
|
|
90
92
|
let s = false;
|
|
91
93
|
for (const t of Reflect.ownKeys(n)) {
|
|
92
|
-
if (
|
|
94
|
+
if (i && t === "length" || t === $OWNER) continue;
|
|
93
95
|
// A chained draft holds the inner store's raws; a raw is the inner slot it
|
|
94
96
|
// resolves to (as a read of it serves) — the row a held write staged is
|
|
95
97
|
// the same row, not a guess over the slot (#3796).
|
|
96
|
-
const
|
|
97
|
-
const f = unwrapValue(o &&
|
|
98
|
-
if (!(t in
|
|
98
|
+
const a = n[t];
|
|
99
|
+
const f = unwrapValue(o && a ? resolveChainedRaw(e, t, a) : a);
|
|
100
|
+
if (!(t in r)) {
|
|
99
101
|
guess(getNode(e, t), t, false, f);
|
|
100
102
|
guess(getHasNode(e, t), t, true, true);
|
|
101
103
|
s = true;
|
|
102
104
|
} else {
|
|
103
|
-
const n =
|
|
104
|
-
const
|
|
105
|
-
if (!isEqual(
|
|
105
|
+
const n = r[t];
|
|
106
|
+
const a = unwrapValue(o && n ? resolveChainedRaw(e, t, n) : n);
|
|
107
|
+
if (!isEqual(a, f) && !targetsEqual(a, f)) {
|
|
106
108
|
guess(getNode(e, t), t, false, f);
|
|
107
|
-
if (
|
|
109
|
+
if (i) s = true;
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
|
-
for (const t of Reflect.ownKeys(
|
|
112
|
-
if (
|
|
113
|
+
for (const t of Reflect.ownKeys(r)) {
|
|
114
|
+
if (i && t === "length" || t === $OWNER) continue;
|
|
113
115
|
if (t in n) continue;
|
|
114
116
|
guess(getNode(e, t), t, false, undefined);
|
|
115
117
|
guess(getHasNode(e, t), t, true, false);
|
|
116
118
|
s = true;
|
|
117
119
|
}
|
|
118
|
-
if (
|
|
120
|
+
if (i && r.length !== n.length) {
|
|
119
121
|
guess(getNode(e, "length"), "length", false, n.length);
|
|
120
122
|
s = true;
|
|
121
123
|
}
|
|
122
124
|
if (s) {
|
|
123
|
-
const o =
|
|
125
|
+
const o = i ? n.map(unwrapValue) : shallowKeys(n);
|
|
124
126
|
// The rows the setter removed from the view it saw, by key: a later
|
|
125
127
|
// re-base over another action's truth must not bring them back. A set
|
|
126
128
|
// (empty or not) marks a keyed arrangement guess (`fold`).
|
|
127
129
|
let s = null;
|
|
128
|
-
const
|
|
129
|
-
if (
|
|
130
|
+
const a = e.fam.key;
|
|
131
|
+
if (i && a !== null && a !== undefined) {
|
|
130
132
|
s = new Set;
|
|
131
133
|
const e = new Set;
|
|
132
134
|
for (let n = 0; n < o.length; n++) {
|
|
133
|
-
const t = isWrappable(o[n]) ?
|
|
135
|
+
const t = isWrappable(o[n]) ? a(o[n]) : undefined;
|
|
134
136
|
if (t !== undefined) e.add(t);
|
|
135
137
|
}
|
|
136
|
-
for (let n = 0; n <
|
|
137
|
-
const t = unwrapValue(
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
138
|
+
for (let n = 0; n < r.length; n++) {
|
|
139
|
+
const t = unwrapValue(r[n]);
|
|
140
|
+
const i = isWrappable(t) ? a(t) : undefined;
|
|
141
|
+
if (i !== undefined && !e.has(i)) s.add(i);
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
optimisticWrite(getContainerNode(e), new LaneView(o, t, s));
|
|
143
145
|
}
|
|
144
146
|
if (e.dk !== null) optimisticWrite(e.dk, {});
|
|
145
147
|
markOverlaid(e);
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
148
|
+
const a = optStaged.get(e);
|
|
149
|
+
if (a === undefined) return null;
|
|
148
150
|
optStaged.delete(e);
|
|
149
|
-
return
|
|
151
|
+
return a;
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
/** The container comparator's lane arm (store.ts `containerEquals`): an
|
|
153
155
|
* arrangement guess (`LaneView`) against a landing or another arrangement,
|
|
154
156
|
* by row identity — by key when the family has one. */ function arrangement(e, n, t) {
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
158
|
-
return arrangementEqual(
|
|
157
|
+
const r = n instanceof LaneView;
|
|
158
|
+
const i = t instanceof LaneView;
|
|
159
|
+
if (!r && !i) return false;
|
|
160
|
+
return arrangementEqual(r ? n.rows : n, i ? t.rows : t, e.fam);
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
function rowSame(e, n, t) {
|
|
162
164
|
if (isEqual(e, n) || targetsEqual(e, n)) return true;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
165
|
+
const r = t.key;
|
|
166
|
+
if (!r || !isWrappable(e) || !isWrappable(n)) return false;
|
|
167
|
+
// A row the store already serves is its proxy's row: a reader (a list)
|
|
168
|
+
// holds that proxy, so another object at the index is another row, by key
|
|
169
|
+
// or contents alike — a guess's fresh object wraps a new proxy. A landing's
|
|
170
|
+
// object has been adopted by the row it pairs with, so it serves that
|
|
171
|
+
// row's proxy. Only rows no proxy serves yet pair by key, keyless rows by
|
|
172
|
+
// position — reconcile's fallback (#3898).
|
|
173
|
+
const i = lookupTarget(e = unwrapValue(e), t);
|
|
174
|
+
const o = lookupTarget(n = unwrapValue(n), t);
|
|
175
|
+
if (i || o) return i === o;
|
|
176
|
+
// (Two keyless rows: `undefined` is the same key.)
|
|
177
|
+
return sameKey(r(e), r(n));
|
|
174
178
|
}
|
|
175
179
|
|
|
176
180
|
/** Whether reconcile would write anything into `a` from `b`: wrappable
|
|
@@ -183,7 +187,7 @@ function rowSame(e, n, t) {
|
|
|
183
187
|
n = unwrapValue(n);
|
|
184
188
|
const t = Object.keys(e);
|
|
185
189
|
if (Array.isArray(e) !== Array.isArray(n) || t.length !== Object.keys(n).length) return false;
|
|
186
|
-
for (const
|
|
190
|
+
for (const r of t) if (!hasOwn.call(n, r) || !echoes(e[r], n[r])) return false;
|
|
187
191
|
return true;
|
|
188
192
|
}
|
|
189
193
|
|
|
@@ -201,13 +205,13 @@ function arrangementEqual(e, n, t) {
|
|
|
201
205
|
if (Array.isArray(e) !== Array.isArray(n)) return false;
|
|
202
206
|
if (Array.isArray(e)) {
|
|
203
207
|
if (e.length !== n.length) return false;
|
|
204
|
-
for (let
|
|
208
|
+
for (let r = 0; r < e.length; r++) if (!rowSame(e[r], n[r], t)) return false;
|
|
205
209
|
return true;
|
|
206
210
|
}
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
if (
|
|
210
|
-
for (const e of
|
|
211
|
+
const r = Reflect.ownKeys(e).filter(e => e !== $OWNER);
|
|
212
|
+
const i = Reflect.ownKeys(n).filter(e => e !== $OWNER);
|
|
213
|
+
if (r.length !== i.length) return false;
|
|
214
|
+
for (const e of r) if (!(e in n)) return false;
|
|
211
215
|
return true;
|
|
212
216
|
}
|
|
213
217
|
|
|
@@ -216,47 +220,70 @@ function arrangementEqual(e, n, t) {
|
|
|
216
220
|
* the matched row's draft; objects recurse into a wrappable pair, assign
|
|
217
221
|
* otherwise; keys `incoming` lacks are deleted. */ function reconcileDraft(e, n, t) {
|
|
218
222
|
if (Array.isArray(n)) {
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
for (let n = 0; n <
|
|
223
|
+
const r = e.length;
|
|
224
|
+
const i = new Array(r);
|
|
225
|
+
for (let n = 0; n < r; n++) i[n] = e[n];
|
|
222
226
|
let o = null;
|
|
223
227
|
if (t !== null) {
|
|
224
228
|
o = new Map;
|
|
225
|
-
for (let e = 0; e <
|
|
226
|
-
const n = unwrapValue(
|
|
229
|
+
for (let e = 0; e < r; e++) {
|
|
230
|
+
const n = unwrapValue(i[e]);
|
|
227
231
|
if (!isWrappable(n)) continue;
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
232
|
+
const r = t(n);
|
|
233
|
+
if (r !== undefined && !o.has(r)) o.set(r, i[e]);
|
|
230
234
|
}
|
|
231
235
|
}
|
|
232
236
|
for (let s = 0; s < n.length; s++) {
|
|
233
|
-
const
|
|
237
|
+
const a = n[s];
|
|
234
238
|
let f;
|
|
235
|
-
if (isWrappable(
|
|
239
|
+
if (isWrappable(a)) {
|
|
236
240
|
if (o !== null) {
|
|
237
|
-
const e = t(
|
|
241
|
+
const e = t(a);
|
|
238
242
|
if (e !== undefined) f = o.get(e);
|
|
239
|
-
} else if (s <
|
|
243
|
+
} else if (s < r && isWrappable(unwrapValue(i[s]))) f = i[s];
|
|
240
244
|
}
|
|
241
245
|
if (f !== undefined) {
|
|
242
|
-
if (unwrapValue(f) !==
|
|
246
|
+
if (unwrapValue(f) !== a) reconcileDraft(f, a, t);
|
|
243
247
|
if (e[s] !== f) e[s] = f;
|
|
244
|
-
} else if (unwrapValue(e[s]) !==
|
|
248
|
+
} else if (unwrapValue(e[s]) !== a) e[s] = a;
|
|
245
249
|
}
|
|
246
250
|
if (e.length !== n.length) e.length = n.length;
|
|
247
251
|
return;
|
|
248
252
|
}
|
|
249
|
-
for (const
|
|
250
|
-
const
|
|
251
|
-
const o = e[
|
|
253
|
+
for (const r of Object.keys(n)) {
|
|
254
|
+
const i = n[r];
|
|
255
|
+
const o = e[r];
|
|
252
256
|
const s = unwrapValue(o);
|
|
253
|
-
if (isWrappable(
|
|
254
|
-
if (s !==
|
|
255
|
-
} else if (s !==
|
|
257
|
+
if (isWrappable(i) && isWrappable(s) && Array.isArray(i) === Array.isArray(s) && (t === null || Array.isArray(i) || sameKey(t(i), t(s)))) {
|
|
258
|
+
if (s !== i) reconcileDraft(o, i, t);
|
|
259
|
+
} else if (s !== i) e[r] = i;
|
|
256
260
|
}
|
|
257
261
|
for (const t of Object.keys(e)) if (!(t in n)) delete e[t];
|
|
258
262
|
}
|
|
259
263
|
|
|
264
|
+
/** Core asks (lanes `laneWrite`): the family's derive, re-asking in a lane of
|
|
265
|
+
* another action than the one that guessed this slot, writes it — that
|
|
266
|
+
* lane's new question, which the guess (over the old one) does not answer:
|
|
267
|
+
* the slot is the lane's derivation from here (F-6, #4336; lanes
|
|
268
|
+
* `laneStage` for a node's own pass). */ GlobalQueue.fn = (e, n) => {
|
|
269
|
+
const t = e.O;
|
|
270
|
+
if (n) {
|
|
271
|
+
// The guess comes back (lanes `endLanes`): on the slot's node now — the
|
|
272
|
+
// node itself, or the one a read made after it was released.
|
|
273
|
+
const n = e.gt;
|
|
274
|
+
markOverlaid(t);
|
|
275
|
+
return n === $TRACK ? getContainerNode(t) : e.je === isEqual ? getHasNode(t, n) : getNode(t, n);
|
|
276
|
+
}
|
|
277
|
+
const r = t?.fam?.node?.T?.Je;
|
|
278
|
+
const i = txOf(e);
|
|
279
|
+
if (!r?.V || sameAction(r, i)) return false;
|
|
280
|
+
// The re-ask takes the target's guess whole: the slot is the lane's
|
|
281
|
+
// derivation; its other guessed slots show their truth in the lane's
|
|
282
|
+
// world (lanes `take`).
|
|
283
|
+
for (const n of nodesOf(t)) if (n?.l & CONFIG_GUESS && txOf(n) === i) take(n, r, n !== e);
|
|
284
|
+
return true;
|
|
285
|
+
};
|
|
286
|
+
|
|
260
287
|
/** Q-D (plan sec. 39): an OLDER question's truth landed beneath an arrangement
|
|
261
288
|
* guess (`laneWrite` held it — another action's landing is not this guess's
|
|
262
289
|
* answer). The guess is re-based over it: order from the guess, rows from
|
|
@@ -270,54 +297,54 @@ function arrangementEqual(e, n, t) {
|
|
|
270
297
|
* node is born for it as the lane's derivation), on `length` only when the
|
|
271
298
|
* committed length differs, and as the `LaneView`'s base for structural
|
|
272
299
|
* reads. Unkeyed families compose
|
|
273
|
-
* positionally — the guess stands as written (the documented residue). */ GlobalQueue.
|
|
300
|
+
* positionally — the guess stands as written (the documented residue). */ GlobalQueue.Nn = (e, n, t) => {
|
|
274
301
|
if (!t) return fold(e, n);
|
|
275
|
-
const
|
|
276
|
-
if (
|
|
277
|
-
const
|
|
278
|
-
if (!(
|
|
279
|
-
if (!Array.isArray(n) || !Array.isArray(
|
|
280
|
-
|
|
302
|
+
const r = e.O;
|
|
303
|
+
if (r === undefined || e.gt !== $TRACK) return;
|
|
304
|
+
const i = e.T.V;
|
|
305
|
+
if (!(i instanceof LaneView)) return;
|
|
306
|
+
if (!Array.isArray(n) || !Array.isArray(i.rows)) {
|
|
307
|
+
i.base = n;
|
|
281
308
|
return;
|
|
282
309
|
}
|
|
283
|
-
const o =
|
|
310
|
+
const o = r.fam?.key ?? null;
|
|
284
311
|
if (o === null) return;
|
|
285
312
|
const s = new Map;
|
|
286
313
|
for (let e = 0; e < n.length; e++) {
|
|
287
314
|
const t = n[e];
|
|
288
|
-
const
|
|
289
|
-
if (
|
|
315
|
+
const r = isWrappable(t) ? o(t) : undefined;
|
|
316
|
+
if (r !== undefined && !s.has(r)) s.set(r, t);
|
|
290
317
|
}
|
|
291
|
-
const
|
|
318
|
+
const a = new Set;
|
|
292
319
|
const f = [];
|
|
293
|
-
const
|
|
294
|
-
for (let e = 0; e <
|
|
295
|
-
const n =
|
|
320
|
+
const l = i.rows;
|
|
321
|
+
for (let e = 0; e < l.length; e++) {
|
|
322
|
+
const n = l[e];
|
|
296
323
|
const t = isWrappable(n) ? o(n) : undefined;
|
|
297
|
-
const
|
|
298
|
-
if (
|
|
299
|
-
f.push(
|
|
300
|
-
|
|
324
|
+
const r = t !== undefined ? s.get(t) : undefined;
|
|
325
|
+
if (r !== undefined) {
|
|
326
|
+
f.push(r);
|
|
327
|
+
a.add(t);
|
|
301
328
|
} else f.push(n);
|
|
302
329
|
}
|
|
303
|
-
const
|
|
330
|
+
const u = i.removed;
|
|
304
331
|
for (let e = 0; e < n.length; e++) {
|
|
305
332
|
const t = n[e];
|
|
306
|
-
const
|
|
307
|
-
if (
|
|
333
|
+
const r = isWrappable(t) ? o(t) : undefined;
|
|
334
|
+
if (r !== undefined && !a.has(r) && (u === null || !u.has(r))) f.push(t);
|
|
308
335
|
}
|
|
309
|
-
|
|
310
|
-
|
|
336
|
+
i.rows = f;
|
|
337
|
+
i.base = f;
|
|
311
338
|
const c = txOf(e);
|
|
312
|
-
const d = committed(
|
|
313
|
-
const p =
|
|
339
|
+
const d = committed(r);
|
|
340
|
+
const p = r.n;
|
|
314
341
|
for (let e = 0; e < f.length; e++) {
|
|
315
342
|
const n = p?.[e];
|
|
316
343
|
// A slot the lane does not own shows the committed row: it becomes the
|
|
317
344
|
// lane's only where the composed row is another row (by key — the same
|
|
318
345
|
// key's new object is the same row, its target adopted it).
|
|
319
346
|
if (n === undefined) {
|
|
320
|
-
if (!sameRow(d[e], f[e], o)) laneSet(getNode(
|
|
347
|
+
if (!sameRow(d[e], f[e], o)) laneSet(getNode(r, e), f[e], c);
|
|
321
348
|
} else if (!sameRow(n.l & CONFIG_OVERRIDE ? n.T.V : d[e], f[e], o)) laneSet(n, f[e], c);
|
|
322
349
|
}
|
|
323
350
|
if (p !== null) {
|
|
@@ -339,16 +366,16 @@ function arrangementEqual(e, n, t) {
|
|
|
339
366
|
* (A17): only truth readers are told. True: the lane stages the truth
|
|
340
367
|
* beneath (`laneWrite`). */ function fold(e, n) {
|
|
341
368
|
const t = e.O?.k;
|
|
342
|
-
const
|
|
369
|
+
const r = txOf(e);
|
|
343
370
|
// A guess the screen never showed is void at the correction (D-34); a
|
|
344
371
|
// keyed arrangement guess's view carries its removed-keys set.
|
|
345
|
-
if (!
|
|
346
|
-
const
|
|
347
|
-
if ((!e.je || !e.je(
|
|
348
|
-
|
|
372
|
+
if (!r.Nt || !(t?.l & CONFIG_OVERRIDE) || !t.T.V?.removed) return false;
|
|
373
|
+
const i = e.T.V;
|
|
374
|
+
if ((!e.je || !e.je(i, n)) && !GlobalQueue.sn(i, n)) {
|
|
375
|
+
r.folded = foldedRead;
|
|
349
376
|
insertSubs(e);
|
|
350
|
-
} else GlobalQueue.tn?.(
|
|
351
|
-
joinFuture(resolveTx(
|
|
377
|
+
} else GlobalQueue.tn?.(r);
|
|
378
|
+
joinFuture(resolveTx(r._parent ?? r));
|
|
352
379
|
return true;
|
|
353
380
|
}
|
|
354
381
|
|
|
@@ -365,33 +392,54 @@ function arrangementEqual(e, n, t) {
|
|
|
365
392
|
const t = txOf(n);
|
|
366
393
|
joinPassAt(e, resolveTx(t._parent ?? t));
|
|
367
394
|
}
|
|
368
|
-
if (n.
|
|
369
|
-
return n.l & CONFIG_SLOT_NODE ? GlobalQueue.
|
|
395
|
+
if (n.ue !== NOT_PENDING) return n.ue;
|
|
396
|
+
return n.l & CONFIG_SLOT_NODE ? GlobalQueue.an(n) : n.le;
|
|
370
397
|
}
|
|
371
398
|
|
|
372
399
|
function sameRow(e, n, t) {
|
|
373
400
|
if (e === n) return true;
|
|
374
401
|
if (!isWrappable(e) || !isWrappable(n)) return false;
|
|
375
|
-
const
|
|
376
|
-
return
|
|
402
|
+
const r = t(unwrapValue(e));
|
|
403
|
+
return r !== undefined && sameKey(r, t(unwrapValue(n)));
|
|
377
404
|
}
|
|
378
405
|
|
|
379
406
|
/** A composed value becomes the lane's on `node`: a guess re-based, or a
|
|
380
407
|
* node that was not the lane's becomes its derivation (its committed value
|
|
381
408
|
* untouched beneath; the lane's end commits the latest). */ function laneSet(e, n, t) {
|
|
382
|
-
const
|
|
409
|
+
const r = ext(e);
|
|
383
410
|
if (!(e.l & CONFIG_OVERRIDE)) {
|
|
384
411
|
e.l |= CONFIG_OVERRIDE;
|
|
385
412
|
list(e, t);
|
|
386
|
-
} else if (
|
|
387
|
-
|
|
413
|
+
} else if (r.V === n) return;
|
|
414
|
+
r.V = n;
|
|
388
415
|
if (e.o !== null) {
|
|
389
416
|
insertSubs(e);
|
|
390
417
|
laneDirty(e, t);
|
|
391
418
|
}
|
|
392
419
|
}
|
|
393
420
|
|
|
421
|
+
/** D-34 for a per-key guess (#4329): the derive's draft wrote new objects
|
|
422
|
+
* over a target holding guessed rows (`d.length = 0; d.push(...rows)`, or
|
|
423
|
+
* `d.issues = rows`). The container on the way down to each guessed row
|
|
424
|
+
* reconciles the new object at its slot — in a keyed list, the one with
|
|
425
|
+
* the row's key — exactly as a returned landing would: rows match by key,
|
|
426
|
+
* a guessed row keeps its target, and its leaves are judged against their
|
|
427
|
+
* guesses. Rows nothing guessed are left as the draft wrote them. */ function pairRows(e, n) {
|
|
428
|
+
const t = e.fam.key;
|
|
429
|
+
for (let r of e.fam.overlaid ?? []) {
|
|
430
|
+
// The guessed row, or its ancestor directly under `t` (none: skip).
|
|
431
|
+
while (r.u !== e) if ((r = r.u) === null) break;
|
|
432
|
+
if (r === null) continue;
|
|
433
|
+
const i = t?.(r.v);
|
|
434
|
+
const o = Array.isArray(n) ? i !== undefined && n.find(e => isWrappable(e) && sameKey(t(e), i)) : n[r.pk];
|
|
435
|
+
// A new object only: not `c`'s own, nor one another row serves
|
|
436
|
+
// (pairing it would alias the two).
|
|
437
|
+
if (isWrappable(o) && !lookupTarget(o, e.fam)) reconcileState(o, r.px, t, true);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
394
441
|
installOptHooks({
|
|
442
|
+
pair: pairRows,
|
|
395
443
|
draft: optimisticDraft,
|
|
396
444
|
writes: notifyOptimisticWrites,
|
|
397
445
|
arrangement: arrangement,
|
|
@@ -399,9 +447,53 @@ installOptHooks({
|
|
|
399
447
|
view: (e, n, t = false) => t ? optimisticView(e, n, laneValueOf, true) : optimisticView(e, n),
|
|
400
448
|
has: optimisticHas,
|
|
401
449
|
descriptor: optimisticOwnDescriptor,
|
|
402
|
-
sweep: sweepOverlaid
|
|
450
|
+
sweep: sweepOverlaid,
|
|
451
|
+
// A new question (not a `refresh()`) asked over guesses an earlier frame
|
|
452
|
+
// opened, which answer the old one (D-34, #4336). Stamped on `fam.asked`
|
|
453
|
+
// with this flush's `clock`, so it lapses when the flush ends: `clock + 1`
|
|
454
|
+
// from mainline over a live guess — a frame still reading a guess at the
|
|
455
|
+
// seam shows it beside the new input, so the flush holds with the guess's
|
|
456
|
+
// action (`park`); `~clock` from a lane with no guess of its own, or with
|
|
457
|
+
// no live guess left — the lane waits for its answer (F-6), as for a
|
|
458
|
+
// guessed `createOptimistic` (lanes `laneStage`). The guess's own lane
|
|
459
|
+
// asks nothing new and stamps nothing.
|
|
460
|
+
ask: e => {
|
|
461
|
+
if (e.node.R & REACTIVE_REASK || !e.overlaid?.size) return;
|
|
462
|
+
// Stamped with the flush (`clock`): it lapses when the flush ends.
|
|
463
|
+
let n = ~clock;
|
|
464
|
+
for (const t of e.overlaid) {
|
|
465
|
+
const e = laneNodeOf(t);
|
|
466
|
+
if (e) {
|
|
467
|
+
if (passLane === null) n = clock + 1; else if (sameAction(txOf(e), passLane)) return;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if ((e.asked = n) > 0) joinFuture(null);
|
|
471
|
+
},
|
|
472
|
+
// Over a new question the guesses do not answer (`asked`): from mainline
|
|
473
|
+
// only a target carrying a guess stands in — a read of one with none is an
|
|
474
|
+
// ordinary pending read (F-2), so a keyed store's other keys wait for their
|
|
475
|
+
// answer, not for the guess's action; in a lane with no guess of its own,
|
|
476
|
+
// or with no live guess left, none does (F-6).
|
|
477
|
+
stands: e => {
|
|
478
|
+
const n = e.fam.overlaid;
|
|
479
|
+
const t = e.fam.asked;
|
|
480
|
+
return !!n?.size && (t !== clock + 1 || passLane !== null || n.has(e)) && t !== ~clock;
|
|
481
|
+
},
|
|
482
|
+
// Mainline hold (`clock + 1`): the flush joins the action of each guess a
|
|
483
|
+
// reader still reads at the seam; a reader that moved off it (a keyed
|
|
484
|
+
// store's other key) holds nothing.
|
|
485
|
+
park: e => {
|
|
486
|
+
for (const n of overlaidFamilies) if (n.asked === clock + 1) for (const t of n.overlaid) {
|
|
487
|
+
const n = laneNodeOf(t, true);
|
|
488
|
+
if (n) merge(txOf(n)._parent, e);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
403
491
|
});
|
|
404
492
|
|
|
493
|
+
/** Two lanes of one action (their parents resolved through merges). */ function sameAction(e, n) {
|
|
494
|
+
return resolveTx(e._parent) === resolveTx(n._parent);
|
|
495
|
+
}
|
|
496
|
+
|
|
405
497
|
function shallowKeys(e) {
|
|
406
498
|
const n = {};
|
|
407
499
|
for (const t of Reflect.ownKeys(e)) if (t !== $OWNER) n[t] = unwrapValue(e[t]);
|
|
@@ -424,7 +516,7 @@ function sweepOverlaid() {
|
|
|
424
516
|
if (overlaidFamilies.size === 0) return;
|
|
425
517
|
for (const e of overlaidFamilies) {
|
|
426
518
|
const n = e.overlaid;
|
|
427
|
-
for (const e of n) if (!
|
|
519
|
+
for (const e of n) if (!laneNodeOf(e)) {
|
|
428
520
|
n.delete(e);
|
|
429
521
|
if (e.ch) rebaseChained(e);
|
|
430
522
|
// The container shows its backing again (a shown arrangement guess
|
|
@@ -443,36 +535,36 @@ function sweepOverlaid() {
|
|
|
443
535
|
* re-derive the readers whose shown value it is not. */ function rebaseChained(e) {
|
|
444
536
|
const n = e.v;
|
|
445
537
|
let t = false;
|
|
446
|
-
const
|
|
447
|
-
if (
|
|
448
|
-
const
|
|
538
|
+
const r = e.n;
|
|
539
|
+
if (r !== null) for (const e of Reflect.ownKeys(r)) {
|
|
540
|
+
const i = r[e];
|
|
449
541
|
const o = untrack(() => unwrapValue(n[e]));
|
|
450
|
-
if (
|
|
451
|
-
|
|
542
|
+
if (i.je && i.je(i.le, o)) continue;
|
|
543
|
+
i.le = o;
|
|
452
544
|
// (The lane's end notified it this epoch when the shown value and the
|
|
453
545
|
// covered one differed — its readers re-derive already.)
|
|
454
|
-
if (
|
|
455
|
-
insertSubs(
|
|
546
|
+
if (i.o !== null && i.Dt !== notifyEpoch) {
|
|
547
|
+
insertSubs(i);
|
|
456
548
|
t = true;
|
|
457
549
|
}
|
|
458
550
|
}
|
|
459
|
-
const
|
|
460
|
-
if (
|
|
461
|
-
const
|
|
551
|
+
const i = e.h;
|
|
552
|
+
if (i !== null) for (const e of Reflect.ownKeys(i)) {
|
|
553
|
+
const r = i[e];
|
|
462
554
|
const o = untrack(() => e in n);
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
if (
|
|
466
|
-
insertSubs(
|
|
555
|
+
if (r.le === o) continue;
|
|
556
|
+
r.le = o;
|
|
557
|
+
if (r.o !== null && r.Dt !== notifyEpoch) {
|
|
558
|
+
insertSubs(r);
|
|
467
559
|
t = true;
|
|
468
560
|
}
|
|
469
561
|
}
|
|
470
562
|
// The arrangement the readers saw (the shown guess) against the inner's
|
|
471
563
|
// now: a structural reader re-derives when they differ.
|
|
472
564
|
const o = e.k;
|
|
473
|
-
if (o !== null && o.o !== null && o.le instanceof LaneView && o.
|
|
474
|
-
const
|
|
475
|
-
if (!arrangementEqual(o.le.rows,
|
|
565
|
+
if (o !== null && o.o !== null && o.le instanceof LaneView && o.Dt !== notifyEpoch) {
|
|
566
|
+
const r = Array.isArray(n) ? untrack(() => n.map(unwrapValue)) : n;
|
|
567
|
+
if (!arrangementEqual(o.le.rows, r, e.fam)) {
|
|
476
568
|
insertSubs(o);
|
|
477
569
|
t = true;
|
|
478
570
|
}
|
|
@@ -480,14 +572,18 @@ function sweepOverlaid() {
|
|
|
480
572
|
if (t) schedule();
|
|
481
573
|
}
|
|
482
574
|
|
|
483
|
-
function
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
575
|
+
/** `t`'s nodes (a missing container or deep node: null). */ function nodesOf(e) {
|
|
576
|
+
const n = [ e.k, e.dk ];
|
|
577
|
+
for (const t of [ e.n, e.h ]) if (t !== null) for (const e of Reflect.ownKeys(t)) n.push(t[e]);
|
|
578
|
+
return n;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/** A node of `t` carrying a lane value, if any (`read`: one a reader's
|
|
582
|
+
* last pass read). */ function laneNodeOf(e, n) {
|
|
583
|
+
for (const t of nodesOf(e)) if (t?.l & CONFIG_OVERRIDE) {
|
|
584
|
+
if (!n) return t;
|
|
585
|
+
for (let e = t.o; e !== null; e = e.t) if (e.de === e.u.Ne) return t;
|
|
586
|
+
}
|
|
491
587
|
}
|
|
492
588
|
|
|
493
589
|
function overlaid(e) {
|
|
@@ -505,8 +601,8 @@ function overlaid(e) {
|
|
|
505
601
|
if (!overlaid(e)) return undefined;
|
|
506
602
|
const t = e.h?.[n];
|
|
507
603
|
if (t !== undefined && t.l & CONFIG_OVERRIDE) return !!shown(t);
|
|
508
|
-
const
|
|
509
|
-
if (
|
|
604
|
+
const r = e.n?.[n];
|
|
605
|
+
if (r !== undefined && r.l & CONFIG_OVERRIDE) return shown(r) !== undefined;
|
|
510
606
|
return undefined;
|
|
511
607
|
}
|
|
512
608
|
|
|
@@ -516,17 +612,17 @@ function overlaid(e) {
|
|
|
516
612
|
const t = optimisticHas(e, n);
|
|
517
613
|
if (t === undefined) return undefined;
|
|
518
614
|
if (!t) return null;
|
|
519
|
-
const
|
|
520
|
-
const
|
|
615
|
+
const r = e.n?.[n];
|
|
616
|
+
const i = r !== undefined && r.l & CONFIG_OVERRIDE ? shown(r) : e.v[n];
|
|
521
617
|
// An array's `length` is reported as the array has it (non-configurable).
|
|
522
618
|
if (n === "length" && Array.isArray(e.v)) return {
|
|
523
|
-
value:
|
|
619
|
+
value: i,
|
|
524
620
|
writable: true,
|
|
525
621
|
enumerable: false,
|
|
526
622
|
configurable: false
|
|
527
623
|
};
|
|
528
624
|
return {
|
|
529
|
-
value:
|
|
625
|
+
value: i,
|
|
530
626
|
writable: true,
|
|
531
627
|
enumerable: true,
|
|
532
628
|
configurable: true
|
|
@@ -539,18 +635,18 @@ function overlaid(e) {
|
|
|
539
635
|
* length. Returns `src` itself when nothing is overlaid. */ function optimisticView(e, n, t = shown,
|
|
540
636
|
// The writer's view: the unflushed guesses too (on nodes that are no
|
|
541
637
|
// lane's yet) — what the next write composes on (#3665).
|
|
542
|
-
|
|
543
|
-
if (!
|
|
544
|
-
let
|
|
545
|
-
const ensure = () =>
|
|
638
|
+
r = false) {
|
|
639
|
+
if (!r && !overlaid(e)) return n;
|
|
640
|
+
let i = null;
|
|
641
|
+
const ensure = () => i ??= Array.isArray(n) ? [ ...n ] : {
|
|
546
642
|
...n
|
|
547
643
|
};
|
|
548
644
|
const o = e.n;
|
|
549
645
|
if (o !== null) {
|
|
550
646
|
for (const e of Reflect.ownKeys(o)) {
|
|
551
|
-
const
|
|
647
|
+
const i = o[e];
|
|
552
648
|
let s;
|
|
553
|
-
if (
|
|
649
|
+
if (i.l & CONFIG_OVERRIDE) s = t(i); else if (!r || (s = pendingGuessOf(i)) === NOT_PENDING) continue;
|
|
554
650
|
if (e === "length" && Array.isArray(n)) {
|
|
555
651
|
if (n.length !== s) ensure().length = s;
|
|
556
652
|
} else if (!isEqual(n[e], s) || !hasOwn.call(n, e)) {
|
|
@@ -562,54 +658,54 @@ i = false) {
|
|
|
562
658
|
if (s !== null) {
|
|
563
659
|
for (const e of Reflect.ownKeys(s)) {
|
|
564
660
|
const o = s[e];
|
|
565
|
-
let
|
|
566
|
-
if (o.l & CONFIG_OVERRIDE)
|
|
567
|
-
const f = !!
|
|
568
|
-
const
|
|
569
|
-
if (!f && e in
|
|
661
|
+
let a;
|
|
662
|
+
if (o.l & CONFIG_OVERRIDE) a = t(o); else if (!r || (a = pendingGuessOf(o)) === NOT_PENDING) continue;
|
|
663
|
+
const f = !!a;
|
|
664
|
+
const l = i ?? n;
|
|
665
|
+
if (!f && e in l) delete ensure()[e];
|
|
570
666
|
}
|
|
571
667
|
}
|
|
572
|
-
return
|
|
668
|
+
return i ?? n;
|
|
573
669
|
}
|
|
574
670
|
|
|
575
671
|
function createOptimisticStore(e, n, t) {
|
|
576
|
-
const
|
|
577
|
-
const
|
|
672
|
+
const r = typeof e === "function";
|
|
673
|
+
const i = r ? t : n;
|
|
578
674
|
// Seedless derived form: the root starts as an empty record (see
|
|
579
675
|
// createProjectionInternal).
|
|
580
|
-
const o =
|
|
581
|
-
const s =
|
|
582
|
-
const
|
|
676
|
+
const o = r ? n || {} : e;
|
|
677
|
+
const s = i?.key === undefined ? "id" : i.key;
|
|
678
|
+
const a = {
|
|
583
679
|
map: new WeakMap,
|
|
584
680
|
node: null,
|
|
585
681
|
live: new Set,
|
|
586
|
-
shallow: !!
|
|
682
|
+
shallow: !!i?.shallow,
|
|
587
683
|
opt: true,
|
|
588
684
|
key: typeof s === "function" ? s : s === null ? null : e => isWrappable(e) ? e[s] : undefined
|
|
589
685
|
};
|
|
590
|
-
const f = wrap(o, null, null,
|
|
591
|
-
nameStore(f,
|
|
592
|
-
if (
|
|
686
|
+
const f = wrap(o, null, null, a);
|
|
687
|
+
nameStore(f, i?.name, i?.loc);
|
|
688
|
+
if (a.shallow) {
|
|
593
689
|
f[$TARGET].s = true;
|
|
594
690
|
markRawIngest(o);
|
|
595
691
|
}
|
|
596
|
-
if (
|
|
692
|
+
if (r) {
|
|
597
693
|
const n = e;
|
|
598
694
|
let t;
|
|
599
|
-
if (
|
|
695
|
+
if (i?.seedLoadingValue) t = {
|
|
600
696
|
loadingValue: undefined
|
|
601
697
|
};
|
|
602
|
-
if (
|
|
698
|
+
if (i?.name || i?.loc) t = {
|
|
603
699
|
...t,
|
|
604
|
-
name:
|
|
605
|
-
loc:
|
|
700
|
+
name: i.name,
|
|
701
|
+
loc: i.loc
|
|
606
702
|
};
|
|
607
|
-
const
|
|
608
|
-
if (!
|
|
703
|
+
const r = computed(() => {
|
|
704
|
+
if (!a.node) a.node = getOwner();
|
|
609
705
|
runProjectionComputed(f, n, s);
|
|
610
706
|
}, t);
|
|
611
|
-
|
|
612
|
-
|
|
707
|
+
r.l &= ~CONFIG_AUTO_DISPOSE;
|
|
708
|
+
a.node = r;
|
|
613
709
|
}
|
|
614
710
|
return [ f, e => storeSetter(f, e) ];
|
|
615
711
|
}
|