@solidjs/signals 2.0.0-rc.16 → 2.0.0-rc.17
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 +1719 -1603
- package/dist/dev.attribution.js +13 -13
- package/dist/dev.js +182 -45
- package/dist/observe/boundaries.js +2 -2
- package/dist/observe/core/async.js +3 -1
- package/dist/observe/core/context.js +2 -0
- package/dist/observe/core/core.js +26 -10
- package/dist/observe/core/effect.js +2 -0
- package/dist/observe/core/graph.js +10 -8
- package/dist/observe/core/lanes.js +25 -14
- package/dist/observe/core/owner.js +73 -47
- package/dist/observe/core/scheduler.js +189 -173
- package/dist/observe/core/verdict.js +3 -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/store.js +25 -16
- package/dist/prod/boundaries.js +2 -2
- package/dist/prod/core/async.js +3 -1
- package/dist/prod/core/context.js +2 -0
- package/dist/prod/core/core.js +31 -15
- package/dist/prod/core/effect.js +2 -0
- package/dist/prod/core/graph.js +10 -8
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +25 -14
- package/dist/prod/core/owner.js +70 -44
- package/dist/prod/core/scheduler.js +43 -27
- package/dist/prod/core/verdict.js +4 -1
- 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/store.js +18 -7
- package/dist/types/core/core.d.ts +10 -3
- 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 +23 -4
- package/package.json +1 -1
package/dist/dev.attribution.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
bP as OBSERVE,
|
|
3
|
+
b_ as setAttributionHooks,
|
|
4
|
+
b$ as liveRootOwners,
|
|
5
5
|
N as NOT_PENDING,
|
|
6
6
|
as as emitDiagnostic,
|
|
7
|
-
|
|
7
|
+
c0 as ownerPath,
|
|
8
8
|
aJ as reportDiagnostic,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
c1 as isSuppressed,
|
|
10
|
+
c2 as blockersOf,
|
|
11
|
+
c3 as CONFIG_PLUMBING,
|
|
12
|
+
c4 as anyExcluded,
|
|
13
|
+
c5 as isExcluded,
|
|
14
|
+
c6 as CONFIG_WIDE,
|
|
15
15
|
c as CONFIG_OVERRIDE,
|
|
16
16
|
C as CONFIG_GUESS,
|
|
17
17
|
G as GlobalQueue,
|
|
18
18
|
f as CONFIG_VERDICT,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
c7 as GRAPH_SIZE_WARN_AT,
|
|
20
|
+
c8 as noteFanOut,
|
|
21
|
+
c9 as CONFIG_SUPERSEDES,
|
|
22
22
|
b0 as $REFRESH
|
|
23
23
|
} from "./dev-shared.js";
|
|
24
24
|
|
package/dist/dev.js
CHANGED
|
@@ -105,7 +105,7 @@ import {
|
|
|
105
105
|
aN as EFFECT_TRACKED,
|
|
106
106
|
aO as heldRunHook,
|
|
107
107
|
aP as unwrapStatusError,
|
|
108
|
-
aQ as
|
|
108
|
+
aQ as haltReactivityAt,
|
|
109
109
|
aR as setStrictRead,
|
|
110
110
|
aS as StatusError,
|
|
111
111
|
aT as trimStaleDeps,
|
|
@@ -154,31 +154,33 @@ import {
|
|
|
154
154
|
by as propagateStatus,
|
|
155
155
|
bz as forEachDependent,
|
|
156
156
|
bA as settlePendingSource,
|
|
157
|
-
bB as
|
|
158
|
-
bC as
|
|
159
|
-
bD as
|
|
160
|
-
bE as
|
|
161
|
-
bF as
|
|
162
|
-
bG as
|
|
163
|
-
bH as
|
|
164
|
-
bI as
|
|
165
|
-
bJ as
|
|
166
|
-
bK as
|
|
167
|
-
bL as
|
|
168
|
-
bM as
|
|
169
|
-
bN as
|
|
157
|
+
bB as joinPassAt,
|
|
158
|
+
bC as notifyEpoch,
|
|
159
|
+
bD as SUPPORTS_PROXY,
|
|
160
|
+
bE as releaseMarksHook,
|
|
161
|
+
bF as installMarkHooks,
|
|
162
|
+
bG as CONFIG_LAGS,
|
|
163
|
+
bH as REACTIVE_REASK,
|
|
164
|
+
bI as haltReactivity,
|
|
165
|
+
bJ as heldUnder,
|
|
166
|
+
bK as unhold,
|
|
167
|
+
bL as laneStageHook,
|
|
168
|
+
bM as REACTIVE_ZOMBIE,
|
|
169
|
+
bN as installBoundaryHooks,
|
|
170
|
+
bO as CAUGHT_REARMED,
|
|
171
|
+
bP as OBSERVE$1
|
|
170
172
|
} from "./dev-shared.js";
|
|
171
173
|
export {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
bQ as clearSnapshots,
|
|
175
|
+
bR as enforceLoadingBoundary,
|
|
176
|
+
bS as getNextChildId,
|
|
177
|
+
bT as markSnapshotScope,
|
|
178
|
+
bU as peekNextChildId,
|
|
179
|
+
bV as releaseSnapshotScope,
|
|
180
|
+
bW as resetErrorHalt,
|
|
181
|
+
bX as setConsoleFooter,
|
|
182
|
+
bY as setSnapshotCapture,
|
|
183
|
+
bZ as setWriteHook
|
|
182
184
|
} from "./dev-shared.js";
|
|
183
185
|
|
|
184
186
|
// Truth reads (`until`, `refresh`; CONFIG_AUTHORITATIVE) over lane nodes.
|
|
@@ -696,12 +698,21 @@ function dissolveLane(l, into, except) {
|
|
|
696
698
|
* member throws like any. */
|
|
697
699
|
function laneRead(c, el) {
|
|
698
700
|
const guess = el._config & CONFIG_GUESS;
|
|
701
|
+
const l = txOf(el);
|
|
702
|
+
// A lane a correction folded under (D-34): store/optimistic.ts reads it.
|
|
703
|
+
// Before the authoritative arm: an `until` outside the lane reads the
|
|
704
|
+
// same truth there, and joins the hold it folded into — its posture over
|
|
705
|
+
// a foreign hold (D-31), which the truth read alone would not take.
|
|
706
|
+
const folded = c !== null && l.folded;
|
|
707
|
+
if (folded) {
|
|
708
|
+
const v = folded(c, el);
|
|
709
|
+
if (v !== NOT_PENDING) return v;
|
|
710
|
+
}
|
|
699
711
|
if (c !== null && c._config & CONFIG_AUTHORITATIVE) return GlobalQueue._truth(el);
|
|
700
712
|
const status = el._statusFlags;
|
|
701
713
|
// An errored member's lane value predates its error; the error is the answer (#4106).
|
|
702
714
|
if (!guess && status & STATUS_ERROR) return NOT_PENDING;
|
|
703
715
|
if (c === null) return laneValueOf(el);
|
|
704
|
-
const l = txOf(el);
|
|
705
716
|
if (!tracking || c._config & CONFIG_CHILDREN_FORBIDDEN) {
|
|
706
717
|
// An untracked read inside lane work derives from the lane like a
|
|
707
718
|
// tracked one, minus the subscription — `untrack` is about
|
|
@@ -869,10 +880,12 @@ function laneWrite(el, v) {
|
|
|
869
880
|
// question is the one being asked (`question`: a slice's, a derive's
|
|
870
881
|
// continuation's — the flight's); unstamped writes and mainline are
|
|
871
882
|
// current.
|
|
872
|
-
|
|
883
|
+
// A store re-bases a keyed arrangement guess over an older truth, and
|
|
884
|
+
// folds a current one beneath it (D-34): true.
|
|
885
|
+
const older = stale(el, question);
|
|
886
|
+
if (GlobalQueue._laneRebase?.(el, v, older) || older) {
|
|
873
887
|
el._pendingValue = v;
|
|
874
888
|
el._config |= CONFIG_HELD;
|
|
875
|
-
GlobalQueue._laneRebase?.(el, v);
|
|
876
889
|
schedule();
|
|
877
890
|
return v;
|
|
878
891
|
}
|
|
@@ -1325,6 +1338,9 @@ function verdictValue(el, c) {
|
|
|
1325
1338
|
(!el._equals || !el._equals(el._x._lane, el._pendingValue))))
|
|
1326
1339
|
)
|
|
1327
1340
|
probeFound = true;
|
|
1341
|
+
// A lane a correction folded under (D-34): `latest()` shows the hold's
|
|
1342
|
+
// world whole — the truth beneath each guess, else what it covered.
|
|
1343
|
+
if (latestActive && l.folded) return l.folded(null, el);
|
|
1328
1344
|
return el._x._lane;
|
|
1329
1345
|
}
|
|
1330
1346
|
if (probing && flying) probeFound = true;
|
|
@@ -1776,6 +1792,10 @@ function setReadWatch(watch) {
|
|
|
1776
1792
|
* via a `setContext` call.
|
|
1777
1793
|
*/
|
|
1778
1794
|
function createContext(defaultValue, description) {
|
|
1795
|
+
// Dev: an explicit `undefined` default is still no default (`undefined`
|
|
1796
|
+
// means "not provided"); remembered so a failed read can say so (#4181).
|
|
1797
|
+
if (arguments.length && defaultValue === undefined)
|
|
1798
|
+
return { id: Symbol(description), defaultValue, _undefinedDefault: true };
|
|
1779
1799
|
return { id: Symbol(description), defaultValue };
|
|
1780
1800
|
}
|
|
1781
1801
|
/**
|
|
@@ -1796,7 +1816,9 @@ function getContext(context, owner = getOwner()) {
|
|
|
1796
1816
|
let value = owner._context[context.id];
|
|
1797
1817
|
if (value === undefined) value = context.defaultValue;
|
|
1798
1818
|
if (value === undefined) {
|
|
1799
|
-
|
|
1819
|
+
// Dev names the context and why it has no value: no provider, or a
|
|
1820
|
+
// provider that passed `undefined` (the key is set, to `undefined`).
|
|
1821
|
+
throw new ContextNotFoundError(context, context.id in owner._context);
|
|
1800
1822
|
}
|
|
1801
1823
|
return value;
|
|
1802
1824
|
}
|
|
@@ -1886,7 +1908,12 @@ function notifyEffectStatus(status, error) {
|
|
|
1886
1908
|
return;
|
|
1887
1909
|
}
|
|
1888
1910
|
if (!globalQueue.notify(this, STATUS_ERROR, STATUS_ERROR)) {
|
|
1889
|
-
|
|
1911
|
+
// Dev locates the halt at the node that threw: the StatusError's
|
|
1912
|
+
// source, upstream of this effect when a computed failed.
|
|
1913
|
+
haltReactivityAt(
|
|
1914
|
+
unwrapStatusError(actualError),
|
|
1915
|
+
actualError instanceof StatusError ? actualError.source : this
|
|
1916
|
+
);
|
|
1890
1917
|
throw actualError;
|
|
1891
1918
|
}
|
|
1892
1919
|
} else if (this._type === EFFECT_RENDER) {
|
|
@@ -1952,7 +1979,7 @@ function runEffect(node, type) {
|
|
|
1952
1979
|
: console.error(err);
|
|
1953
1980
|
} catch (error) {
|
|
1954
1981
|
if (!globalQueue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
|
|
1955
|
-
|
|
1982
|
+
haltReactivityAt(error, node);
|
|
1956
1983
|
throw error;
|
|
1957
1984
|
}
|
|
1958
1985
|
}
|
|
@@ -1988,7 +2015,7 @@ function runEffect(node, type) {
|
|
|
1988
2015
|
ext(node)._error = new StatusError(node, error);
|
|
1989
2016
|
node._statusFlags |= STATUS_ERROR;
|
|
1990
2017
|
if (!globalQueue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
|
|
1991
|
-
|
|
2018
|
+
haltReactivityAt(error, node);
|
|
1992
2019
|
throw error;
|
|
1993
2020
|
}
|
|
1994
2021
|
} finally {
|
|
@@ -2617,7 +2644,12 @@ function createReaction(effectFn, options) {
|
|
|
2617
2644
|
}
|
|
2618
2645
|
runWithOwner(owner, () => {
|
|
2619
2646
|
effect(
|
|
2620
|
-
() =>
|
|
2647
|
+
() => {
|
|
2648
|
+
const node = getOwner();
|
|
2649
|
+
arm = node;
|
|
2650
|
+
tracking();
|
|
2651
|
+
return node;
|
|
2652
|
+
},
|
|
2621
2653
|
node => {
|
|
2622
2654
|
arm = undefined;
|
|
2623
2655
|
cl?.();
|
|
@@ -3355,7 +3387,7 @@ function updateKeyedMap() {
|
|
|
3355
3387
|
this._rows && (this._rows = []);
|
|
3356
3388
|
this._indexes && (this._indexes = []);
|
|
3357
3389
|
}
|
|
3358
|
-
if (this._fallback && !this._mappings
|
|
3390
|
+
if (this._fallback && !this._mappings.length) {
|
|
3359
3391
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
3360
3392
|
// dispose it before re-creating
|
|
3361
3393
|
this._nodes[0]?.dispose();
|
|
@@ -3636,7 +3668,7 @@ function updateRepeat() {
|
|
|
3636
3668
|
// Reset offset to match the cleared data (#2767, repro 2).
|
|
3637
3669
|
this._offset = 0;
|
|
3638
3670
|
}
|
|
3639
|
-
if (this._fallback && !this._mappings
|
|
3671
|
+
if (this._fallback && !this._mappings.length) {
|
|
3640
3672
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
3641
3673
|
// dispose it before re-creating
|
|
3642
3674
|
this._nodes[0]?.dispose();
|
|
@@ -3941,9 +3973,13 @@ function bornStaged(target, node, staged, key) {
|
|
|
3941
3973
|
if (k === null) return; // a node-less staging holds nothing (`stageOn`)
|
|
3942
3974
|
// Held by the container's transaction only for a key the HELD staging
|
|
3943
3975
|
// changed — a mainline layer's change above it stages mainline. (A
|
|
3944
|
-
// container carrying a guess is a lane's: the flush's park decides
|
|
3945
|
-
|
|
3946
|
-
|
|
3976
|
+
// container carrying a guess is a lane's: the flush's park decides —
|
|
3977
|
+
// unless a correction folded under the guess, D-34: the key is that
|
|
3978
|
+
// correction's hold's.)
|
|
3979
|
+
if (k._config & CONFIG_HELD && heldKeyChanged(target, key)) {
|
|
3980
|
+
const l = txOf(k);
|
|
3981
|
+
if (!(k._config & CONFIG_OVERRIDE) || l.folded) holdNode(node, resolveTx(l._parent ?? l));
|
|
3982
|
+
}
|
|
3947
3983
|
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
3948
3984
|
// late-materialized leaf with it.
|
|
3949
3985
|
if (k._config & CONFIG_PROMOTED) notePromotedWrite(node);
|
|
@@ -5371,6 +5407,47 @@ function touch(target, key) {
|
|
|
5371
5407
|
}
|
|
5372
5408
|
if (wk !== WK_ALL) (target.wk ??= new Set()).add(key);
|
|
5373
5409
|
}
|
|
5410
|
+
/** Dev: targets (and their keys) already warned about for a write outside
|
|
5411
|
+
* the setter — #4179's once-per-target+key memory. */
|
|
5412
|
+
let warnedOutsideWrites;
|
|
5413
|
+
/** Dev: set while the runtime itself writes through a proxy it knows the
|
|
5414
|
+
* trap ignores (`registerGraph`'s `_owner` stamp). */
|
|
5415
|
+
let internalOutsideWrite = false;
|
|
5416
|
+
/** Dev: a write, defineProperty or delete on a store proxy outside its
|
|
5417
|
+
* setter's draft is ignored (the trap returns `true` so strict-mode code
|
|
5418
|
+
* does not throw) — say so once per target+key instead of silently dropping
|
|
5419
|
+
* it (#4179). */
|
|
5420
|
+
function warnWriteOutsideSetter(target, key, operation) {
|
|
5421
|
+
if (internalOutsideWrite) return;
|
|
5422
|
+
warnedOutsideWrites ??= new WeakMap();
|
|
5423
|
+
let keys = warnedOutsideWrites.get(target);
|
|
5424
|
+
if (keys === undefined) warnedOutsideWrites.set(target, (keys = new Set()));
|
|
5425
|
+
if (keys.has(key)) return;
|
|
5426
|
+
keys.add(key);
|
|
5427
|
+
const name = String(key);
|
|
5428
|
+
const access =
|
|
5429
|
+
typeof key === "string" && /^[A-Za-z_$][\w$]*$/.test(key)
|
|
5430
|
+
? `.${key}`
|
|
5431
|
+
: typeof key === "string" && /^\d+$/.test(key)
|
|
5432
|
+
? `[${key}]`
|
|
5433
|
+
: `[${typeof key === "string" ? JSON.stringify(key) : name}]`;
|
|
5434
|
+
const hint =
|
|
5435
|
+
operation === "deleteProperty"
|
|
5436
|
+
? `setStore(s => { delete s${access} })`
|
|
5437
|
+
: `setStore(s => { s${access} = value })`;
|
|
5438
|
+
reportDiagnostic(
|
|
5439
|
+
emitDiagnostic({
|
|
5440
|
+
code: "STORE_WRITE_OUTSIDE_SETTER",
|
|
5441
|
+
kind: "write",
|
|
5442
|
+
severity: "warn",
|
|
5443
|
+
message:
|
|
5444
|
+
`[STORE_WRITE_OUTSIDE_SETTER] ${operation === "deleteProperty" ? "Deleting" : "Writing"} store property "${name}" outside its setter was ignored. ` +
|
|
5445
|
+
`Stores are read-only outside the setter's draft: use \`${hint}\`.`,
|
|
5446
|
+
nodeName: name,
|
|
5447
|
+
data: { operation, property: name }
|
|
5448
|
+
})
|
|
5449
|
+
);
|
|
5450
|
+
}
|
|
5374
5451
|
const traps = {
|
|
5375
5452
|
get(target, key, receiver) {
|
|
5376
5453
|
// One typeof gates every brand-symbol compare off the hot string path.
|
|
@@ -5614,7 +5691,10 @@ const traps = {
|
|
|
5614
5691
|
set(target, key, value) {
|
|
5615
5692
|
const draft = inDraft(target);
|
|
5616
5693
|
const override = !draft && writeOverride;
|
|
5617
|
-
if (!draft && !override)
|
|
5694
|
+
if (!draft && !override) {
|
|
5695
|
+
warnWriteOutsideSetter(target, key, "set");
|
|
5696
|
+
return true;
|
|
5697
|
+
}
|
|
5618
5698
|
if (key === "__proto__") return true; // pollution guard (core R30)
|
|
5619
5699
|
const uv = target.s ? value : unwrapValue(value);
|
|
5620
5700
|
const pb = ensurePB(target);
|
|
@@ -5663,7 +5743,10 @@ const traps = {
|
|
|
5663
5743
|
defineProperty(target, key, desc) {
|
|
5664
5744
|
const draft = inDraft(target);
|
|
5665
5745
|
const override = !draft && writeOverride;
|
|
5666
|
-
if (!draft && !override)
|
|
5746
|
+
if (!draft && !override) {
|
|
5747
|
+
warnWriteOutsideSetter(target, key, "defineProperty");
|
|
5748
|
+
return true;
|
|
5749
|
+
}
|
|
5667
5750
|
if (key === "__proto__") return true;
|
|
5668
5751
|
if (desc.get || desc.set) target.a = true;
|
|
5669
5752
|
if ("value" in desc) desc = { ...desc, value: unwrapValue(desc.value) };
|
|
@@ -5678,7 +5761,10 @@ const traps = {
|
|
|
5678
5761
|
deleteProperty(target, key) {
|
|
5679
5762
|
const draft = inDraft(target);
|
|
5680
5763
|
const override = !draft && writeOverride;
|
|
5681
|
-
if (!draft && !override)
|
|
5764
|
+
if (!draft && !override) {
|
|
5765
|
+
warnWriteOutsideSetter(target, key, "deleteProperty");
|
|
5766
|
+
return true;
|
|
5767
|
+
}
|
|
5682
5768
|
const pb = ensurePB(target);
|
|
5683
5769
|
touch(target, key);
|
|
5684
5770
|
// The key count (`kc`, the overlay gate and the rebuild cap) follows a
|
|
@@ -5836,8 +5922,11 @@ function createStore$1(initialValue, options) {
|
|
|
5836
5922
|
{
|
|
5837
5923
|
const owner = getOwner();
|
|
5838
5924
|
// Dev-tier graph registration (owner signal lists, onGraph); the
|
|
5839
|
-
// `_owner` write itself never reaches the proxy, see storeOwners
|
|
5925
|
+
// `_owner` write itself never reaches the proxy, see storeOwners — an
|
|
5926
|
+
// internal ignored write, not the user's (#4179's warning skips it).
|
|
5927
|
+
internalOutsideWrite = true;
|
|
5840
5928
|
registerGraph(proxy, owner);
|
|
5929
|
+
internalOutsideWrite = false;
|
|
5841
5930
|
// Only once the engine is installed, like the node stamping it feeds: a
|
|
5842
5931
|
// WeakMap.set per fresh store is a growing ephemeron table and,
|
|
5843
5932
|
// disabled, buys nothing.
|
|
@@ -6809,10 +6898,12 @@ function notifyOptimisticWrites(t, pb) {
|
|
|
6809
6898
|
if (structural) {
|
|
6810
6899
|
const rows = isArr ? pb.map(unwrapValue) : shallowKeys(pb);
|
|
6811
6900
|
// The rows the setter removed from the view it saw, by key: a later
|
|
6812
|
-
// re-base over another action's truth must not bring them back.
|
|
6901
|
+
// re-base over another action's truth must not bring them back. A set
|
|
6902
|
+
// (empty or not) marks a keyed arrangement guess (`fold`).
|
|
6813
6903
|
let removed = null;
|
|
6814
6904
|
const keyFn = t.fam.key;
|
|
6815
6905
|
if (isArr && keyFn !== null && keyFn !== undefined) {
|
|
6906
|
+
removed = new Set();
|
|
6816
6907
|
const kept = new Set();
|
|
6817
6908
|
for (let i = 0; i < rows.length; i++) {
|
|
6818
6909
|
const k = isWrappable(rows[i]) ? keyFn(rows[i]) : undefined;
|
|
@@ -6821,7 +6912,7 @@ function notifyOptimisticWrites(t, pb) {
|
|
|
6821
6912
|
for (let i = 0; i < old.length; i++) {
|
|
6822
6913
|
const r = unwrapValue(old[i]);
|
|
6823
6914
|
const k = isWrappable(r) ? keyFn(r) : undefined;
|
|
6824
|
-
if (k !== undefined && !kept.has(k))
|
|
6915
|
+
if (k !== undefined && !kept.has(k)) removed.add(k);
|
|
6825
6916
|
}
|
|
6826
6917
|
}
|
|
6827
6918
|
optimisticWrite(getContainerNode(t), new LaneView(rows, base, removed));
|
|
@@ -6957,7 +7048,8 @@ function reconcileDraft(draft, incoming, keyFn) {
|
|
|
6957
7048
|
* committed length differs, and as the `LaneView`'s base for structural
|
|
6958
7049
|
* reads. Unkeyed families compose
|
|
6959
7050
|
* positionally — the guess stands as written (the documented residue). */
|
|
6960
|
-
GlobalQueue._laneRebase = (el, truth) => {
|
|
7051
|
+
GlobalQueue._laneRebase = (el, truth, older) => {
|
|
7052
|
+
if (!older) return fold(el, truth);
|
|
6961
7053
|
const t = el._host;
|
|
6962
7054
|
if (t === undefined || el._key !== $TRACK) return;
|
|
6963
7055
|
const view = el._x._lane;
|
|
@@ -7018,6 +7110,51 @@ GlobalQueue._laneRebase = (el, truth) => {
|
|
|
7018
7110
|
laneSet(len, rows.length, lane);
|
|
7019
7111
|
}
|
|
7020
7112
|
};
|
|
7113
|
+
/** D-34 for a keyed arrangement guess: rows by key, not positions. A
|
|
7114
|
+
* current truth that differs folds into the hold beneath it instead of
|
|
7115
|
+
* voiding the lane: the guess stays the lane's value and the screen's until
|
|
7116
|
+
* the hold commits, where rows match by key, and the graph outside the lane
|
|
7117
|
+
* re-derives from the truth now (`foldedRead`). A confirmation is silent
|
|
7118
|
+
* (A17): only truth readers are told. True: the lane stages the truth
|
|
7119
|
+
* beneath (`laneWrite`). */
|
|
7120
|
+
function fold(el, truth) {
|
|
7121
|
+
const k = el._host?.k;
|
|
7122
|
+
const l = txOf(el);
|
|
7123
|
+
// A guess the screen never showed is void at the correction (D-34); a
|
|
7124
|
+
// keyed arrangement guess's view carries its removed-keys set.
|
|
7125
|
+
if (!l._shown || !(k?._config & CONFIG_OVERRIDE) || !k._x._lane?.removed) return false;
|
|
7126
|
+
const guess = el._x._lane;
|
|
7127
|
+
if ((!el._equals || !el._equals(guess, truth)) && !GlobalQueue._echoes(guess, truth)) {
|
|
7128
|
+
l.folded = foldedRead;
|
|
7129
|
+
insertSubs(el);
|
|
7130
|
+
} else GlobalQueue._confirmed?.(l);
|
|
7131
|
+
joinFuture(resolveTx(l._parent ?? l));
|
|
7132
|
+
return true;
|
|
7133
|
+
}
|
|
7134
|
+
/** A folded lane's guess read by a deriving pass outside the lane, tracked
|
|
7135
|
+
* or not (`untrack` is about dependencies, not about which world a pass
|
|
7136
|
+
* derives from), or by `latest()` (`c` null): the hold's world — the truth
|
|
7137
|
+
* staged beneath the guess, else the value it covered — and the pass joins
|
|
7138
|
+
* the hold. Lane passes and the screen keep the guess: held truth is masked
|
|
7139
|
+
* from lane passes only (#3164). The store's own derive reconciles its
|
|
7140
|
+
* landings against the arrangement the guesses made: it is the author, not
|
|
7141
|
+
* a reader. */
|
|
7142
|
+
function foldedRead(c, el) {
|
|
7143
|
+
if (c !== null) {
|
|
7144
|
+
if (
|
|
7145
|
+
!(el._config & CONFIG_GUESS) ||
|
|
7146
|
+
passLane !== null ||
|
|
7147
|
+
c._type === EFFECT_RENDER ||
|
|
7148
|
+
c._config & CONFIG_CHILDREN_FORBIDDEN ||
|
|
7149
|
+
el._host?.fam?.node === c
|
|
7150
|
+
)
|
|
7151
|
+
return NOT_PENDING;
|
|
7152
|
+
const l = txOf(el);
|
|
7153
|
+
joinPassAt(c, resolveTx(l._parent ?? l));
|
|
7154
|
+
}
|
|
7155
|
+
if (el._pendingValue !== NOT_PENDING) return el._pendingValue;
|
|
7156
|
+
return el._config & CONFIG_SLOT_NODE ? GlobalQueue._slotCovered(el) : el._value;
|
|
7157
|
+
}
|
|
7021
7158
|
function sameRow(a, b, keyFn) {
|
|
7022
7159
|
if (a === b) return true;
|
|
7023
7160
|
if (!isWrappable(a) || !isWrappable(b)) return false;
|
|
@@ -9310,7 +9447,7 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9310
9447
|
} catch (e) {
|
|
9311
9448
|
if (!(e instanceof NotReadyError)) {
|
|
9312
9449
|
if (!globalQueue.notify(getOwner(), STATUS_ERROR, STATUS_ERROR, e)) {
|
|
9313
|
-
|
|
9450
|
+
if (true) haltReactivityAt(e, getOwner());
|
|
9314
9451
|
throw e;
|
|
9315
9452
|
}
|
|
9316
9453
|
return;
|
|
@@ -14,7 +14,7 @@ import { enqueueSub } from "./core/heap.js";
|
|
|
14
14
|
|
|
15
15
|
import { createOwner, getOwner, cleanup, dispose } from "./core/owner.js";
|
|
16
16
|
|
|
17
|
-
import { globalQueue, haltReactivity, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx, schedule, txOf, unhold, laneStageHook, installBoundaryHooks } from "./core/scheduler.js";
|
|
17
|
+
import { globalQueue, haltReactivityAt, haltReactivity, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx, schedule, txOf, unhold, laneStageHook, installBoundaryHooks } from "./core/scheduler.js";
|
|
18
18
|
|
|
19
19
|
import { attrHooks } from "./core/attribution-hooks.js";
|
|
20
20
|
|
|
@@ -353,7 +353,7 @@ function createBoundary(e, r, n, t) {
|
|
|
353
353
|
} catch (e) {
|
|
354
354
|
if (!(e instanceof NotReadyError)) {
|
|
355
355
|
if (!globalQueue.notify(getOwner(), STATUS_ERROR, STATUS_ERROR, e)) {
|
|
356
|
-
haltReactivity(e);
|
|
356
|
+
if (false) ; else haltReactivity(e);
|
|
357
357
|
throw e;
|
|
358
358
|
}
|
|
359
359
|
return;
|
|
@@ -148,7 +148,9 @@ function releaseSettledDependents(e) {
|
|
|
148
148
|
// Staged whether or not the flush has joined a hold yet: a pass that runs
|
|
149
149
|
// before its async sibling joins is the seam's to decide, like a value
|
|
150
150
|
// (#4109). A flush that parks nothing publishes it at its commit (S-38).
|
|
151
|
-
|
|
151
|
+
// Not a projection's derive: its readers learn its error through the
|
|
152
|
+
// family, which the commit's publish never reaches (#4189).
|
|
153
|
+
if (!(e.l & CONFIG_HELD || n && e.R & REACTIVE_STAGED_READ && globalQueue.q && !e.fam) || passLane) return false;
|
|
152
154
|
// The node answered (an error ends the wait — no longer pending, nor
|
|
153
155
|
// blocked); a shown error stays its display until the commit.
|
|
154
156
|
clearPendingSources(e);
|
|
@@ -33,6 +33,8 @@ import { getOwner } from "./owner.js";
|
|
|
33
33
|
let r = t.A[e.id];
|
|
34
34
|
if (r === undefined) r = e.defaultValue;
|
|
35
35
|
if (r === undefined) {
|
|
36
|
+
// Dev names the context and why it has no value: no provider, or a
|
|
37
|
+
// provider that passed `undefined` (the key is set, to `undefined`).
|
|
36
38
|
throw new ContextNotFoundError;
|
|
37
39
|
}
|
|
38
40
|
return r;
|
|
@@ -14,7 +14,7 @@ import { attrHooks } from "./attribution-hooks.js";
|
|
|
14
14
|
|
|
15
15
|
import { inheritId, linkChild, disposeChildren, markDisposal } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook,
|
|
17
|
+
import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook, laneStageHook, passLane, laneReadHook, wireExternalSourceHook, bumpNotifyEpoch, setPassLane, joinPassTx, freshHook, GlobalQueue, laneOutcomeHook, passTx, holdNode, laneDirty, heldTrim, staleReader, laneSeamsHook, stagedReaders, deferZombie } from "./scheduler.js";
|
|
18
18
|
|
|
19
19
|
// The heap's per-node step. A tracked effect's heap visit is its compute
|
|
20
20
|
// phase — empty, like a user effect whose compute reads nothing — and hands
|
|
@@ -222,7 +222,9 @@ function recompute(e, t = false) {
|
|
|
222
222
|
// and own nothing to park.
|
|
223
223
|
if (n === EFFECT_TRACKED || e.l & CONFIG_STAGED) {
|
|
224
224
|
// A lazy node only those children read is released when this pass
|
|
225
|
-
// ends, if the pass doesn't read it again (#4142).
|
|
225
|
+
// ends, if the pass doesn't read it again (#4142). A cleanup or
|
|
226
|
+
// `unobserved` hook that throws here neither stops the pass nor skips
|
|
227
|
+
// that drain: its error is rethrown when the flush ends (owner.ts).
|
|
226
228
|
const t = deferredReleases;
|
|
227
229
|
deferReleases(o = []);
|
|
228
230
|
disposeChildren(e);
|
|
@@ -240,8 +242,8 @@ function recompute(e, t = false) {
|
|
|
240
242
|
// A conditional can drop its pending source and recover to an unchanged
|
|
241
243
|
// value, leaving blocked dependents outside that source’s settle walk.
|
|
242
244
|
const u = e.m & STATUS_ERROR ? e.T?.F : undefined;
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
+
const a = (e.m & STATUS_PENDING) !== 0;
|
|
246
|
+
const _ = a ? e.T?.fe : undefined;
|
|
245
247
|
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
246
248
|
// whose own flight parked dependents self-registers in _pendingSources
|
|
247
249
|
// (notifyStatus, isSource). If this recompute supersedes that flight and
|
|
@@ -332,7 +334,7 @@ function recompute(e, t = false) {
|
|
|
332
334
|
// re-park twin of the unchanged-value recovery sweep below; dependents
|
|
333
335
|
// with another path keep the source (retryReaches). A staged error
|
|
334
336
|
// answered what the pass carried: the same.
|
|
335
|
-
if ((n || N) &&
|
|
337
|
+
if ((n || N) && _) for (const t of _) if (t !== e && !e.T?.fe?.has(t)) settlePendingSource(e, t);
|
|
336
338
|
}
|
|
337
339
|
} finally {
|
|
338
340
|
tracking = d;
|
|
@@ -343,7 +345,10 @@ function recompute(e, t = false) {
|
|
|
343
345
|
// (markNode(c) in read()) marks the running node as part of ordinary
|
|
344
346
|
// bookkeeping, and those marks are correctly discarded here.
|
|
345
347
|
A = (e.R & REACTIVE_MISSED_WAKE) !== 0;
|
|
346
|
-
|
|
348
|
+
// A mount's staged read joins it — except a nested derivation left
|
|
349
|
+
// pending: no render effect reaches it yet, so it holds nothing and
|
|
350
|
+
// reaches no boundary (Reader, #4203). A reader that does is caught.
|
|
351
|
+
R = !!(e.R & (l && (s || n || !(e.m & STATUS_PENDING)) ? REACTIVE_JOINED | REACTIVE_STAGED_READ : REACTIVE_JOINED));
|
|
347
352
|
// A verdict reader is one for as long as it probes, or reads a memo
|
|
348
353
|
// that does (`read`): a pass that did neither (REACTIVE_PROBED, the same
|
|
349
354
|
// bit) is an ordinary derivation again.
|
|
@@ -526,7 +531,7 @@ function recompute(e, t = false) {
|
|
|
526
531
|
if (u !== undefined) settleErroredDependents(e, u);
|
|
527
532
|
// Self-registration (this node's own superseded flight) is the #3181
|
|
528
533
|
// sweep's business below — retiring it here too would walk twice.
|
|
529
|
-
if (
|
|
534
|
+
if (_) for (const t of _) if (t !== e) settlePendingSource(e, t);
|
|
530
535
|
}
|
|
531
536
|
// #3181: a synchronous settle supersedes the old landing callback, so
|
|
532
537
|
// recompute owns its pending-source sweep. An uninitialized node without
|
|
@@ -726,6 +731,12 @@ function computed(e, t) {
|
|
|
726
731
|
if (t.m & STATUS_UNINITIALIZED || e.i !== EFFECT_RENDER) joinPassTx(txOf(t), !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
|
|
727
732
|
}
|
|
728
733
|
|
|
734
|
+
/** `joinPass` into the hold `t` (a folded guess's: store/optimistic.ts
|
|
735
|
+
* `foldedRead`, D-34) — a reader is never a render effect there. */ function joinPassAt(e, t) {
|
|
736
|
+
e.R |= REACTIVE_JOINED;
|
|
737
|
+
joinPassTx(t, !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
|
|
738
|
+
}
|
|
739
|
+
|
|
729
740
|
/** A15's stale reader (shared-hole and reveal corollaries): a render effect
|
|
730
741
|
* reading a node held by a transaction that is not the flush's. It is served
|
|
731
742
|
* the committed value — a pending one's too — publishes mainline, and is
|
|
@@ -1197,7 +1208,9 @@ function read(e) {
|
|
|
1197
1208
|
// Committed visibility for untracked and children-forbidden readers.
|
|
1198
1209
|
if (!t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.Ie;
|
|
1199
1210
|
// (A plain signal is never uninitialized — the carve-out is `serve`'s.)
|
|
1200
|
-
|
|
1211
|
+
// Lane work over a folded lane (D-34), once the frame has a
|
|
1212
|
+
// transaction, reads the screen too.
|
|
1213
|
+
if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.Ie;
|
|
1201
1214
|
stagedRead(t);
|
|
1202
1215
|
return e.ue;
|
|
1203
1216
|
}
|
|
@@ -1338,7 +1351,10 @@ function read(e) {
|
|
|
1338
1351
|
|
|
1339
1352
|
/** A pass read a staging of this flush: it derives from what the flush may
|
|
1340
1353
|
* yet hold (REACTIVE_STAGED_READ — the seam decides). Lane work too (plan sec. 28, a
|
|
1341
|
-
* lane sees the screen plus its own guesses)
|
|
1354
|
+
* lane sees the screen plus its own guesses) — a folded lane's only before
|
|
1355
|
+
* the frame has a transaction (after, it reads the screen like a verdict
|
|
1356
|
+
* reader, `stagedScreen` — a list pass never pairs a held length with the
|
|
1357
|
+
* guess's rows, D-34): the staging is the screen if
|
|
1342
1358
|
* the frame commits — one pass, the common case — and a held write if it
|
|
1343
1359
|
* parks, which the seam repairs (`stagedReaders`): the pass re-derives on
|
|
1344
1360
|
* the committed world and its lane's runs wait that round, so the held
|
|
@@ -1580,4 +1596,4 @@ function staleValues(e, t = true) {
|
|
|
1580
1596
|
}
|
|
1581
1597
|
}
|
|
1582
1598
|
|
|
1583
|
-
export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, isSame, joinPass, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };
|
|
1599
|
+
export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, isSame, joinPass, joinPassAt, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };
|
|
@@ -50,6 +50,8 @@ function notifyEffectStatus(t, e) {
|
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
if (!globalQueue.notify(this, STATUS_ERROR, STATUS_ERROR)) {
|
|
53
|
+
// Dev locates the halt at the node that threw: the StatusError's
|
|
54
|
+
// source, upstream of this effect when a computed failed.
|
|
53
55
|
haltReactivity(unwrapStatusError(u));
|
|
54
56
|
throw u;
|
|
55
57
|
}
|
|
@@ -2,7 +2,7 @@ import { REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIV
|
|
|
2
2
|
|
|
3
3
|
import { deleteFromHeap } from "./heap.js";
|
|
4
4
|
|
|
5
|
-
import { disposeChildren } from "./owner.js";
|
|
5
|
+
import { disposeChildren, runCleanup } from "./owner.js";
|
|
6
6
|
|
|
7
7
|
import { bumpNotifyEpoch, dirtyQueue } from "./scheduler.js";
|
|
8
8
|
|
|
@@ -27,7 +27,9 @@ function unlinkSubs(e) {
|
|
|
27
27
|
if (o === null) {
|
|
28
28
|
// Store slot nodes dispatch to the ONE shared hook — no per-node
|
|
29
29
|
// unobserved closure, no NodeExtension to hold it.
|
|
30
|
-
|
|
30
|
+
// A user hook that throws is collected like a cleanup (owner.ts): the
|
|
31
|
+
// unlink and the teardown around it carry on.
|
|
32
|
+
if (n.l & CONFIG_SLOT_NODE) slotUnobservedHook(n); else if (n.T?.Dt) runCleanup(n.T.Dt);
|
|
31
33
|
// No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
|
|
32
34
|
// A pending node is exempt: its in-flight async work is an observer —
|
|
33
35
|
// tearing down would orphan the work and re-execute it on the next
|
|
@@ -141,18 +143,18 @@ function link(e, n) {
|
|
|
141
143
|
// [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
|
|
142
144
|
// (the old alien-signals `isValidLink` walk, O(n²) when a computation
|
|
143
145
|
// re-reads earlier deps non-consecutively, e.g. store leaf reads).
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
const
|
|
146
|
+
const r = e.dt;
|
|
147
|
+
if (r !== null && r.u === n && (!s || r.Te === n.Ne)) return;
|
|
148
|
+
const t = n._ = e.dt = {
|
|
147
149
|
S: e,
|
|
148
150
|
u: n,
|
|
149
151
|
G: o,
|
|
150
|
-
en:
|
|
152
|
+
en: r,
|
|
151
153
|
t: null,
|
|
152
154
|
Te: n.Ne
|
|
153
155
|
};
|
|
154
|
-
if (l !== null) l.G =
|
|
155
|
-
if (
|
|
156
|
+
if (l !== null) l.G = t; else n.C = t;
|
|
157
|
+
if (r !== null) r.t = t; else e.o = t;
|
|
156
158
|
// New subscriber edge: staged-rewrite skips (§12d) must not miss it.
|
|
157
159
|
bumpNotifyEpoch();
|
|
158
160
|
}
|