@solidjs/signals 2.0.0-rc.16 → 2.0.0-rc.18
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 +1972 -1751
- package/dist/dev.attribution.js +16 -16
- package/dist/dev.js +672 -319
- package/dist/observe/boundaries.js +2 -2
- package/dist/observe/core/async.js +151 -116
- package/dist/observe/core/constants.js +3 -1
- package/dist/observe/core/context.js +2 -0
- package/dist/observe/core/core.js +51 -35
- package/dist/observe/core/effect.js +29 -30
- package/dist/observe/core/graph.js +13 -11
- package/dist/observe/core/lanes.js +25 -14
- package/dist/observe/core/owner.js +77 -51
- package/dist/observe/core/scheduler.js +202 -175
- package/dist/observe/core/verdict.js +5 -2
- package/dist/observe/deferred.js +16 -13
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +2 -0
- package/dist/observe/map.js +2 -2
- package/dist/observe/signals.js +6 -1
- package/dist/observe/store/affects.js +13 -13
- package/dist/observe/store/optimistic.js +112 -71
- package/dist/observe/store/reconcile.js +137 -121
- package/dist/observe/store/store.js +612 -626
- package/dist/observe/store/target.js +1 -5
- package/dist/prod/boundaries.js +2 -2
- package/dist/prod/core/async.js +211 -176
- package/dist/prod/core/constants.js +3 -1
- package/dist/prod/core/context.js +2 -0
- package/dist/prod/core/core.js +63 -47
- package/dist/prod/core/effect.js +24 -25
- package/dist/prod/core/graph.js +12 -10
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +39 -28
- package/dist/prod/core/owner.js +70 -44
- package/dist/prod/core/scheduler.js +78 -51
- package/dist/prod/core/truth.js +4 -4
- package/dist/prod/core/verdict.js +11 -8
- package/dist/prod/deferred.js +17 -14
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +2 -0
- package/dist/prod/map.js +2 -2
- package/dist/prod/signals.js +18 -13
- package/dist/prod/store/optimistic.js +112 -71
- package/dist/prod/store/reconcile.js +137 -121
- package/dist/prod/store/store.js +109 -121
- package/dist/prod/store/target.js +13 -17
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +29 -6
- package/dist/types/core/dev.d.ts +9 -1
- package/dist/types/core/error.d.ts +9 -1
- package/dist/types/core/owner.d.ts +4 -0
- package/dist/types/core/scheduler.d.ts +38 -7
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { isThenable, onSupersede } from "./core/async.js";
|
|
2
|
+
|
|
3
|
+
import { CONFIG_CHILDREN_FORBIDDEN } from "./core/constants.js";
|
|
4
|
+
|
|
5
|
+
import { cleanup, getOwner } from "./core/owner.js";
|
|
6
|
+
|
|
7
|
+
// `from` — a push source (callback subscription, Observable, AsyncIterable)
|
|
8
|
+
// as an AsyncIterable a computation can return or `for await` over:
|
|
9
|
+
// const value = createMemo(() => from(source));
|
|
10
|
+
// A subscription keeps the latest value: values that arrive between pulls
|
|
11
|
+
// coalesce. An AsyncIterable streams through unchanged. Either way the source
|
|
12
|
+
// opens when the result is iterated and closes on return(), on supersede (the
|
|
13
|
+
// owning computation re-runs) and on owner disposal, whichever comes first.
|
|
14
|
+
function toSubscribe(e) {
|
|
15
|
+
if (typeof e === "function") return e;
|
|
16
|
+
// TC39 / RxJS interop: `Symbol.observable` (or "@@observable") returns the observable.
|
|
17
|
+
// Read per call, not at module scope, so the module stays side-effect free.
|
|
18
|
+
const n = Symbol.observable;
|
|
19
|
+
const r = (n && e?.[n]) ?? e?.["@@observable"];
|
|
20
|
+
const t = typeof r === "function" ? r.call(e) : e;
|
|
21
|
+
if (t && typeof t.subscribe === "function") return (e, n, r) => t.subscribe({
|
|
22
|
+
next: e,
|
|
23
|
+
error: n,
|
|
24
|
+
complete: r
|
|
25
|
+
});
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DONE = {
|
|
30
|
+
value: undefined,
|
|
31
|
+
done: true
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Ties an iterator's `finish` to the computation iterating it. Solid reaches
|
|
36
|
+
* an iterator's return() only when the compute returns the iterable directly.
|
|
37
|
+
* Inside an async generator the generator's own return() is queued behind its
|
|
38
|
+
* pending `await`, and owner cleanups wait for the replacing pass to commit,
|
|
39
|
+
* so close on supersede, with owner disposal as the backstop. No owner (an
|
|
40
|
+
* async compute iterating it after its first `await`, or plain iteration):
|
|
41
|
+
* no registration and no diagnostic, so only return() closes it. Scopes that
|
|
42
|
+
* forbid cleanups rely on supersede and return().
|
|
43
|
+
*/ function bindToOwner(e) {
|
|
44
|
+
const n = getOwner();
|
|
45
|
+
if (!n) return;
|
|
46
|
+
onSupersede(e);
|
|
47
|
+
if (!(n.l & CONFIG_CHILDREN_FORBIDDEN)) cleanup(e);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* An AsyncIterable as a stream bound to the iterating computation: every
|
|
52
|
+
* value passes through (no coalescing). `finish` calls the inner return()
|
|
53
|
+
* once and settles pending pulls as done, so a `for await` over it exits as
|
|
54
|
+
* soon as the computation re-runs — even when the inner iterator is an async
|
|
55
|
+
* generator whose own return() the language queues behind its pending
|
|
56
|
+
* `await`.
|
|
57
|
+
*/ function wrapIterable(e) {
|
|
58
|
+
return {
|
|
59
|
+
[Symbol.asyncIterator]() {
|
|
60
|
+
const n = e[Symbol.asyncIterator]();
|
|
61
|
+
let r = false;
|
|
62
|
+
const t = new Set;
|
|
63
|
+
const finish = () => {
|
|
64
|
+
if (r) return DONE;
|
|
65
|
+
r = true;
|
|
66
|
+
for (const e of t) e(DONE);
|
|
67
|
+
t.clear();
|
|
68
|
+
try {
|
|
69
|
+
const e = n.return?.();
|
|
70
|
+
if (isThenable(e)) e.then(undefined, () => {});
|
|
71
|
+
} catch {}
|
|
72
|
+
return DONE;
|
|
73
|
+
};
|
|
74
|
+
bindToOwner(finish);
|
|
75
|
+
return {
|
|
76
|
+
next(...e) {
|
|
77
|
+
if (r) return Promise.resolve(DONE);
|
|
78
|
+
const o = n.next(...e);
|
|
79
|
+
// A bare result (the producer's buffered fast path) passes through.
|
|
80
|
+
if (!isThenable(o)) return o;
|
|
81
|
+
return new Promise((e, n) => {
|
|
82
|
+
t.add(e);
|
|
83
|
+
o.then(n => t.delete(e) && e(n), r => t.delete(e) && n(r));
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
return() {
|
|
87
|
+
return Promise.resolve(finish());
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const toCleanup = e => typeof e === "function" ? e : e && typeof e.unsubscribe === "function" ? () => e.unsubscribe() : () => {};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Converts a callback subscription, an Observable or an AsyncIterable into an
|
|
98
|
+
* AsyncIterable for a Solid computation. A subscription keeps only the latest
|
|
99
|
+
* value, and a value the source emits during `subscribe` is served without a
|
|
100
|
+
* loading frame; an AsyncIterable streams every value through.
|
|
101
|
+
*
|
|
102
|
+
* The source closes on supersede — the owning computation re-running — and
|
|
103
|
+
* on owner disposal. Inside an async generator memo that holds for a
|
|
104
|
+
* `for await` that starts before the first `await`; one that starts after it
|
|
105
|
+
* has no owner to bind to, so its source closes only when the loop ends.
|
|
106
|
+
* Prefer returning `from(...)` from its own memo, with awaited inputs read
|
|
107
|
+
* from upstream memos and transforms done in a downstream memo.
|
|
108
|
+
*
|
|
109
|
+
* Iterate the result with `for await` or return it from a computation: its
|
|
110
|
+
* `next()` returns an already-buffered value as a bare result, not a promise.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const position = createMemo(() =>
|
|
115
|
+
* from<Position>((next, error) => {
|
|
116
|
+
* const id = navigator.geolocation.watchPosition(next, error);
|
|
117
|
+
* return () => navigator.geolocation.clearWatch(id);
|
|
118
|
+
* })
|
|
119
|
+
* );
|
|
120
|
+
* const ticks = createMemo(() => from(interval$(rate())));
|
|
121
|
+
* ```
|
|
122
|
+
*/ function from(e) {
|
|
123
|
+
const n = toSubscribe(e);
|
|
124
|
+
if (!n) {
|
|
125
|
+
if (e && typeof e[Symbol.asyncIterator] === "function") return wrapIterable(e);
|
|
126
|
+
throw new TypeError("from(): expected a subscribe function, an Observable or an AsyncIterable");
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
[Symbol.asyncIterator]() {
|
|
130
|
+
let e;
|
|
131
|
+
let r = false;
|
|
132
|
+
let t = false;
|
|
133
|
+
let o = false;
|
|
134
|
+
let u;
|
|
135
|
+
let f = null;
|
|
136
|
+
let l = null;
|
|
137
|
+
let s = false;
|
|
138
|
+
const close = () => {
|
|
139
|
+
if (s) return;
|
|
140
|
+
s = true;
|
|
141
|
+
t = true;
|
|
142
|
+
const e = l;
|
|
143
|
+
l = null;
|
|
144
|
+
// A throwing unsubscribe must not escape: at supersede this runs
|
|
145
|
+
// inside flush(), ahead of the replacing pass (the returned-iterator
|
|
146
|
+
// path swallows it the same way).
|
|
147
|
+
try {
|
|
148
|
+
e?.();
|
|
149
|
+
} catch {}
|
|
150
|
+
};
|
|
151
|
+
const settle = () => {
|
|
152
|
+
if (!f) return;
|
|
153
|
+
const n = f;
|
|
154
|
+
if (r) {
|
|
155
|
+
f = null;
|
|
156
|
+
r = false;
|
|
157
|
+
n.resolve({
|
|
158
|
+
value: e,
|
|
159
|
+
done: false
|
|
160
|
+
});
|
|
161
|
+
} else if (o) {
|
|
162
|
+
f = null;
|
|
163
|
+
o = false;
|
|
164
|
+
n.reject(u);
|
|
165
|
+
} else if (t) {
|
|
166
|
+
f = null;
|
|
167
|
+
n.resolve({
|
|
168
|
+
value: undefined,
|
|
169
|
+
done: true
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const i = n(n => {
|
|
174
|
+
if (t) return;
|
|
175
|
+
e = n;
|
|
176
|
+
r = true;
|
|
177
|
+
settle();
|
|
178
|
+
}, e => {
|
|
179
|
+
if (t) return;
|
|
180
|
+
o = true;
|
|
181
|
+
u = e;
|
|
182
|
+
t = true;
|
|
183
|
+
settle();
|
|
184
|
+
close();
|
|
185
|
+
}, () => {
|
|
186
|
+
if (t) return;
|
|
187
|
+
t = true;
|
|
188
|
+
settle();
|
|
189
|
+
close();
|
|
190
|
+
});
|
|
191
|
+
l = toCleanup(i);
|
|
192
|
+
// Completed or errored during subscribe: release what it just returned.
|
|
193
|
+
if (s) {
|
|
194
|
+
const e = l;
|
|
195
|
+
l = null;
|
|
196
|
+
try {
|
|
197
|
+
e();
|
|
198
|
+
} catch {}
|
|
199
|
+
}
|
|
200
|
+
// Idempotent: return(), supersede and disposal may all reach it.
|
|
201
|
+
const finish = () => {
|
|
202
|
+
close();
|
|
203
|
+
r = false;
|
|
204
|
+
if (f) {
|
|
205
|
+
const e = f;
|
|
206
|
+
f = null;
|
|
207
|
+
e.resolve(DONE);
|
|
208
|
+
}
|
|
209
|
+
return DONE;
|
|
210
|
+
};
|
|
211
|
+
bindToOwner(finish);
|
|
212
|
+
return {
|
|
213
|
+
next() {
|
|
214
|
+
// A buffered value is returned as a bare result, not a promise: the
|
|
215
|
+
// iterator consumer lands it in the same pass, so a source that
|
|
216
|
+
// emits during subscribe (BehaviorSubject-style) shows its value
|
|
217
|
+
// with no loading frame. `for await` accepts either form.
|
|
218
|
+
if (r) {
|
|
219
|
+
r = false;
|
|
220
|
+
return {
|
|
221
|
+
value: e,
|
|
222
|
+
done: false
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (o) {
|
|
226
|
+
o = false;
|
|
227
|
+
return Promise.reject(u);
|
|
228
|
+
}
|
|
229
|
+
if (t) return Promise.resolve({
|
|
230
|
+
value: undefined,
|
|
231
|
+
done: true
|
|
232
|
+
});
|
|
233
|
+
return new Promise((e, n) => {
|
|
234
|
+
f = {
|
|
235
|
+
resolve: e,
|
|
236
|
+
reject: n
|
|
237
|
+
};
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
return() {
|
|
241
|
+
return Promise.resolve(finish());
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export { from };
|
package/dist/prod/index.js
CHANGED
|
@@ -36,6 +36,8 @@ export { affects } from "./affects.js";
|
|
|
36
36
|
|
|
37
37
|
export { createDeferred } from "./deferred.js";
|
|
38
38
|
|
|
39
|
+
export { from } from "./from.js";
|
|
40
|
+
|
|
39
41
|
export { flatten } from "./flatten.js";
|
|
40
42
|
|
|
41
43
|
export { createErrorBoundary, createLoadingBoundary } from "./boundaries.js";
|
package/dist/prod/map.js
CHANGED
|
@@ -301,7 +301,7 @@ function updateKeyedMap() {
|
|
|
301
301
|
this.ct && (this.ct = []);
|
|
302
302
|
this.lt && (this.lt = []);
|
|
303
303
|
}
|
|
304
|
-
if (this.H && !this.ht
|
|
304
|
+
if (this.H && !this.ht.length) {
|
|
305
305
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
306
306
|
// dispose it before re-creating
|
|
307
307
|
this.tn[0]?.dispose();
|
|
@@ -539,7 +539,7 @@ function updateRepeat() {
|
|
|
539
539
|
// Reset offset to match the cleared data (#2767, repro 2).
|
|
540
540
|
this.dt = 0;
|
|
541
541
|
}
|
|
542
|
-
if (this.H && !this.ht
|
|
542
|
+
if (this.H && !this.ht.length) {
|
|
543
543
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
544
544
|
// dispose it before re-creating
|
|
545
545
|
this.tn[0]?.dispose();
|
package/dist/prod/signals.js
CHANGED
|
@@ -292,7 +292,12 @@ function createMemo(e, t) {
|
|
|
292
292
|
o = undefined;
|
|
293
293
|
}
|
|
294
294
|
runWithOwner(n, () => {
|
|
295
|
-
effect(() =>
|
|
295
|
+
effect(() => {
|
|
296
|
+
const e = getOwner();
|
|
297
|
+
o = e;
|
|
298
|
+
c();
|
|
299
|
+
return e;
|
|
300
|
+
}, t => {
|
|
296
301
|
o = undefined;
|
|
297
302
|
r?.();
|
|
298
303
|
const n = (e.effect || e)?.();
|
|
@@ -334,14 +339,14 @@ function createMemo(e, t) {
|
|
|
334
339
|
// or an already-displayed guess would satisfy it in a later slice. (REDERIVE
|
|
335
340
|
// after: the first pass's own error is the promise's rejection.)
|
|
336
341
|
computed(() => {
|
|
337
|
-
let
|
|
342
|
+
let u, i = false;
|
|
338
343
|
try {
|
|
339
|
-
|
|
344
|
+
u = e();
|
|
340
345
|
} catch (e) {
|
|
341
346
|
if (e instanceof NotReadyError) throw e;
|
|
342
347
|
// An error is an answer (D-41): delivered by the same frame rule.
|
|
343
|
-
|
|
344
|
-
|
|
348
|
+
u = unwrapStatusError(e);
|
|
349
|
+
i = true;
|
|
345
350
|
}
|
|
346
351
|
const s = getOwner();
|
|
347
352
|
// A truth reader waits out a `createDeferred` flight its value derives
|
|
@@ -354,9 +359,9 @@ function createMemo(e, t) {
|
|
|
354
359
|
staleReader(s, resolveTx(a));
|
|
355
360
|
return;
|
|
356
361
|
}
|
|
357
|
-
if (
|
|
358
|
-
o?.(
|
|
359
|
-
queueMicrotask(() => t(
|
|
362
|
+
if (i) return void queueMicrotask(() => r(u));
|
|
363
|
+
o?.(u);
|
|
364
|
+
queueMicrotask(() => t(u));
|
|
360
365
|
}, {
|
|
361
366
|
Le: n
|
|
362
367
|
}).l |= CONFIG_REDERIVE;
|
|
@@ -516,14 +521,14 @@ function createMemo(e, t) {
|
|
|
516
521
|
// (`saving=true` beside the confirmed `version=1` is a tear no timeline
|
|
517
522
|
// contains). Updates that do not flip the predicate reveal freely.
|
|
518
523
|
const c = flushTransaction;
|
|
519
|
-
createRoot(
|
|
520
|
-
let
|
|
524
|
+
createRoot(u => {
|
|
525
|
+
let i;
|
|
521
526
|
let s;
|
|
522
527
|
const settle = e => {
|
|
523
|
-
if (
|
|
528
|
+
if (i !== undefined) clearTimeout(i);
|
|
524
529
|
if (s !== undefined) o.removeEventListener("abort", s);
|
|
525
530
|
e();
|
|
526
|
-
|
|
531
|
+
u();
|
|
527
532
|
};
|
|
528
533
|
// Authoritative: the predicate sees the truth, never the action's own
|
|
529
534
|
// optimistic guess — a guess cannot satisfy the wait for its own
|
|
@@ -538,7 +543,7 @@ function createMemo(e, t) {
|
|
|
538
543
|
// revealed with the action.
|
|
539
544
|
if (e && c !== null && globalQueue.q && resolveTx(c).tt !== 0) joinFuture(c);
|
|
540
545
|
});
|
|
541
|
-
if (t?.timeout !== undefined)
|
|
546
|
+
if (t?.timeout !== undefined) i = setTimeout(() => settle(() => n(new TimeoutError)), t.timeout);
|
|
542
547
|
if (o !== undefined) {
|
|
543
548
|
s = () => settle(() => n(o.reason));
|
|
544
549
|
o.addEventListener("abort", s, {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { CONFIG_OVERRIDE,
|
|
1
|
+
import { CONFIG_OVERRIDE, CONFIG_GUESS, EFFECT_RENDER, CONFIG_CHILDREN_FORBIDDEN, NOT_PENDING, CONFIG_SLOT_NODE, CONFIG_AUTO_DISPOSE } from "../core/constants.js";
|
|
2
2
|
|
|
3
|
-
import { computed, ext, untrack, read, isEqual } from "../core/core.js";
|
|
3
|
+
import { computed, joinPassAt, ext, untrack, read, isEqual } from "../core/core.js";
|
|
4
4
|
|
|
5
5
|
import { pendingGuessOf, optimisticWrite } from "../core/lanes.js";
|
|
6
6
|
|
|
7
7
|
import { getOwner } from "../core/owner.js";
|
|
8
8
|
|
|
9
|
-
import { GlobalQueue, txOf,
|
|
9
|
+
import { GlobalQueue, txOf, insertSubs, joinFuture, resolveTx, passLane, list, laneDirty, notifyEpoch, schedule } from "../core/scheduler.js";
|
|
10
10
|
|
|
11
11
|
import { runProjectionComputed } from "./projection.js";
|
|
12
12
|
|
|
@@ -93,16 +93,16 @@ function notifyOptimisticWrites(e, n) {
|
|
|
93
93
|
// A chained draft holds the inner store's raws; a raw is the inner slot it
|
|
94
94
|
// resolves to (as a read of it serves) — the row a held write staged is
|
|
95
95
|
// the same row, not a guess over the slot (#3796).
|
|
96
|
-
const
|
|
97
|
-
const f = unwrapValue(o &&
|
|
96
|
+
const u = n[t];
|
|
97
|
+
const f = unwrapValue(o && u ? resolveChainedRaw(e, t, u) : u);
|
|
98
98
|
if (!(t in i)) {
|
|
99
99
|
guess(getNode(e, t), t, false, f);
|
|
100
100
|
guess(getHasNode(e, t), t, true, true);
|
|
101
101
|
s = true;
|
|
102
102
|
} else {
|
|
103
103
|
const n = i[t];
|
|
104
|
-
const
|
|
105
|
-
if (!isEqual(
|
|
104
|
+
const u = unwrapValue(o && n ? resolveChainedRaw(e, t, n) : n);
|
|
105
|
+
if (!isEqual(u, f) && !targetsEqual(u, f)) {
|
|
106
106
|
guess(getNode(e, t), t, false, f);
|
|
107
107
|
if (r) s = true;
|
|
108
108
|
}
|
|
@@ -122,29 +122,31 @@ function notifyOptimisticWrites(e, n) {
|
|
|
122
122
|
if (s) {
|
|
123
123
|
const o = r ? n.map(unwrapValue) : shallowKeys(n);
|
|
124
124
|
// The rows the setter removed from the view it saw, by key: a later
|
|
125
|
-
// re-base over another action's truth must not bring them back.
|
|
125
|
+
// re-base over another action's truth must not bring them back. A set
|
|
126
|
+
// (empty or not) marks a keyed arrangement guess (`fold`).
|
|
126
127
|
let s = null;
|
|
127
|
-
const
|
|
128
|
-
if (r &&
|
|
128
|
+
const u = e.fam.key;
|
|
129
|
+
if (r && u !== null && u !== undefined) {
|
|
130
|
+
s = new Set;
|
|
129
131
|
const e = new Set;
|
|
130
132
|
for (let n = 0; n < o.length; n++) {
|
|
131
|
-
const t = isWrappable(o[n]) ?
|
|
133
|
+
const t = isWrappable(o[n]) ? u(o[n]) : undefined;
|
|
132
134
|
if (t !== undefined) e.add(t);
|
|
133
135
|
}
|
|
134
136
|
for (let n = 0; n < i.length; n++) {
|
|
135
137
|
const t = unwrapValue(i[n]);
|
|
136
|
-
const r = isWrappable(t) ?
|
|
137
|
-
if (r !== undefined && !e.has(r))
|
|
138
|
+
const r = isWrappable(t) ? u(t) : undefined;
|
|
139
|
+
if (r !== undefined && !e.has(r)) s.add(r);
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
optimisticWrite(getContainerNode(e), new LaneView(o, t, s));
|
|
141
143
|
}
|
|
142
144
|
if (e.dk !== null) optimisticWrite(e.dk, {});
|
|
143
145
|
markOverlaid(e);
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
+
const u = optStaged.get(e);
|
|
147
|
+
if (u === undefined) return null;
|
|
146
148
|
optStaged.delete(e);
|
|
147
|
-
return
|
|
149
|
+
return u;
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
/** The container comparator's lane arm (store.ts `containerEquals`): an
|
|
@@ -228,18 +230,18 @@ function arrangementEqual(e, n, t) {
|
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
for (let s = 0; s < n.length; s++) {
|
|
231
|
-
const
|
|
233
|
+
const u = n[s];
|
|
232
234
|
let f;
|
|
233
|
-
if (isWrappable(
|
|
235
|
+
if (isWrappable(u)) {
|
|
234
236
|
if (o !== null) {
|
|
235
|
-
const e = t(
|
|
237
|
+
const e = t(u);
|
|
236
238
|
if (e !== undefined) f = o.get(e);
|
|
237
239
|
} else if (s < i && isWrappable(unwrapValue(r[s]))) f = r[s];
|
|
238
240
|
}
|
|
239
241
|
if (f !== undefined) {
|
|
240
|
-
if (unwrapValue(f) !==
|
|
242
|
+
if (unwrapValue(f) !== u) reconcileDraft(f, u, t);
|
|
241
243
|
if (e[s] !== f) e[s] = f;
|
|
242
|
-
} else if (unwrapValue(e[s]) !==
|
|
244
|
+
} else if (unwrapValue(e[s]) !== u) e[s] = u;
|
|
243
245
|
}
|
|
244
246
|
if (e.length !== n.length) e.length = n.length;
|
|
245
247
|
return;
|
|
@@ -268,66 +270,105 @@ function arrangementEqual(e, n, t) {
|
|
|
268
270
|
* node is born for it as the lane's derivation), on `length` only when the
|
|
269
271
|
* committed length differs, and as the `LaneView`'s base for structural
|
|
270
272
|
* reads. Unkeyed families compose
|
|
271
|
-
* positionally — the guess stands as written (the documented residue). */ GlobalQueue.an = (e, n) => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (!
|
|
277
|
-
|
|
273
|
+
* positionally — the guess stands as written (the documented residue). */ GlobalQueue.an = (e, n, t) => {
|
|
274
|
+
if (!t) return fold(e, n);
|
|
275
|
+
const i = e.O;
|
|
276
|
+
if (i === undefined || e.ze !== $TRACK) return;
|
|
277
|
+
const r = e.T.v;
|
|
278
|
+
if (!(r instanceof LaneView)) return;
|
|
279
|
+
if (!Array.isArray(n) || !Array.isArray(r.rows)) {
|
|
280
|
+
r.base = n;
|
|
278
281
|
return;
|
|
279
282
|
}
|
|
280
|
-
const
|
|
281
|
-
if (
|
|
282
|
-
const
|
|
283
|
+
const o = i.fam?.key ?? null;
|
|
284
|
+
if (o === null) return;
|
|
285
|
+
const s = new Map;
|
|
283
286
|
for (let e = 0; e < n.length; e++) {
|
|
284
287
|
const t = n[e];
|
|
285
|
-
const i = isWrappable(t) ?
|
|
286
|
-
if (i !== undefined && !
|
|
288
|
+
const i = isWrappable(t) ? o(t) : undefined;
|
|
289
|
+
if (i !== undefined && !s.has(i)) s.set(i, t);
|
|
287
290
|
}
|
|
288
|
-
const
|
|
289
|
-
const
|
|
290
|
-
const
|
|
291
|
-
for (let e = 0; e <
|
|
292
|
-
const n =
|
|
293
|
-
const t = isWrappable(n) ?
|
|
294
|
-
const i = t !== undefined ?
|
|
291
|
+
const u = new Set;
|
|
292
|
+
const f = [];
|
|
293
|
+
const a = r.rows;
|
|
294
|
+
for (let e = 0; e < a.length; e++) {
|
|
295
|
+
const n = a[e];
|
|
296
|
+
const t = isWrappable(n) ? o(n) : undefined;
|
|
297
|
+
const i = t !== undefined ? s.get(t) : undefined;
|
|
295
298
|
if (i !== undefined) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
} else
|
|
299
|
+
f.push(i);
|
|
300
|
+
u.add(t);
|
|
301
|
+
} else f.push(n);
|
|
299
302
|
}
|
|
300
|
-
const
|
|
303
|
+
const l = r.removed;
|
|
301
304
|
for (let e = 0; e < n.length; e++) {
|
|
302
305
|
const t = n[e];
|
|
303
|
-
const i = isWrappable(t) ?
|
|
304
|
-
if (i !== undefined && !
|
|
306
|
+
const i = isWrappable(t) ? o(t) : undefined;
|
|
307
|
+
if (i !== undefined && !u.has(i) && (l === null || !l.has(i))) f.push(t);
|
|
305
308
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
const
|
|
311
|
-
for (let e = 0; e <
|
|
312
|
-
const n =
|
|
309
|
+
r.rows = f;
|
|
310
|
+
r.base = f;
|
|
311
|
+
const c = txOf(e);
|
|
312
|
+
const d = committed(i);
|
|
313
|
+
const p = i.n;
|
|
314
|
+
for (let e = 0; e < f.length; e++) {
|
|
315
|
+
const n = p?.[e];
|
|
313
316
|
// A slot the lane does not own shows the committed row: it becomes the
|
|
314
317
|
// lane's only where the composed row is another row (by key — the same
|
|
315
318
|
// key's new object is the same row, its target adopted it).
|
|
316
319
|
if (n === undefined) {
|
|
317
|
-
if (!sameRow(
|
|
318
|
-
} else if (!sameRow(n.l & CONFIG_OVERRIDE ? n.T.v :
|
|
320
|
+
if (!sameRow(d[e], f[e], o)) laneSet(getNode(i, e), f[e], c);
|
|
321
|
+
} else if (!sameRow(n.l & CONFIG_OVERRIDE ? n.T.v : d[e], f[e], o)) laneSet(n, f[e], c);
|
|
319
322
|
}
|
|
320
|
-
if (
|
|
321
|
-
for (let e =
|
|
322
|
-
const n =
|
|
323
|
+
if (p !== null) {
|
|
324
|
+
for (let e = f.length; ;e++) {
|
|
325
|
+
const n = p[e];
|
|
323
326
|
if (n === undefined) break;
|
|
324
|
-
laneSet(n, undefined,
|
|
327
|
+
laneSet(n, undefined, c);
|
|
325
328
|
}
|
|
326
|
-
const e =
|
|
327
|
-
if (e !== undefined && (e.l & CONFIG_OVERRIDE ||
|
|
329
|
+
const e = p.length;
|
|
330
|
+
if (e !== undefined && (e.l & CONFIG_OVERRIDE || d.length !== f.length)) laneSet(e, f.length, c);
|
|
328
331
|
}
|
|
329
332
|
};
|
|
330
333
|
|
|
334
|
+
/** D-34 for a keyed arrangement guess: rows by key, not positions. A
|
|
335
|
+
* current truth that differs folds into the hold beneath it instead of
|
|
336
|
+
* voiding the lane: the guess stays the lane's value and the screen's until
|
|
337
|
+
* the hold commits, where rows match by key, and the graph outside the lane
|
|
338
|
+
* re-derives from the truth now (`foldedRead`). A confirmation is silent
|
|
339
|
+
* (A17): only truth readers are told. True: the lane stages the truth
|
|
340
|
+
* beneath (`laneWrite`). */ function fold(e, n) {
|
|
341
|
+
const t = e.O?.k;
|
|
342
|
+
const i = txOf(e);
|
|
343
|
+
// A guess the screen never showed is void at the correction (D-34); a
|
|
344
|
+
// keyed arrangement guess's view carries its removed-keys set.
|
|
345
|
+
if (!i.ke || !(t?.l & CONFIG_OVERRIDE) || !t.T.v?.removed) return false;
|
|
346
|
+
const r = e.T.v;
|
|
347
|
+
if ((!e.xe || !e.xe(r, n)) && !GlobalQueue.nn(r, n)) {
|
|
348
|
+
i.folded = foldedRead;
|
|
349
|
+
insertSubs(e);
|
|
350
|
+
} else GlobalQueue.sn?.(i);
|
|
351
|
+
joinFuture(resolveTx(i._parent ?? i));
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** A folded lane's guess read by a deriving pass outside the lane, tracked
|
|
356
|
+
* or not (`untrack` is about dependencies, not about which world a pass
|
|
357
|
+
* derives from), or by `latest()` (`c` null): the hold's world — the truth
|
|
358
|
+
* staged beneath the guess, else the value it covered — and the pass joins
|
|
359
|
+
* the hold. Lane passes and the screen keep the guess: held truth is masked
|
|
360
|
+
* from lane passes only (#3164). The store's own derive reconciles its
|
|
361
|
+
* landings against the arrangement the guesses made: it is the author, not
|
|
362
|
+
* a reader. */ function foldedRead(e, n) {
|
|
363
|
+
if (e !== null) {
|
|
364
|
+
if (!(n.l & CONFIG_GUESS) || passLane !== null || e.i === EFFECT_RENDER || e.l & CONFIG_CHILDREN_FORBIDDEN || n.O?.fam?.node === e) return NOT_PENDING;
|
|
365
|
+
const t = txOf(n);
|
|
366
|
+
joinPassAt(e, resolveTx(t._parent ?? t));
|
|
367
|
+
}
|
|
368
|
+
if (n.ue !== NOT_PENDING) return n.ue;
|
|
369
|
+
return n.l & CONFIG_SLOT_NODE ? GlobalQueue.fn(n) : n.Ie;
|
|
370
|
+
}
|
|
371
|
+
|
|
331
372
|
function sameRow(e, n, t) {
|
|
332
373
|
if (e === n) return true;
|
|
333
374
|
if (!isWrappable(e) || !isWrappable(n)) return false;
|
|
@@ -521,11 +562,11 @@ i = false) {
|
|
|
521
562
|
if (s !== null) {
|
|
522
563
|
for (const e of Reflect.ownKeys(s)) {
|
|
523
564
|
const o = s[e];
|
|
524
|
-
let
|
|
525
|
-
if (o.l & CONFIG_OVERRIDE)
|
|
526
|
-
const f = !!
|
|
527
|
-
const
|
|
528
|
-
if (!f && e in
|
|
565
|
+
let u;
|
|
566
|
+
if (o.l & CONFIG_OVERRIDE) u = t(o); else if (!i || (u = pendingGuessOf(o)) === NOT_PENDING) continue;
|
|
567
|
+
const f = !!u;
|
|
568
|
+
const a = r ?? n;
|
|
569
|
+
if (!f && e in a) delete ensure()[e];
|
|
529
570
|
}
|
|
530
571
|
}
|
|
531
572
|
return r ?? n;
|
|
@@ -538,7 +579,7 @@ function createOptimisticStore(e, n, t) {
|
|
|
538
579
|
// createProjectionInternal).
|
|
539
580
|
const o = i ? n || {} : e;
|
|
540
581
|
const s = r?.key === undefined ? "id" : r.key;
|
|
541
|
-
const
|
|
582
|
+
const u = {
|
|
542
583
|
map: new WeakMap,
|
|
543
584
|
node: null,
|
|
544
585
|
live: new Set,
|
|
@@ -546,8 +587,8 @@ function createOptimisticStore(e, n, t) {
|
|
|
546
587
|
opt: true,
|
|
547
588
|
key: typeof s === "function" ? s : s === null ? null : e => isWrappable(e) ? e[s] : undefined
|
|
548
589
|
};
|
|
549
|
-
const f = wrap(o, null, null,
|
|
550
|
-
if (
|
|
590
|
+
const f = wrap(o, null, null, u);
|
|
591
|
+
if (u.shallow) {
|
|
551
592
|
f[$TARGET].s = true;
|
|
552
593
|
markRawIngest(o);
|
|
553
594
|
}
|
|
@@ -558,11 +599,11 @@ function createOptimisticStore(e, n, t) {
|
|
|
558
599
|
loadingValue: undefined
|
|
559
600
|
};
|
|
560
601
|
const i = computed(() => {
|
|
561
|
-
if (!
|
|
602
|
+
if (!u.node) u.node = getOwner();
|
|
562
603
|
runProjectionComputed(f, n, s);
|
|
563
604
|
}, t);
|
|
564
605
|
i.l &= ~CONFIG_AUTO_DISPOSE;
|
|
565
|
-
|
|
606
|
+
u.node = i;
|
|
566
607
|
}
|
|
567
608
|
return [ f, e => storeSetter(f, e) ];
|
|
568
609
|
}
|