@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.19
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 +426 -241
- package/dist/dev.attribution.js +19 -16
- package/dist/dev.js +768 -409
- package/dist/observe/boundaries.js +88 -67
- package/dist/observe/core/action.js +8 -0
- package/dist/observe/core/async.js +183 -138
- package/dist/observe/core/attribution.js +30 -28
- package/dist/observe/core/constants.js +23 -1
- package/dist/observe/core/core.js +185 -157
- package/dist/observe/core/effect.js +34 -37
- package/dist/observe/core/graph.js +8 -8
- package/dist/observe/core/heap.js +5 -5
- package/dist/observe/core/lanes.js +104 -84
- package/dist/observe/core/owner.js +17 -16
- package/dist/observe/core/scheduler.js +61 -47
- package/dist/observe/core/truth.js +6 -6
- package/dist/observe/core/verdict.js +15 -15
- package/dist/observe/deferred.js +40 -38
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +4 -2
- package/dist/observe/map.js +22 -22
- package/dist/observe/reveal.js +5 -5
- package/dist/observe/signals.js +57 -15
- package/dist/observe/store/affects.js +1 -1
- package/dist/observe/store/hooks.js +3 -0
- package/dist/observe/store/optimistic.js +11 -11
- package/dist/observe/store/projection.js +34 -21
- package/dist/observe/store/reconcile.js +138 -122
- package/dist/observe/store/store.js +628 -641
- package/dist/observe/store/target.js +1 -5
- package/dist/observe/store/utils.js +7 -2
- package/dist/prod/boundaries.js +153 -132
- package/dist/prod/core/action.js +8 -0
- package/dist/prod/core/async.js +242 -197
- package/dist/prod/core/constants.js +16 -1
- package/dist/prod/core/core.js +166 -138
- package/dist/prod/core/effect.js +31 -34
- package/dist/prod/core/graph.js +5 -5
- package/dist/prod/core/heap.js +12 -12
- package/dist/prod/core/lanes.js +98 -78
- package/dist/prod/core/owner.js +15 -14
- package/dist/prod/core/scheduler.js +64 -50
- package/dist/prod/core/truth.js +6 -6
- package/dist/prod/core/verdict.js +17 -17
- package/dist/prod/deferred.js +40 -38
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +4 -2
- package/dist/prod/map.js +14 -14
- package/dist/prod/reveal.js +8 -8
- package/dist/prod/signals.js +46 -4
- package/dist/prod/store/affects.js +1 -1
- package/dist/prod/store/hooks.js +3 -0
- package/dist/prod/store/optimistic.js +16 -16
- package/dist/prod/store/projection.js +22 -9
- package/dist/prod/store/reconcile.js +138 -122
- package/dist/prod/store/store.js +308 -331
- package/dist/prod/store/target.js +13 -17
- package/dist/prod/store/utils.js +2 -0
- package/dist/types/core/action.d.ts +8 -0
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +25 -0
- package/dist/types/core/core.d.ts +33 -4
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +5 -1
- package/dist/types/core/scheduler.d.ts +17 -3
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/signals.d.ts +37 -0
- package/dist/types/store/target.d.ts +2 -0
- package/package.json +1 -1
package/dist/dev.js
CHANGED
|
@@ -33,8 +33,8 @@ import {
|
|
|
33
33
|
A as tracking,
|
|
34
34
|
B as CONFIG_CHILDREN_FORBIDDEN,
|
|
35
35
|
D as passLane,
|
|
36
|
-
E as
|
|
37
|
-
F as
|
|
36
|
+
E as EFFECT_RENDER,
|
|
37
|
+
F as setPassLane,
|
|
38
38
|
H as sameLane,
|
|
39
39
|
I as flushTransaction,
|
|
40
40
|
J as REACTIVE_SCREEN_READ,
|
|
@@ -44,15 +44,15 @@ import {
|
|
|
44
44
|
O as REACTIVE_IN_HEAP,
|
|
45
45
|
P as REACTIVE_DIRTY,
|
|
46
46
|
Q as REACTIVE_CHECK,
|
|
47
|
-
T as
|
|
48
|
-
U as
|
|
49
|
-
V as
|
|
50
|
-
W as
|
|
51
|
-
X as
|
|
52
|
-
Y as
|
|
53
|
-
Z as
|
|
54
|
-
_ as
|
|
55
|
-
$ as
|
|
47
|
+
T as STATUS_PENDING,
|
|
48
|
+
U as CONFIG_INPUTS_PUBLISHED,
|
|
49
|
+
V as releaseQueues,
|
|
50
|
+
W as CONFIG_SLOT_NODE,
|
|
51
|
+
X as holdNode,
|
|
52
|
+
Y as joinFuture,
|
|
53
|
+
Z as reruns,
|
|
54
|
+
_ as CONFIG_STAGED,
|
|
55
|
+
$ as merge$1,
|
|
56
56
|
a0 as REACTIVE_JOINED,
|
|
57
57
|
a1 as REACTIVE_STAGED_READ,
|
|
58
58
|
a2 as stagedRead,
|
|
@@ -64,123 +64,130 @@ import {
|
|
|
64
64
|
a8 as link,
|
|
65
65
|
a9 as pullComputed,
|
|
66
66
|
aa as strictRead,
|
|
67
|
-
ab as
|
|
68
|
-
ac as
|
|
69
|
-
ad as
|
|
70
|
-
ae as
|
|
71
|
-
af as
|
|
72
|
-
ag as
|
|
73
|
-
ah as
|
|
74
|
-
ai as
|
|
75
|
-
aj as
|
|
76
|
-
ak as
|
|
77
|
-
al as
|
|
78
|
-
am as
|
|
79
|
-
an as
|
|
80
|
-
ao as
|
|
81
|
-
ap as
|
|
82
|
-
aq as
|
|
83
|
-
ar as
|
|
84
|
-
as as
|
|
85
|
-
at as
|
|
86
|
-
au as
|
|
87
|
-
av as
|
|
88
|
-
aw as
|
|
89
|
-
ax as
|
|
90
|
-
ay as
|
|
91
|
-
az as
|
|
92
|
-
aA as
|
|
93
|
-
aB as
|
|
94
|
-
aC as
|
|
95
|
-
aD as
|
|
96
|
-
aE as
|
|
97
|
-
aF as
|
|
98
|
-
aG as
|
|
99
|
-
aH as
|
|
100
|
-
aI as
|
|
101
|
-
aJ as
|
|
102
|
-
aK as
|
|
103
|
-
aL as
|
|
104
|
-
aM as
|
|
105
|
-
aN as
|
|
106
|
-
aO as
|
|
107
|
-
aP as
|
|
108
|
-
aQ as
|
|
109
|
-
aR as
|
|
110
|
-
aS as
|
|
111
|
-
aT as
|
|
112
|
-
aU as
|
|
113
|
-
aV as
|
|
114
|
-
aW as
|
|
115
|
-
aX as
|
|
116
|
-
aY as
|
|
117
|
-
aZ as
|
|
118
|
-
a_ as
|
|
119
|
-
a$ as
|
|
120
|
-
b0 as
|
|
121
|
-
b1 as
|
|
122
|
-
b2 as
|
|
123
|
-
b3 as
|
|
124
|
-
b4 as
|
|
125
|
-
b5 as
|
|
126
|
-
b6 as
|
|
127
|
-
b7 as
|
|
128
|
-
b8 as
|
|
129
|
-
b9 as
|
|
130
|
-
ba as
|
|
131
|
-
bb as
|
|
132
|
-
bc as
|
|
133
|
-
bd as
|
|
134
|
-
be as
|
|
135
|
-
bf as
|
|
136
|
-
bg as
|
|
137
|
-
bh as
|
|
138
|
-
bi as
|
|
139
|
-
bj as
|
|
140
|
-
bk as
|
|
141
|
-
bl as
|
|
142
|
-
bm as
|
|
143
|
-
bn as
|
|
144
|
-
bo as
|
|
145
|
-
bp as
|
|
146
|
-
bq as
|
|
147
|
-
br as
|
|
148
|
-
bs as
|
|
149
|
-
bt as
|
|
150
|
-
bu as
|
|
151
|
-
bv as
|
|
152
|
-
bw as
|
|
153
|
-
bx as
|
|
154
|
-
by as
|
|
155
|
-
bz as
|
|
156
|
-
bA as
|
|
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
|
|
170
|
-
bO as
|
|
171
|
-
bP as
|
|
67
|
+
ab as strictReadActive,
|
|
68
|
+
ac as warnStrictReadUntracked,
|
|
69
|
+
ad as staleReader,
|
|
70
|
+
ae as globalQueue,
|
|
71
|
+
af as joinPass,
|
|
72
|
+
ag as markLateLinker,
|
|
73
|
+
ah as spectating,
|
|
74
|
+
ai as unflushedValue,
|
|
75
|
+
aj as CONFIG_STAGED_ERROR,
|
|
76
|
+
ak as stagedReaders,
|
|
77
|
+
al as bornInto,
|
|
78
|
+
am as enterAction,
|
|
79
|
+
an as setEffectCallback,
|
|
80
|
+
ao as exitAction,
|
|
81
|
+
ap as isThenable,
|
|
82
|
+
aq as actionDepth,
|
|
83
|
+
ar as flush,
|
|
84
|
+
as as setQuestion,
|
|
85
|
+
at as emitDiagnostic,
|
|
86
|
+
au as enterCallback,
|
|
87
|
+
av as exitCallback,
|
|
88
|
+
aw as getOwner,
|
|
89
|
+
ax as inEffectCallback,
|
|
90
|
+
ay as effectCallbackEvent,
|
|
91
|
+
az as setExternalHooks,
|
|
92
|
+
aA as signal,
|
|
93
|
+
aB as setSignal,
|
|
94
|
+
aC as cleanup,
|
|
95
|
+
aD as read,
|
|
96
|
+
aE as DEV$1,
|
|
97
|
+
aF as CONFIG_TRANSPARENT,
|
|
98
|
+
aG as NoOwnerError,
|
|
99
|
+
aH as ContextNotFoundError,
|
|
100
|
+
aI as createEffectNode,
|
|
101
|
+
aJ as recompute,
|
|
102
|
+
aK as EFFECT_USER,
|
|
103
|
+
aL as reportDiagnostic,
|
|
104
|
+
aM as computed,
|
|
105
|
+
aN as staleValues,
|
|
106
|
+
aO as CONFIG_AUTO_DISPOSE,
|
|
107
|
+
aP as EFFECT_TRACKED,
|
|
108
|
+
aQ as heldRunHook,
|
|
109
|
+
aR as unwrapStatusError,
|
|
110
|
+
aS as setStrictRead,
|
|
111
|
+
aT as queueEvent,
|
|
112
|
+
aU as StatusError,
|
|
113
|
+
aV as trimStaleDeps,
|
|
114
|
+
aW as haltReactivityAt,
|
|
115
|
+
aX as strictReadEvent,
|
|
116
|
+
aY as setTrackedQueueCallback,
|
|
117
|
+
aZ as setEffectStatusNotify,
|
|
118
|
+
a_ as _hitUnhandledAsync,
|
|
119
|
+
a$ as resetUnhandledAsync,
|
|
120
|
+
b0 as ROOT_ERROR_HOOK,
|
|
121
|
+
b1 as registerGraph,
|
|
122
|
+
b2 as runWithOwner,
|
|
123
|
+
b3 as setMemo,
|
|
124
|
+
b4 as $REFRESH,
|
|
125
|
+
b5 as markRefresh,
|
|
126
|
+
b6 as createRoot,
|
|
127
|
+
b7 as getObserver,
|
|
128
|
+
b8 as dispose,
|
|
129
|
+
b9 as TimeoutError,
|
|
130
|
+
ba as untrack,
|
|
131
|
+
bb as dirtyQueue,
|
|
132
|
+
bc as CONFIG_REDERIVE,
|
|
133
|
+
bd as passTx,
|
|
134
|
+
be as CONFIG_FRESH_READ,
|
|
135
|
+
bf as createOwner,
|
|
136
|
+
bg as writeHook,
|
|
137
|
+
bh as REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE,
|
|
138
|
+
bi as ownedScopeWriteMessage,
|
|
139
|
+
bj as isEqual,
|
|
140
|
+
bk as CONFIG_MANUAL_WRITE,
|
|
141
|
+
bl as snapshotCaptureActive,
|
|
142
|
+
bm as captureWriteSnapshot,
|
|
143
|
+
bn as notePromotedWrite,
|
|
144
|
+
bo as slotSignal,
|
|
145
|
+
bp as stagedScreen,
|
|
146
|
+
bq as CONFIG_PROMOTED,
|
|
147
|
+
br as setSlotUnobserved,
|
|
148
|
+
bs as installStoreHooks,
|
|
149
|
+
bt as joinPassTx,
|
|
150
|
+
bu as asyncTailFlights,
|
|
151
|
+
bv as untrackDepth,
|
|
152
|
+
bw as checkPostAwaitRead,
|
|
153
|
+
bx as disposalDepth,
|
|
154
|
+
by as insertIntoHeap,
|
|
155
|
+
bz as isDisposed,
|
|
156
|
+
bA as handleAsync,
|
|
157
|
+
bB as addPendingSource,
|
|
158
|
+
bC as propagateStatus,
|
|
159
|
+
bD as forEachDependent,
|
|
160
|
+
bE as settlePendingSource,
|
|
161
|
+
bF as joinPassAt,
|
|
162
|
+
bG as notifyEpoch,
|
|
163
|
+
bH as SUPPORTS_PROXY,
|
|
164
|
+
bI as CONFIG_KEY_BAG,
|
|
165
|
+
bJ as releaseMarksHook,
|
|
166
|
+
bK as installMarkHooks,
|
|
167
|
+
bL as CONFIG_LAGS,
|
|
168
|
+
bM as REACTIVE_REASK,
|
|
169
|
+
bN as onSupersede,
|
|
170
|
+
bO as haltReactivity,
|
|
171
|
+
bP as CONFIG_FRAME,
|
|
172
|
+
bQ as heldUnder,
|
|
173
|
+
bR as unhold,
|
|
174
|
+
bS as laneStageHook,
|
|
175
|
+
bT as REACTIVE_ZOMBIE,
|
|
176
|
+
bU as installBoundaryHooks,
|
|
177
|
+
bV as CAUGHT_REARMED,
|
|
178
|
+
bW as OBSERVE$1
|
|
172
179
|
} from "./dev-shared.js";
|
|
173
180
|
export {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
bX as clearSnapshots,
|
|
182
|
+
bY as enforceLoadingBoundary,
|
|
183
|
+
bZ as getNextChildId,
|
|
184
|
+
b_ as markSnapshotScope,
|
|
185
|
+
b$ as peekNextChildId,
|
|
186
|
+
c0 as releaseSnapshotScope,
|
|
187
|
+
c1 as resetErrorHalt,
|
|
188
|
+
c2 as setConsoleFooter,
|
|
189
|
+
c3 as setSnapshotCapture,
|
|
190
|
+
c4 as setWriteHook
|
|
184
191
|
} from "./dev-shared.js";
|
|
185
192
|
|
|
186
193
|
// Truth reads (`until`, `refresh`; CONFIG_AUTHORITATIVE) over lane nodes.
|
|
@@ -695,7 +702,11 @@ function dissolveLane(l, into, except) {
|
|
|
695
702
|
* has shown, or one the seam has not judged yet (its run waits on the
|
|
696
703
|
* verdict: released if the lane shows, held if it blocks) — and sees the
|
|
697
704
|
* lane's latest. NOT_PENDING falls through: lane work's read of a pending
|
|
698
|
-
* member throws like any.
|
|
705
|
+
* member throws like any. A verdict reader entering a lane the seam has
|
|
706
|
+
* not judged reads ahead of it like a staging read: if the seam holds the
|
|
707
|
+
* lane, the pass re-derives on the screen (`entered`, #4284) — `latest()`
|
|
708
|
+
* beside the lane's nodes tears ahead of the hold however the reads are
|
|
709
|
+
* grouped into effects. */
|
|
699
710
|
function laneRead(c, el) {
|
|
700
711
|
const guess = el._config & CONFIG_GUESS;
|
|
701
712
|
const l = txOf(el);
|
|
@@ -713,6 +724,7 @@ function laneRead(c, el) {
|
|
|
713
724
|
// An errored member's lane value predates its error; the error is the answer (#4106).
|
|
714
725
|
if (!guess && status & STATUS_ERROR) return NOT_PENDING;
|
|
715
726
|
if (c === null) return laneValueOf(el);
|
|
727
|
+
const pending = !guess && status & STATUS_PENDING;
|
|
716
728
|
if (!tracking || c._config & CONFIG_CHILDREN_FORBIDDEN) {
|
|
717
729
|
// An untracked read inside lane work derives from the lane like a
|
|
718
730
|
// tracked one, minus the subscription — `untrack` is about
|
|
@@ -721,40 +733,47 @@ function laneRead(c, el) {
|
|
|
721
733
|
// lanes read by one pass link, as for tracked reads). Outside lane
|
|
722
734
|
// work, the screen.
|
|
723
735
|
if (passLane === null || c._config & CONFIG_CHILDREN_FORBIDDEN) return display(el);
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
+
} else if (c._type === EFFECT_RENDER) {
|
|
737
|
+
// A flight whose value has never shown: nothing to show — the leaf
|
|
738
|
+
// waits on it, and its own frame holds (A15, #3334), not the lane's
|
|
739
|
+
// parent. The landing lands the frame: its own pass (the flush joined
|
|
740
|
+
// its transaction) reads the answer it waited on — the lane's staging,
|
|
741
|
+
// revealed at this seam.
|
|
742
|
+
const never = pending && el._x._lane === NOT_PENDING;
|
|
743
|
+
// D-28: a leaf a verdict read moved into the lane still throws on such
|
|
744
|
+
// a flight — the pass leaves the lane, and its frame holds as if the
|
|
745
|
+
// read had come first (S-39, #4275). Nor does a verdict lane's seat carry
|
|
746
|
+
// the leaf into another lane (no link): it reads that lane as from the
|
|
747
|
+
// frame's seat (#4284).
|
|
748
|
+
if ((never || passLane !== l) && passLane?._parent._verdict === passLane) setPassLane(null);
|
|
749
|
+
if (passLane === null || !sameLane(passLane, l)) {
|
|
750
|
+
// (A lane that has never shown: the screen is the committed value.)
|
|
751
|
+
if (never && l._shown) return NOT_PENDING;
|
|
736
752
|
if (
|
|
753
|
+
!guess &&
|
|
737
754
|
el._pendingValue !== NOT_PENDING &&
|
|
738
755
|
c._config & CONFIG_HELD &&
|
|
739
756
|
flushTransaction !== null &&
|
|
740
757
|
resolveTx(flushTransaction) === txOf(c)
|
|
741
758
|
)
|
|
742
759
|
return el._pendingValue;
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
760
|
+
if (l._held || pending) {
|
|
761
|
+
if (!guess && !l._shown && status & STATUS_UNINITIALIZED) throw new NotReadyError(null);
|
|
762
|
+
// Re-derived at the reveal; once per pass (the pass may also be a stale
|
|
763
|
+
// reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
|
|
764
|
+
if (!(c._flags & REACTIVE_SCREEN_READ)) {
|
|
765
|
+
c._flags |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
|
|
766
|
+
l._reruns.push(c);
|
|
767
|
+
}
|
|
768
|
+
return display(el);
|
|
751
769
|
}
|
|
752
|
-
|
|
770
|
+
// A verdict reader reads ahead of the seam's verdict: re-derived on the
|
|
771
|
+
// screen if the lane holds (#4284).
|
|
772
|
+
if (c._config & CONFIG_VERDICT && l._parent._verdict !== l) entered.push(c);
|
|
753
773
|
}
|
|
754
774
|
}
|
|
755
775
|
enterLane(l, c);
|
|
756
|
-
|
|
757
|
-
return laneValueOf(el);
|
|
776
|
+
return pending ? NOT_PENDING : laneValueOf(el);
|
|
758
777
|
}
|
|
759
778
|
/** The value a guess covered: the node's committed value — a store slot's
|
|
760
779
|
* asked of the store (a chained link follows the inner store's commits). */
|
|
@@ -987,7 +1006,17 @@ const laneSeams = leaks => {
|
|
|
987
1006
|
}
|
|
988
1007
|
for (let k = Math.min(judged, lanes.length) - 1; k >= 0; k--)
|
|
989
1008
|
laneSeam(lanes[k], leaked.indexOf(lanes[k]) !== -1);
|
|
1009
|
+
// A verdict reader that entered a lane this seam held: its run waits with
|
|
1010
|
+
// the lane, and the verdict beside it would freeze — it re-derives next
|
|
1011
|
+
// round from the frame's seat, a stale reader of the lane (#4284).
|
|
1012
|
+
for (let r; (r = entered.pop()); )
|
|
1013
|
+
if (r._x._transaction?._held) {
|
|
1014
|
+
r._flags &= ~REACTIVE_LANE_DIRTY;
|
|
1015
|
+
enqueueSub(r);
|
|
1016
|
+
}
|
|
990
1017
|
};
|
|
1018
|
+
/** Render effects that read a verdict and entered a lane before its seam (#4284). */
|
|
1019
|
+
const entered = [];
|
|
991
1020
|
installLaneHooks(
|
|
992
1021
|
laneRead,
|
|
993
1022
|
laneStage,
|
|
@@ -1350,7 +1379,7 @@ function verdictValue(el, c) {
|
|
|
1350
1379
|
// verdict read as on any untracked read; the pending throw they add for a
|
|
1351
1380
|
// plain read does not apply — a verdict read is a sanctioned read of a
|
|
1352
1381
|
// pending node (an uninitialized one still throws NotReady below).
|
|
1353
|
-
if (strictRead)
|
|
1382
|
+
if (strictRead && strictReadActive())
|
|
1354
1383
|
warnStrictReadUntracked(strictRead, {
|
|
1355
1384
|
ownerId: c?.id,
|
|
1356
1385
|
ownerName: c?._name,
|
|
@@ -1505,6 +1534,14 @@ function inTransaction(t, q, fn) {
|
|
|
1505
1534
|
* yield until(() => todos.some(t => t.id === saved.id));
|
|
1506
1535
|
* ```
|
|
1507
1536
|
*
|
|
1537
|
+
* The same holds inside an async helper the action yields
|
|
1538
|
+
* (`yield (async () => { ... })()`): everything after its first `await` runs
|
|
1539
|
+
* outside the transaction. An `await refresh(x)` or `await until(...)` there
|
|
1540
|
+
* waits for this action's commit, which waits on the helper, so it hangs
|
|
1541
|
+
* whenever `x` re-ran during the action (#4263) and works otherwise. Call
|
|
1542
|
+
* `refresh(x)` in the generator once the helper returns; `yield` it only when
|
|
1543
|
+
* the action needs the fresh value.
|
|
1544
|
+
*
|
|
1508
1545
|
* (For the same reason, don't call `flush()` inside an action body — it
|
|
1509
1546
|
* cannot reveal the held writes and is refused.)
|
|
1510
1547
|
*
|
|
@@ -1583,21 +1620,23 @@ function action(genFn) {
|
|
|
1583
1620
|
// it may have been called from — a root created in it (`until`,
|
|
1584
1621
|
// `resolve`) is deliberate (PRIMITIVE_IN_EFFECT_CALLBACK is off).
|
|
1585
1622
|
let effectCallback = false;
|
|
1623
|
+
let effectEvent;
|
|
1586
1624
|
{
|
|
1587
1625
|
enterCallback();
|
|
1588
1626
|
effectCallback = inEffectCallback;
|
|
1627
|
+
effectEvent = effectCallbackEvent;
|
|
1589
1628
|
setEffectCallback(false);
|
|
1590
1629
|
}
|
|
1591
1630
|
try {
|
|
1592
1631
|
r = err ? it.throw(v) : it.next(v);
|
|
1593
1632
|
} catch (e) {
|
|
1594
1633
|
exitAction();
|
|
1595
|
-
(exitCallback(), setEffectCallback(effectCallback));
|
|
1634
|
+
(exitCallback(), setEffectCallback(effectCallback, effectEvent));
|
|
1596
1635
|
if (attrHooks !== null) attrHooks.actionStepEnd(it);
|
|
1597
1636
|
return done(undefined, e, true);
|
|
1598
1637
|
}
|
|
1599
1638
|
exitAction();
|
|
1600
|
-
(exitCallback(), setEffectCallback(effectCallback));
|
|
1639
|
+
(exitCallback(), setEffectCallback(effectCallback, effectEvent));
|
|
1601
1640
|
if (attrHooks !== null) attrHooks.actionStepEnd(it);
|
|
1602
1641
|
// A rejected iterator result (async generators) means the error already
|
|
1603
1642
|
// escaped the generator body — it is completed, and throwing back in
|
|
@@ -1887,10 +1926,9 @@ function effect(compute, effect, error, options) {
|
|
|
1887
1926
|
);
|
|
1888
1927
|
}
|
|
1889
1928
|
}
|
|
1890
|
-
function notifyEffectStatus(
|
|
1891
|
-
|
|
1892
|
-
const
|
|
1893
|
-
const actualError = error !== undefined ? error : this._x?._error;
|
|
1929
|
+
function notifyEffectStatus() {
|
|
1930
|
+
const actualStatus = this._statusFlags;
|
|
1931
|
+
const actualError = this._x?._error;
|
|
1894
1932
|
if (actualStatus & STATUS_ERROR) {
|
|
1895
1933
|
globalQueue.notify(this, STATUS_PENDING, 0);
|
|
1896
1934
|
if (this._type === EFFECT_USER) {
|
|
@@ -1898,24 +1936,22 @@ function notifyEffectStatus(status, error) {
|
|
|
1898
1936
|
// queue it like the effect function. It runs in the same imperative,
|
|
1899
1937
|
// writable scope and throws escalate the same way. No payload is queued — the node already carries `_statusFlags`/`_error`,
|
|
1900
1938
|
// and the runner dispatches on them, so a recovery before the effect
|
|
1901
|
-
// phase takes the success arm instead.
|
|
1902
|
-
//
|
|
1903
|
-
// re-propagates unblocked at commit.
|
|
1939
|
+
// phase takes the success arm instead. (Re-read: the boundary
|
|
1940
|
+
// notify above can run a redraw.)
|
|
1904
1941
|
if (this._statusFlags & STATUS_ERROR) {
|
|
1905
1942
|
this._modified = true;
|
|
1906
1943
|
globalQueue.enqueue(this._type, (this._boundRunEffect ??= runEffect.bind(null, this)));
|
|
1907
1944
|
}
|
|
1908
1945
|
return;
|
|
1909
1946
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
}
|
|
1947
|
+
// Dev locates the halt at the node that threw: the StatusError's
|
|
1948
|
+
// source, upstream of this effect when a computed failed.
|
|
1949
|
+
escalate(
|
|
1950
|
+
this,
|
|
1951
|
+
unwrapStatusError(actualError),
|
|
1952
|
+
actualError,
|
|
1953
|
+
actualError instanceof StatusError ? actualError.source : this
|
|
1954
|
+
);
|
|
1919
1955
|
} else if (this._type === EFFECT_RENDER) {
|
|
1920
1956
|
globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, actualStatus, actualError);
|
|
1921
1957
|
if (_hitUnhandledAsync && resetUnhandledAsync()) {
|
|
@@ -1944,6 +1980,14 @@ function notifyEffectStatus(status, error) {
|
|
|
1944
1980
|
}
|
|
1945
1981
|
}
|
|
1946
1982
|
}
|
|
1983
|
+
/** An effect's error no boundary takes halts the system (boundary-or-halt):
|
|
1984
|
+
* halted with `cause`, `thrown` rethrown; dev locates it at `subject`. */
|
|
1985
|
+
function escalate(node, cause, thrown = cause, subject = node) {
|
|
1986
|
+
if (!globalQueue.notify(node, STATUS_ERROR, STATUS_ERROR)) {
|
|
1987
|
+
haltReactivityAt(cause, subject);
|
|
1988
|
+
throw thrown;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1947
1991
|
function runEffect(node, type) {
|
|
1948
1992
|
if (!node._modified || node._flags & REACTIVE_DISPOSED) return;
|
|
1949
1993
|
// A queued run behind a fallback (boundaries.ts) waits for the reveal: a
|
|
@@ -1978,10 +2022,7 @@ function runEffect(node, type) {
|
|
|
1978
2022
|
})
|
|
1979
2023
|
: console.error(err);
|
|
1980
2024
|
} catch (error) {
|
|
1981
|
-
|
|
1982
|
-
haltReactivityAt(error, node);
|
|
1983
|
-
throw error;
|
|
1984
|
-
}
|
|
2025
|
+
escalate(node, error);
|
|
1985
2026
|
}
|
|
1986
2027
|
return;
|
|
1987
2028
|
}
|
|
@@ -1989,9 +2030,13 @@ function runEffect(node, type) {
|
|
|
1989
2030
|
// the compute pass that produced `_value` was clean, so its tail still goes.
|
|
1990
2031
|
const cleanPass = node._x?._error == null;
|
|
1991
2032
|
let prevStrictRead = false;
|
|
2033
|
+
let prevStrictReadEvent;
|
|
1992
2034
|
{
|
|
1993
|
-
|
|
1994
|
-
|
|
2035
|
+
prevStrictReadEvent = strictReadEvent;
|
|
2036
|
+
// A run outside a queue (a render effect's first run at creation) records
|
|
2037
|
+
// no event: its callback is the compiler's DOM writes.
|
|
2038
|
+
prevStrictRead = setStrictRead("an effect callback", queueEvent);
|
|
2039
|
+
setEffectCallback(true, queueEvent);
|
|
1995
2040
|
enterCallback();
|
|
1996
2041
|
}
|
|
1997
2042
|
// Observe tier, like its `effectRunEnd` twin below: the frame the engine
|
|
@@ -2014,13 +2059,10 @@ function runEffect(node, type) {
|
|
|
2014
2059
|
} catch (error) {
|
|
2015
2060
|
ext(node)._error = new StatusError(node, error);
|
|
2016
2061
|
node._statusFlags |= STATUS_ERROR;
|
|
2017
|
-
|
|
2018
|
-
haltReactivityAt(error, node);
|
|
2019
|
-
throw error;
|
|
2020
|
-
}
|
|
2062
|
+
escalate(node, error);
|
|
2021
2063
|
} finally {
|
|
2022
2064
|
{
|
|
2023
|
-
setStrictRead(prevStrictRead);
|
|
2065
|
+
setStrictRead(prevStrictRead, prevStrictReadEvent);
|
|
2024
2066
|
setEffectCallback(false);
|
|
2025
2067
|
exitCallback();
|
|
2026
2068
|
}
|
|
@@ -2210,9 +2252,6 @@ function lookupTarget(raw, fam) {
|
|
|
2210
2252
|
const owner = raw[$OWNER];
|
|
2211
2253
|
return owner !== undefined && owner.fam === fam ? owner : (fam?.map ?? storeLookup).get(raw);
|
|
2212
2254
|
}
|
|
2213
|
-
function devAssertNeverUserMutation(target) {
|
|
2214
|
-
return;
|
|
2215
|
-
}
|
|
2216
2255
|
/** Sticky descendants flag up the parent chain (§6d): adoption descends
|
|
2217
2256
|
* into a changed child pair only where a target with nodes exists below. */
|
|
2218
2257
|
function markDescendants(target) {
|
|
@@ -3086,6 +3125,65 @@ function onSettled(callback) {
|
|
|
3086
3125
|
}
|
|
3087
3126
|
});
|
|
3088
3127
|
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Schedules `callback` to run **once**, right after the pending flush
|
|
3130
|
+
* finishes, in the same microtask checkpoint (before the browser paints).
|
|
3131
|
+
* Unlike `onSettled`, it never waits on async work: if that flush is held by
|
|
3132
|
+
* a pending transition, the callback still runs.
|
|
3133
|
+
*
|
|
3134
|
+
* Its main use is keeping a write out of another update's hold. Writes made
|
|
3135
|
+
* in the same synchronous frame are applied together, so when one of them
|
|
3136
|
+
* starts async work (a route change, a fetch), the others are held with it
|
|
3137
|
+
* until it resolves. A write made in `onFlush` happens after that flush, so
|
|
3138
|
+
* it is its own update: it commits on its own unless something it changes
|
|
3139
|
+
* also derives from the held work (a memo or `createEffect` reading both),
|
|
3140
|
+
* in which case it joins that hold as any later write would.
|
|
3141
|
+
*
|
|
3142
|
+
* Call it from event handlers and effect callbacks (including
|
|
3143
|
+
* `createTrackedEffect` and `onSettled` bodies). In an owned scope such as a
|
|
3144
|
+
* component body or a computation it warns in dev: use `onSettled` there.
|
|
3145
|
+
*
|
|
3146
|
+
* - A write in the callback lands after any write made inline in the same
|
|
3147
|
+
* frame. Don't write the same signal both inside and outside it; use a
|
|
3148
|
+
* functional setter if the order matters.
|
|
3149
|
+
* - Several `onFlush` callbacks run in call order and batch into one flush.
|
|
3150
|
+
* - `queueMicrotask` is not a substitute: called before the frame's first
|
|
3151
|
+
* write, it runs before that flush and its write joins it.
|
|
3152
|
+
*
|
|
3153
|
+
* @example
|
|
3154
|
+
* ```tsx
|
|
3155
|
+
* // A menu closes now even when the consumer's onSelect starts async work.
|
|
3156
|
+
* const select = (id: string) => {
|
|
3157
|
+
* onFlush(() => setOpen(false));
|
|
3158
|
+
* props.onSelect?.(id);
|
|
3159
|
+
* };
|
|
3160
|
+
* ```
|
|
3161
|
+
*
|
|
3162
|
+
* @param callback Function to run once after the pending flush
|
|
3163
|
+
*/
|
|
3164
|
+
function onFlush(callback) {
|
|
3165
|
+
{
|
|
3166
|
+
const owner = getOwner();
|
|
3167
|
+
if (owner && !(owner._config & CONFIG_CHILDREN_FORBIDDEN)) {
|
|
3168
|
+
const message =
|
|
3169
|
+
"[ON_FLUSH_IN_OWNED_SCOPE] onFlush called in an owned scope (a component body or a computation). It is meant for event handlers and effect callbacks; to run work after this scope's first stable render, use onSettled.";
|
|
3170
|
+
reportDiagnostic(
|
|
3171
|
+
emitDiagnostic({
|
|
3172
|
+
code: "ON_FLUSH_IN_OWNED_SCOPE",
|
|
3173
|
+
kind: "lifecycle",
|
|
3174
|
+
severity: "warn",
|
|
3175
|
+
message,
|
|
3176
|
+
ownerId: owner.id,
|
|
3177
|
+
ownerName: owner._name
|
|
3178
|
+
})
|
|
3179
|
+
);
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
// The frame's flush is queued (or already draining) ahead of this
|
|
3183
|
+
// microtask, so the callback's writes start a flush of their own.
|
|
3184
|
+
schedule();
|
|
3185
|
+
queueMicrotask(callback);
|
|
3186
|
+
}
|
|
3089
3187
|
|
|
3090
3188
|
function mapArray(list, map, options) {
|
|
3091
3189
|
const keyFn = typeof options?.keyed === "function" ? options.keyed : undefined;
|
|
@@ -3714,6 +3812,9 @@ function compare(key, a, b) {
|
|
|
3714
3812
|
return key ? key(a) === key(b) : true;
|
|
3715
3813
|
}
|
|
3716
3814
|
|
|
3815
|
+
// Set before any family is optimistic (`fam.opt`): a call site checks the
|
|
3816
|
+
// hooks first, so a build that does not import optimistic.ts folds the arm
|
|
3817
|
+
// away.
|
|
3717
3818
|
let optHooks = null;
|
|
3718
3819
|
function installOptHooks(hooks) {
|
|
3719
3820
|
optHooks = hooks;
|
|
@@ -3835,7 +3936,7 @@ function wrap(value, parent = null, parentKey = null, fam = parent?.fam ?? null)
|
|
|
3835
3936
|
const existing = lookupTarget(value, fam);
|
|
3836
3937
|
if (existing !== undefined) return existing.px;
|
|
3837
3938
|
const t = value[$TARGET];
|
|
3838
|
-
if (t
|
|
3939
|
+
if (t?.px === value) {
|
|
3839
3940
|
// Foreign-family proxies re-wrap into THIS family (writes stay isolated);
|
|
3840
3941
|
// same-family and plain-store proxies pass through.
|
|
3841
3942
|
if (fam === null || t.fam === fam) return value;
|
|
@@ -3907,7 +4008,7 @@ function asBacking(v) {
|
|
|
3907
4008
|
* pending backing is never "equal" to the committed one); a lane value is
|
|
3908
4009
|
* an arrangement (`LaneView`), judged by the optimistic machinery. */
|
|
3909
4010
|
const containerEquals = function (a, b) {
|
|
3910
|
-
return a === b ||
|
|
4011
|
+
return a === b || !!optHooks?.arrangement(this._host, a, b);
|
|
3911
4012
|
};
|
|
3912
4013
|
function sameLogicalSlot(target, a, b) {
|
|
3913
4014
|
if (a === null || typeof a !== "object" || b === null || typeof b !== "object") return false;
|
|
@@ -3924,10 +4025,10 @@ function releaseSlot(node) {
|
|
|
3924
4025
|
if (t.k === node) t.k = null;
|
|
3925
4026
|
} else if (key === $DEEP) {
|
|
3926
4027
|
if (t.dk === node) t.dk = null;
|
|
3927
|
-
} else if (t.n
|
|
4028
|
+
} else if (t.n?.[key] === node) {
|
|
3928
4029
|
delete t.n[key];
|
|
3929
4030
|
t.nc--;
|
|
3930
|
-
} else if (t.h
|
|
4031
|
+
} else if (t.h?.[key] === node) delete t.h[key];
|
|
3931
4032
|
// The family's index keeps only targets that still carry a node.
|
|
3932
4033
|
if (
|
|
3933
4034
|
t.fam !== null &&
|
|
@@ -4008,12 +4109,7 @@ function getNode(
|
|
|
4008
4109
|
accKnown === -1 ? isOwnAccessor(pb ?? target.v, key) : accKnown === 1
|
|
4009
4110
|
));
|
|
4010
4111
|
if (pb !== null && !created.acc)
|
|
4011
|
-
bornStaged(
|
|
4012
|
-
target,
|
|
4013
|
-
created,
|
|
4014
|
-
target.del !== null && target.del.has(key) ? undefined : pb[key],
|
|
4015
|
-
key
|
|
4016
|
-
);
|
|
4112
|
+
bornStaged(target, created, target.del?.has(key) ? undefined : pb[key], key);
|
|
4017
4113
|
// Attribution-only: name store property nodes by path segment so
|
|
4018
4114
|
// attribution chains and wide-scope warnings read "store.todos" (or
|
|
4019
4115
|
// "todos.title" when the store was declared with a name), not
|
|
@@ -4038,8 +4134,7 @@ function getHasNode(target, key) {
|
|
|
4038
4134
|
if (node === undefined) {
|
|
4039
4135
|
const created = (node = slotSignal(key in committed(target), isEqual, target, key, false));
|
|
4040
4136
|
const pb = target.pb;
|
|
4041
|
-
if (pb !== null)
|
|
4042
|
-
bornStaged(target, created, key in pb && !(target.del !== null && target.del.has(key)), key);
|
|
4137
|
+
if (pb !== null) bornStaged(target, created, key in pb && !target.del?.has(key), key);
|
|
4043
4138
|
if (attrHooks !== null) stampNodeOwner(created, target);
|
|
4044
4139
|
nodes[key] = node;
|
|
4045
4140
|
noteNode(target, created, key);
|
|
@@ -4213,7 +4308,7 @@ function scanAccessorsOnce(target) {
|
|
|
4213
4308
|
const keys = Reflect.ownKeys(src);
|
|
4214
4309
|
let plain = Object.getPrototypeOf(src) === Object.prototype;
|
|
4215
4310
|
for (const key of keys) {
|
|
4216
|
-
if (
|
|
4311
|
+
if (isOwnAccessor(src, key)) {
|
|
4217
4312
|
target.a = true;
|
|
4218
4313
|
plain = false;
|
|
4219
4314
|
break;
|
|
@@ -4238,7 +4333,7 @@ const plainProto = o => {
|
|
|
4238
4333
|
* container at flatten so its shape stays fast (#3689). */
|
|
4239
4334
|
function overlayRebuilds(t, pb) {
|
|
4240
4335
|
if (t.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
|
|
4241
|
-
if (t.del
|
|
4336
|
+
if (t.del?.size) return true;
|
|
4242
4337
|
const wk = t.wk;
|
|
4243
4338
|
if (wk !== null && wk !== WK_ALL && wk.u === undefined && wk.size <= OVERLAY_REBUILD_MIN_ADDS)
|
|
4244
4339
|
return false;
|
|
@@ -4252,18 +4347,14 @@ function overlayRebuilds(t, pb) {
|
|
|
4252
4347
|
function materializePB(target) {
|
|
4253
4348
|
const pb = target.pb;
|
|
4254
4349
|
if (!target.ovl) return pb;
|
|
4255
|
-
const clone =
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
}
|
|
4261
|
-
for (const key of Reflect.ownKeys(pb))
|
|
4262
|
-
target.sc === 2 ? (clone[key] = pb[key]) : copyOwn(clone, pb, key);
|
|
4350
|
+
const clone = overlayOnto(
|
|
4351
|
+
target,
|
|
4352
|
+
target.sc === 2 ? wideClone(target.v, target) : cloneRaw(target.v, target),
|
|
4353
|
+
pb
|
|
4354
|
+
);
|
|
4263
4355
|
target.pb = clone;
|
|
4264
|
-
target.ovl = false;
|
|
4265
4356
|
// The staging IS the pending backing: re-point the container node.
|
|
4266
|
-
if (target.k
|
|
4357
|
+
if (target.k?._pendingValue === pb) target.k._pendingValue = clone;
|
|
4267
4358
|
return clone;
|
|
4268
4359
|
}
|
|
4269
4360
|
/** The batch's pending backing, created on the first draft write: an overlay
|
|
@@ -4275,7 +4366,7 @@ function ensurePB(target) {
|
|
|
4275
4366
|
// An optimistic edit composes on the view the user saw and becomes
|
|
4276
4367
|
// guesses at the setter's exit: nothing is staged, nothing folds
|
|
4277
4368
|
// (optimistic.ts).
|
|
4278
|
-
if (userWrite && target.fam?.opt === true && !pendingNotify.has(target))
|
|
4369
|
+
if (optHooks && userWrite && target.fam?.opt === true && !pendingNotify.has(target))
|
|
4279
4370
|
return (target.pb = optHooks.draft(target));
|
|
4280
4371
|
if (pb !== null && !isOwned(pb)) {
|
|
4281
4372
|
// An adopted object awaiting its fold is the user's: the draft writes a
|
|
@@ -4315,7 +4406,6 @@ function privatizeCommitted(target) {
|
|
|
4315
4406
|
target.ch = false;
|
|
4316
4407
|
if (target.u) {
|
|
4317
4408
|
privatizeCommitted(target.u);
|
|
4318
|
-
devAssertNeverUserMutation(target.u.v);
|
|
4319
4409
|
const pv = target.u.v;
|
|
4320
4410
|
const slot = parentSlotKey(target, before);
|
|
4321
4411
|
if (pv[slot] === before) pv[slot] = target.v;
|
|
@@ -4339,16 +4429,19 @@ function parentSlotKey(target, expected) {
|
|
|
4339
4429
|
* place — the backing keeps its identity. */
|
|
4340
4430
|
function flattenOverlay(t, pb) {
|
|
4341
4431
|
privatizeCommitted(t);
|
|
4342
|
-
|
|
4343
|
-
|
|
4432
|
+
overlayOnto(t, t.v, pb); // the caller (`foldTarget`) closes the staging
|
|
4433
|
+
}
|
|
4434
|
+
/** An overlay's own keys and deletions onto `to` (the two are disjoint: a
|
|
4435
|
+
* write un-deletes its key); the overlay is closed. */
|
|
4436
|
+
function overlayOnto(t, to, pb) {
|
|
4437
|
+
for (const key of Reflect.ownKeys(pb)) t.sc === 2 ? (to[key] = pb[key]) : copyOwn(to, pb, key);
|
|
4344
4438
|
if (t.del !== null) {
|
|
4345
|
-
for (const key of t.del) delete
|
|
4439
|
+
for (const key of t.del) delete to[key];
|
|
4346
4440
|
t.kc -= t.del.size;
|
|
4347
4441
|
t.del = null;
|
|
4348
4442
|
}
|
|
4349
|
-
t.pb = null;
|
|
4350
4443
|
t.ovl = false;
|
|
4351
|
-
|
|
4444
|
+
return to;
|
|
4352
4445
|
}
|
|
4353
4446
|
/** Adoption (a setter's returned replacement; reconcile and projection
|
|
4354
4447
|
* landings in S3): the incoming object becomes the backing by reference at
|
|
@@ -4387,7 +4480,7 @@ function adoptPB(target, incoming, notify = true) {
|
|
|
4387
4480
|
target.a = false;
|
|
4388
4481
|
target.wk = null; // adoption supersedes staged trap writes
|
|
4389
4482
|
const owner = incoming[$OWNER];
|
|
4390
|
-
if (owner
|
|
4483
|
+
if (owner?.fam === target.fam) incoming[$OWNER] = target;
|
|
4391
4484
|
else setLookup(incoming, target);
|
|
4392
4485
|
if (notify && base !== incoming) notifyFold(target, base, incoming);
|
|
4393
4486
|
}
|
|
@@ -4484,7 +4577,7 @@ function drainFolds() {
|
|
|
4484
4577
|
* landing) read it. */
|
|
4485
4578
|
GlobalQueue._slotFlight = n => {
|
|
4486
4579
|
const fam = n._host?.fam;
|
|
4487
|
-
return
|
|
4580
|
+
return (fam?.node?._statusFlags & STATUS_PENDING) !== 0;
|
|
4488
4581
|
};
|
|
4489
4582
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
4490
4583
|
* A chained link's committed value is the inner store's at the last
|
|
@@ -4495,8 +4588,8 @@ GlobalQueue._slotCovered = n => {
|
|
|
4495
4588
|
if (t.ch) {
|
|
4496
4589
|
const key = n._key;
|
|
4497
4590
|
const inner = t.v;
|
|
4498
|
-
if (t.h
|
|
4499
|
-
else if (t.n
|
|
4591
|
+
if (t.h?.[key] === n) n._value = untrack(() => key in inner);
|
|
4592
|
+
else if (t.n?.[key] === n) n._value = untrack(() => unwrapValue(inner[key]));
|
|
4500
4593
|
}
|
|
4501
4594
|
return n._value;
|
|
4502
4595
|
};
|
|
@@ -4529,12 +4622,8 @@ function foldTarget(t, old) {
|
|
|
4529
4622
|
const k = t.k;
|
|
4530
4623
|
const pb = t.pb;
|
|
4531
4624
|
if (pb !== null) {
|
|
4532
|
-
if (pb === t.v)
|
|
4533
|
-
|
|
4534
|
-
// the adopted object (shared ownership — never cloned).
|
|
4535
|
-
t.pb = null;
|
|
4536
|
-
t.wk = null;
|
|
4537
|
-
} else if (
|
|
4625
|
+
if (pb === t.v);
|
|
4626
|
+
else if (
|
|
4538
4627
|
t.ovl &&
|
|
4539
4628
|
(t.v !== old || (!overlayRebuilds(t, pb) && k?._x?._snapshotValue !== old))
|
|
4540
4629
|
) {
|
|
@@ -4557,26 +4646,19 @@ function foldTarget(t, old) {
|
|
|
4557
4646
|
else delete v[key];
|
|
4558
4647
|
}
|
|
4559
4648
|
} else {
|
|
4560
|
-
for (const key of Reflect.ownKeys(pb))
|
|
4561
|
-
const d = Object.getOwnPropertyDescriptor(pb, key);
|
|
4562
|
-
if (d.get || d.set || !d.enumerable || !d.writable || !d.configurable)
|
|
4563
|
-
Object.defineProperty(v, key, d);
|
|
4564
|
-
else if (d.value !== old[key] || !hasOwn$1.call(old, key)) v[key] = d.value;
|
|
4565
|
-
}
|
|
4649
|
+
for (const key of Reflect.ownKeys(pb)) if (key !== $OWNER) copyOwn(v, pb, key);
|
|
4566
4650
|
for (const key of Reflect.ownKeys(old)) {
|
|
4567
|
-
if (!hasOwn$1.call(pb, key)) delete v[key];
|
|
4651
|
+
if (key !== $OWNER && !hasOwn$1.call(pb, key)) delete v[key];
|
|
4568
4652
|
}
|
|
4569
4653
|
}
|
|
4570
|
-
t.pb = null;
|
|
4571
|
-
t.wk = null;
|
|
4572
4654
|
} else {
|
|
4573
4655
|
// The clone (or adopted object) becomes the committed backing
|
|
4574
4656
|
// (path copy below).
|
|
4575
4657
|
t.v = t.ovl ? materializePB(t) : pb;
|
|
4576
4658
|
t.ch = t.v[$TARGET] !== undefined;
|
|
4577
|
-
t.pb = null;
|
|
4578
|
-
t.wk = null;
|
|
4579
4659
|
}
|
|
4660
|
+
t.pb = null;
|
|
4661
|
+
t.wk = null; // the written-keys window closes with the commit
|
|
4580
4662
|
}
|
|
4581
4663
|
if (k !== null) {
|
|
4582
4664
|
// The node's committed value is the backing. A container nobody
|
|
@@ -4591,7 +4673,6 @@ function foldTarget(t, old) {
|
|
|
4591
4673
|
const slot = parentSlotKey(t, old);
|
|
4592
4674
|
if (t.u.v[slot] === old) {
|
|
4593
4675
|
privatizeCommitted(t.u);
|
|
4594
|
-
devAssertNeverUserMutation(t.u.v);
|
|
4595
4676
|
t.u.v[slot] = t.v;
|
|
4596
4677
|
}
|
|
4597
4678
|
}
|
|
@@ -4617,9 +4698,12 @@ function reportReplacedContainers(t, old, pb, writtenKeys) {
|
|
|
4617
4698
|
const keys = writtenKeys ?? Reflect.ownKeys(pb);
|
|
4618
4699
|
const isArray = Array.isArray(pb);
|
|
4619
4700
|
const owner = storeRootOwner(t);
|
|
4701
|
+
// Items the array held before the write: an index that now holds one of
|
|
4702
|
+
// them was moved into place (an in-place splice / shift), not replaced.
|
|
4703
|
+
let oldArrayItems = null;
|
|
4620
4704
|
for (const key of keys) {
|
|
4621
4705
|
if ((isArray && key === "length") || key === $OWNER) continue;
|
|
4622
|
-
if (t.del
|
|
4706
|
+
if (t.del?.has(key)) continue;
|
|
4623
4707
|
const ov = unwrapValue(old[key]);
|
|
4624
4708
|
const nv = unwrapValue(pb[key]);
|
|
4625
4709
|
if (
|
|
@@ -4631,6 +4715,13 @@ function reportReplacedContainers(t, old, pb, writtenKeys) {
|
|
|
4631
4715
|
targetsEqual(ov, nv)
|
|
4632
4716
|
)
|
|
4633
4717
|
continue;
|
|
4718
|
+
if (isArray) {
|
|
4719
|
+
if (oldArrayItems === null) {
|
|
4720
|
+
oldArrayItems = new Set();
|
|
4721
|
+
for (const item of old) oldArrayItems.add(unwrapValue(item));
|
|
4722
|
+
}
|
|
4723
|
+
if (oldArrayItems.has(nv)) continue;
|
|
4724
|
+
}
|
|
4634
4725
|
const isArr = Array.isArray(nv);
|
|
4635
4726
|
if (isArr !== Array.isArray(ov)) continue;
|
|
4636
4727
|
let total;
|
|
@@ -4692,7 +4783,7 @@ function notifyWrites(t) {
|
|
|
4692
4783
|
// Optimistic channel: a user's writes on an optimistic family become
|
|
4693
4784
|
// guesses on the nodes (lanes); the draft is discarded, the committed
|
|
4694
4785
|
// backing never touched.
|
|
4695
|
-
if (userWrite && t.fam?.opt === true) {
|
|
4786
|
+
if (optHooks && userWrite && t.fam?.opt === true) {
|
|
4696
4787
|
// The draft is discarded first: the diff reads the target (its nodes
|
|
4697
4788
|
// are born from the committed frame, not the draft).
|
|
4698
4789
|
t.pb = null;
|
|
@@ -4767,7 +4858,7 @@ function notifyWrites(t) {
|
|
|
4767
4858
|
if (od?.get || od?.set || !isEqual(od?.value, nd.value)) setSignal(node, () => nd.value);
|
|
4768
4859
|
continue;
|
|
4769
4860
|
}
|
|
4770
|
-
const nv = t.del
|
|
4861
|
+
const nv = t.del?.has(key) ? undefined : pb[key];
|
|
4771
4862
|
setSignal(node, () => nv);
|
|
4772
4863
|
if (t.fam !== null) {
|
|
4773
4864
|
// The derive's write is a derivation; a user setter's is a proposal
|
|
@@ -4785,13 +4876,13 @@ function notifyWrites(t) {
|
|
|
4785
4876
|
for (const key of keys) {
|
|
4786
4877
|
const node = has[key];
|
|
4787
4878
|
if (node !== undefined) {
|
|
4788
|
-
setSignal(node, key in pb && !
|
|
4879
|
+
setSignal(node, key in pb && !t.del?.has(key));
|
|
4789
4880
|
if (t.fam !== null) holdWithDerive(t, node);
|
|
4790
4881
|
}
|
|
4791
4882
|
}
|
|
4792
4883
|
}
|
|
4793
4884
|
if (t.dk !== null) {
|
|
4794
|
-
if (t.del
|
|
4885
|
+
if (t.del?.size) bumpDeep(t);
|
|
4795
4886
|
else
|
|
4796
4887
|
for (const key of writtenKeys ?? Reflect.ownKeys(pb)) {
|
|
4797
4888
|
if (key === $OWNER) continue;
|
|
@@ -4811,7 +4902,7 @@ function notifyWrites(t) {
|
|
|
4811
4902
|
if (k !== null && (k._subs !== null || k._config & CONFIG_OVERRIDE)) {
|
|
4812
4903
|
let changed;
|
|
4813
4904
|
if (t.ovl) {
|
|
4814
|
-
changed = t.del
|
|
4905
|
+
changed = !!t.del?.size;
|
|
4815
4906
|
if (!changed) {
|
|
4816
4907
|
for (const key of Reflect.ownKeys(pb)) {
|
|
4817
4908
|
if (!hasOwn$1.call(old, key)) {
|
|
@@ -4966,7 +5057,7 @@ function serveShallow(target, key, v) {
|
|
|
4966
5057
|
function draftServe(target, proxy) {
|
|
4967
5058
|
if (writeScopes !== null && inDraft(target)) {
|
|
4968
5059
|
const ct = proxy?.[$TARGET];
|
|
4969
|
-
if (ct
|
|
5060
|
+
if (ct?.v !== undefined) writeScopes.add(scopeKey(ct));
|
|
4970
5061
|
}
|
|
4971
5062
|
return proxy;
|
|
4972
5063
|
}
|
|
@@ -5009,13 +5100,7 @@ function readSource(target, key, shape = false) {
|
|
|
5009
5100
|
// sees — the lanes' values and the tick's own guesses over the truth
|
|
5010
5101
|
// (#3665), never a staging awaiting its fold: the draft is born on the
|
|
5011
5102
|
// read (and notified at the setter's exit like any).
|
|
5012
|
-
if (
|
|
5013
|
-
userWrite &&
|
|
5014
|
-
target.fam !== null &&
|
|
5015
|
-
target.fam.opt === true &&
|
|
5016
|
-
!pendingNotify.has(target) &&
|
|
5017
|
-
inDraft(target)
|
|
5018
|
-
) {
|
|
5103
|
+
if (userWrite && target.fam?.opt === true && !pendingNotify.has(target) && inDraft(target)) {
|
|
5019
5104
|
const draft = ensurePB(target);
|
|
5020
5105
|
pendingNotify.add(target);
|
|
5021
5106
|
return draft;
|
|
@@ -5054,7 +5139,7 @@ function readSource(target, key, shape = false) {
|
|
|
5054
5139
|
// it).
|
|
5055
5140
|
if (key !== undefined) {
|
|
5056
5141
|
const leaf = target.n?.[key];
|
|
5057
|
-
if (leaf
|
|
5142
|
+
if (leaf?._config & CONFIG_OVERRIDE) return committed(target);
|
|
5058
5143
|
}
|
|
5059
5144
|
const held = k !== null && (k._config & CONFIG_HELD) !== 0;
|
|
5060
5145
|
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
@@ -5168,18 +5253,14 @@ function adoptChild(target, key) {
|
|
|
5168
5253
|
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
5169
5254
|
// is a fresh target around `ov`.
|
|
5170
5255
|
const ct = wrap(ov, target, key)[$TARGET];
|
|
5171
|
-
if (ct
|
|
5256
|
+
if (ct?.v !== ov) return false;
|
|
5172
5257
|
adoptPB(ct, nv, false);
|
|
5173
5258
|
holdNode(getContainerNode(ct), txOf(k));
|
|
5174
5259
|
return true;
|
|
5175
5260
|
}
|
|
5176
5261
|
/** A family target's unheld staging, read with no pass (a handler). */
|
|
5177
5262
|
function familyAhead(target) {
|
|
5178
|
-
return (
|
|
5179
|
-
target.fam !== null &&
|
|
5180
|
-
context === null &&
|
|
5181
|
-
!(target.k !== null && target.k._config & CONFIG_HELD)
|
|
5182
|
-
);
|
|
5263
|
+
return target.fam !== null && context === null && !(target.k?._config & CONFIG_HELD);
|
|
5183
5264
|
}
|
|
5184
5265
|
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
5185
5266
|
* a `shape` reader — a descriptor read, a node's hold) enumerability or
|
|
@@ -5204,7 +5285,7 @@ function changedBetween(target, v, pb, key, shape) {
|
|
|
5204
5285
|
!plainProto(v)
|
|
5205
5286
|
)
|
|
5206
5287
|
return true;
|
|
5207
|
-
if (target.del
|
|
5288
|
+
if (target.del?.has(key)) return key in v;
|
|
5208
5289
|
const inNew = hasOwn$1.call(pb, key) || (target.ovl && hasOwn$1.call(target.v, key));
|
|
5209
5290
|
if (inNew !== hasOwn$1.call(v, key)) return true;
|
|
5210
5291
|
if (!inNew) return false;
|
|
@@ -5376,16 +5457,16 @@ function enumerationSource(target) {
|
|
|
5376
5457
|
// A container's arrangement guess is not a backing: the view composes
|
|
5377
5458
|
// over the base it is shown over.
|
|
5378
5459
|
const base = asBacking(read(getContainerNode(target)));
|
|
5379
|
-
return target.fam?.opt === true ? optHooks.view(target, base) : base;
|
|
5460
|
+
return optHooks && target.fam?.opt === true ? optHooks.view(target, base) : base;
|
|
5380
5461
|
}
|
|
5381
5462
|
const src = readSource(target);
|
|
5382
|
-
return optRead(target) ? optHooks.view(target, src) : src;
|
|
5463
|
+
return optHooks && optRead(target) ? optHooks.view(target, src) : src;
|
|
5383
5464
|
}
|
|
5384
5465
|
/** A read on an optimistic family that composes the lanes' values: not the
|
|
5385
5466
|
* draft's (it composed already), not a derive's write-override read (the
|
|
5386
5467
|
* derive composes on the truth). */
|
|
5387
5468
|
function optRead(target) {
|
|
5388
|
-
return target.fam
|
|
5469
|
+
return target.fam?.opt === true && !inDraft(target) && !writeOverride;
|
|
5389
5470
|
}
|
|
5390
5471
|
/** Pollution keys are never served from the prototype (core R30). */
|
|
5391
5472
|
const UNSAFE_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
@@ -5511,7 +5592,7 @@ const traps = {
|
|
|
5511
5592
|
const src = readSource(target, key);
|
|
5512
5593
|
// Overlay delete (#3044): a prototype overlay cannot shadow a delete, so
|
|
5513
5594
|
// deleted keys are tracked aside and read as absent in the pending view.
|
|
5514
|
-
if (
|
|
5595
|
+
if (src === target.pb && target.del?.has(key)) {
|
|
5515
5596
|
if (!inDraft(target) && getObserver() !== null) read(getNode(target, key));
|
|
5516
5597
|
return undefined;
|
|
5517
5598
|
}
|
|
@@ -5539,7 +5620,8 @@ const traps = {
|
|
|
5539
5620
|
!inDraft(target) &&
|
|
5540
5621
|
typeof key === "string" &&
|
|
5541
5622
|
key !== "then" &&
|
|
5542
|
-
getObserver() === null
|
|
5623
|
+
getObserver() === null &&
|
|
5624
|
+
strictReadActive()
|
|
5543
5625
|
) {
|
|
5544
5626
|
warnStrictReadUntracked(strictRead, {
|
|
5545
5627
|
nodeName: key,
|
|
@@ -5638,12 +5720,12 @@ const traps = {
|
|
|
5638
5720
|
node._value = present; // the link follows the inner store (S4)
|
|
5639
5721
|
if (node._config & CONFIG_OVERRIDE) return !!nv;
|
|
5640
5722
|
return present;
|
|
5641
|
-
} else if (optRead(target)) {
|
|
5723
|
+
} else if (optHooks && optRead(target)) {
|
|
5642
5724
|
const guessed = optHooks.has(target, key);
|
|
5643
5725
|
if (guessed !== undefined) return guessed;
|
|
5644
5726
|
}
|
|
5645
5727
|
}
|
|
5646
|
-
return key in src && !(
|
|
5728
|
+
return key in src && !(src === target.pb && target.del?.has(key));
|
|
5647
5729
|
},
|
|
5648
5730
|
ownKeys(target) {
|
|
5649
5731
|
if (verdict !== null && affectsHooks !== null && getObserver() === null)
|
|
@@ -5678,7 +5760,7 @@ const traps = {
|
|
|
5678
5760
|
return undefined;
|
|
5679
5761
|
} else src = readSource(target, key, true);
|
|
5680
5762
|
let desc;
|
|
5681
|
-
if (!enumerating && optRead(target)) {
|
|
5763
|
+
if (optHooks && !enumerating && optRead(target)) {
|
|
5682
5764
|
const guessed = optHooks.descriptor(target, key);
|
|
5683
5765
|
if (guessed === null) return undefined;
|
|
5684
5766
|
desc = guessed;
|
|
@@ -5690,8 +5772,7 @@ const traps = {
|
|
|
5690
5772
|
},
|
|
5691
5773
|
set(target, key, value) {
|
|
5692
5774
|
const draft = inDraft(target);
|
|
5693
|
-
|
|
5694
|
-
if (!draft && !override) {
|
|
5775
|
+
if (!draft && !writeOverride) {
|
|
5695
5776
|
warnWriteOutsideSetter(target, key, "set");
|
|
5696
5777
|
return true;
|
|
5697
5778
|
}
|
|
@@ -5717,17 +5798,8 @@ const traps = {
|
|
|
5717
5798
|
touch(target, key);
|
|
5718
5799
|
if (!(key in pb)) target.kc++;
|
|
5719
5800
|
}
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
value: uv,
|
|
5723
|
-
writable: true,
|
|
5724
|
-
enumerable: true,
|
|
5725
|
-
configurable: true
|
|
5726
|
-
});
|
|
5727
|
-
if (target.del !== null) target.del.delete(key);
|
|
5728
|
-
return true;
|
|
5729
|
-
}
|
|
5730
|
-
if (target.ovl && target.sc !== 2 && !hasOwn$1.call(pb, key)) {
|
|
5801
|
+
const unsafe = UNSAFE_KEYS.has(key);
|
|
5802
|
+
if (unsafe || (target.ovl && target.sc !== 2 && !hasOwn$1.call(pb, key))) {
|
|
5731
5803
|
Object.defineProperty(pb, key, {
|
|
5732
5804
|
value: uv,
|
|
5733
5805
|
writable: true,
|
|
@@ -5735,15 +5807,15 @@ const traps = {
|
|
|
5735
5807
|
configurable: true
|
|
5736
5808
|
});
|
|
5737
5809
|
} else pb[key] = uv;
|
|
5738
|
-
|
|
5810
|
+
target.del?.delete(key);
|
|
5811
|
+
if (unsafe) return true;
|
|
5739
5812
|
if (target.s && uv !== null && typeof uv === "object") markRawOne(uv);
|
|
5740
|
-
if (
|
|
5813
|
+
if (!draft) notifyWrites(target); // a write-override write
|
|
5741
5814
|
return true;
|
|
5742
5815
|
},
|
|
5743
5816
|
defineProperty(target, key, desc) {
|
|
5744
5817
|
const draft = inDraft(target);
|
|
5745
|
-
|
|
5746
|
-
if (!draft && !override) {
|
|
5818
|
+
if (!draft && !writeOverride) {
|
|
5747
5819
|
warnWriteOutsideSetter(target, key, "defineProperty");
|
|
5748
5820
|
return true;
|
|
5749
5821
|
}
|
|
@@ -5754,14 +5826,13 @@ const traps = {
|
|
|
5754
5826
|
if (target.a || !(desc.enumerable && desc.writable && desc.configurable)) target.sc = 1;
|
|
5755
5827
|
touch(target, key);
|
|
5756
5828
|
Object.defineProperty(pb, key, desc);
|
|
5757
|
-
|
|
5758
|
-
if (
|
|
5829
|
+
target.del?.delete(key);
|
|
5830
|
+
if (!draft) notifyWrites(target); // a write-override write
|
|
5759
5831
|
return true;
|
|
5760
5832
|
},
|
|
5761
5833
|
deleteProperty(target, key) {
|
|
5762
5834
|
const draft = inDraft(target);
|
|
5763
|
-
|
|
5764
|
-
if (!draft && !override) {
|
|
5835
|
+
if (!draft && !writeOverride) {
|
|
5765
5836
|
warnWriteOutsideSetter(target, key, "deleteProperty");
|
|
5766
5837
|
return true;
|
|
5767
5838
|
}
|
|
@@ -5782,7 +5853,7 @@ const traps = {
|
|
|
5782
5853
|
committed || target.kc--;
|
|
5783
5854
|
}
|
|
5784
5855
|
if (committed) (target.del ??= new Set()).add(key);
|
|
5785
|
-
if (
|
|
5856
|
+
if (!draft) notifyWrites(target); // a write-override write
|
|
5786
5857
|
return true;
|
|
5787
5858
|
}
|
|
5788
5859
|
};
|
|
@@ -5883,7 +5954,7 @@ function storeSetter(proxy, fn, guard = true) {
|
|
|
5883
5954
|
// A returned replacement: on an optimistic family a user's is itself an
|
|
5884
5955
|
// optimistic edit — diffed against the view as guesses (`next` parity);
|
|
5885
5956
|
// otherwise it is adopted as the incoming truth.
|
|
5886
|
-
if (guard && target.fam?.opt === true && !writeOverride)
|
|
5957
|
+
if (optHooks && guard && target.fam?.opt === true && !writeOverride)
|
|
5887
5958
|
optHooks.writes(target, unwrapValue(result));
|
|
5888
5959
|
else adoptPB(target, unwrapValue(result));
|
|
5889
5960
|
}
|
|
@@ -5943,17 +6014,17 @@ function createStore$1(initialValue, options) {
|
|
|
5943
6014
|
* slot-patch channel (collected row bodies) over per-record registration. */
|
|
5944
6015
|
function storeIsShallow(proxy) {
|
|
5945
6016
|
const t = proxy?.[$TARGET];
|
|
5946
|
-
return t
|
|
6017
|
+
return t?.s === true;
|
|
5947
6018
|
}
|
|
5948
6019
|
/** True when `proxy` belongs to a projection family (S3). */
|
|
5949
6020
|
function storeHasFamily(proxy) {
|
|
5950
6021
|
const t = proxy?.[$TARGET];
|
|
5951
|
-
return t
|
|
6022
|
+
return t?.fam != null;
|
|
5952
6023
|
}
|
|
5953
6024
|
/** True when `proxy` belongs to an OPTIMISTIC family (S4). */
|
|
5954
6025
|
function storeHasOptimisticFamily(proxy) {
|
|
5955
6026
|
const t = proxy?.[$TARGET];
|
|
5956
|
-
return t
|
|
6027
|
+
return t?.fam?.opt === true;
|
|
5957
6028
|
}
|
|
5958
6029
|
// ---------------------------------------------------------------------------
|
|
5959
6030
|
// enumeration views (one visibility rule for the traps AND the deep walk)
|
|
@@ -5962,7 +6033,7 @@ function visibleKeys(target, src) {
|
|
|
5962
6033
|
if (target.ovl && src === target.pb) {
|
|
5963
6034
|
keys = Reflect.ownKeys(target.v);
|
|
5964
6035
|
const del = target.del;
|
|
5965
|
-
if (del
|
|
6036
|
+
if (del?.size) keys = keys.filter(key => !del.has(key));
|
|
5966
6037
|
for (const key of Reflect.ownKeys(src)) {
|
|
5967
6038
|
if (!hasOwn$1.call(target.v, key)) keys.push(key);
|
|
5968
6039
|
}
|
|
@@ -5979,7 +6050,7 @@ function visibleKeys(target, src) {
|
|
|
5979
6050
|
function visibleDescriptor(target, src, key) {
|
|
5980
6051
|
let desc = Object.getOwnPropertyDescriptor(src, key);
|
|
5981
6052
|
if (target.ovl && src === target.pb) {
|
|
5982
|
-
if (target.del
|
|
6053
|
+
if (target.del?.has(key)) return undefined;
|
|
5983
6054
|
if (desc === undefined) desc = Object.getOwnPropertyDescriptor(target.v, key);
|
|
5984
6055
|
}
|
|
5985
6056
|
return desc;
|
|
@@ -5990,7 +6061,7 @@ function visibleDescriptor(target, src, key) {
|
|
|
5990
6061
|
* witness (one node per record, not one per path), then snapshots. */
|
|
5991
6062
|
function deep$1(value) {
|
|
5992
6063
|
const t0 = value?.[$TARGET];
|
|
5993
|
-
if (t0
|
|
6064
|
+
if (t0?.px !== value) return value;
|
|
5994
6065
|
const visited = new Set();
|
|
5995
6066
|
const childTarget = (t, child, key) => {
|
|
5996
6067
|
let ct = lookupTarget(child, t.fam);
|
|
@@ -6063,7 +6134,7 @@ function snapshotWalk(value, seen, fam) {
|
|
|
6063
6134
|
if (t.ovl) materializePB(t);
|
|
6064
6135
|
backing = t.pb;
|
|
6065
6136
|
}
|
|
6066
|
-
if (optRead(t)) backing = optHooks.view(t, backing);
|
|
6137
|
+
if (optHooks && optRead(t)) backing = optHooks.view(t, backing);
|
|
6067
6138
|
if (backing === src) break;
|
|
6068
6139
|
src = backing;
|
|
6069
6140
|
}
|
|
@@ -6232,14 +6303,14 @@ const storePath = /* @__PURE__ */ Object.assign(
|
|
|
6232
6303
|
*/
|
|
6233
6304
|
function reconcileState(value, state, key, replace = false) {
|
|
6234
6305
|
if (state == null) throw new Error("Cannot reconcile null or undefined state");
|
|
6235
|
-
const t = state
|
|
6236
|
-
if (t
|
|
6306
|
+
const t = state[$TARGET];
|
|
6307
|
+
if (t?.px !== state) throw new Error("reconcile target is not a store proxy");
|
|
6237
6308
|
const keyFn = key === null ? null : typeof key === "string" ? item => item?.[key] : key;
|
|
6238
6309
|
// A user's reconcile on an optimistic family (S4): not an adoption of
|
|
6239
6310
|
// truth but an optimistic edit — the keyed diff is written into the draft
|
|
6240
6311
|
// (a matched row keeps its proxy, its changed leaves become its guesses),
|
|
6241
6312
|
// and the setter's exit turns the draft into guesses like any.
|
|
6242
|
-
if (userWriting() && t.fam?.opt === true) {
|
|
6313
|
+
if (optHooks && userWriting() && t.fam?.opt === true) {
|
|
6243
6314
|
optHooks.reconcile(state, unwrapValue(value), keyFn);
|
|
6244
6315
|
return;
|
|
6245
6316
|
}
|
|
@@ -6258,9 +6329,8 @@ function reconcileState(value, state, key, replace = false) {
|
|
|
6258
6329
|
if (!replace) throw new Error("Cannot reconcile states with different identity");
|
|
6259
6330
|
// A different entity at the root: the old backing is disowned (wraps
|
|
6260
6331
|
// fresh if re-handed) and the new one adopted whole.
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
else (t.fam?.map ?? storeLookup).delete(out);
|
|
6332
|
+
if (isOwned(prev)) delete prev[$OWNER];
|
|
6333
|
+
else (t.fam?.map ?? storeLookup).delete(prev);
|
|
6264
6334
|
adoptPB(t, incoming);
|
|
6265
6335
|
return;
|
|
6266
6336
|
}
|
|
@@ -6276,50 +6346,42 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6276
6346
|
// as present through its prototype; materialized first — `adoptPB` does
|
|
6277
6347
|
// so anyway, after the base was taken — so a deletion the draft made is
|
|
6278
6348
|
// a deletion here (#3743: the presence and value diffs both read `old`).
|
|
6279
|
-
const
|
|
6280
|
-
if (incoming ===
|
|
6281
|
-
const fam = t.fam;
|
|
6349
|
+
const old = t.ovl ? materializePB(t) : (t.pb ?? t.v);
|
|
6350
|
+
if (incoming === old && !isOwned(old)) return;
|
|
6282
6351
|
// Q-D: a landing on an optimistic family is reconciled against the VIEW
|
|
6283
6352
|
// — key matching from the arrangement the guesses made (R28), so a row
|
|
6284
6353
|
// the guess added keeps its proxy when the server returns it.
|
|
6285
|
-
const prevView = fam?.opt === true ? optHooks.view(t,
|
|
6354
|
+
const prevView = optHooks && t.fam?.opt === true ? optHooks.view(t, old) : old;
|
|
6286
6355
|
const nextArr = Array.isArray(incoming);
|
|
6287
6356
|
const shallow = t.s === true;
|
|
6288
|
-
const old = prev;
|
|
6289
6357
|
adoptPB(t, incoming, false);
|
|
6290
6358
|
if (shallow) markRawIngest(incoming);
|
|
6359
|
+
// The deep witness is bumped once per adoption (none to bump: done).
|
|
6360
|
+
let bumped = t.dk === null;
|
|
6291
6361
|
if (Array.isArray(prevView) !== nextArr) {
|
|
6292
6362
|
notifyFold(t, old, incoming);
|
|
6293
6363
|
return;
|
|
6294
6364
|
}
|
|
6295
6365
|
if (nextArr) {
|
|
6296
|
-
const prevRows = prevView;
|
|
6297
|
-
const nextRows = incoming;
|
|
6298
6366
|
const nodes = t.n;
|
|
6299
6367
|
let nodesHit = 0;
|
|
6300
6368
|
if (keyFn && !shallow) {
|
|
6301
|
-
const plen =
|
|
6302
|
-
const nlen =
|
|
6303
|
-
let dkBumpedA = false;
|
|
6369
|
+
const plen = prevView.length;
|
|
6370
|
+
const nlen = incoming.length;
|
|
6304
6371
|
let i = 0;
|
|
6305
6372
|
// Aligned prefix: same row (by reference or key) at the same index.
|
|
6306
6373
|
for (const end = Math.min(plen, nlen); i < end; i++) {
|
|
6307
|
-
const nv =
|
|
6308
|
-
const pvRaw =
|
|
6374
|
+
const nv = incoming[i];
|
|
6375
|
+
const pvRaw = prevView[i];
|
|
6309
6376
|
if (pvRaw !== nv && !sameRow$1(pvRaw, nv, keyFn)) break; // misaligned: fall to the keyed remainder below
|
|
6377
|
+
if (nv !== null && typeof nv === "object" && (pvRaw !== nv || isOwned(nv)))
|
|
6378
|
+
descend(unwrapValue(pvRaw), nv, keyFn, proj, t, i);
|
|
6310
6379
|
if (
|
|
6311
|
-
|
|
6312
|
-
nv !== null &&
|
|
6313
|
-
typeof nv === "object"
|
|
6314
|
-
)
|
|
6315
|
-
descend(unwrapValue(pvRaw), nv, keyFn, fam, proj);
|
|
6316
|
-
if (
|
|
6317
|
-
t.dk !== null &&
|
|
6318
|
-
!dkBumpedA &&
|
|
6380
|
+
!bumped &&
|
|
6319
6381
|
!(nv !== null && typeof nv === "object" ? targetsEqual(pvRaw, nv) : isEqual(pvRaw, nv))
|
|
6320
6382
|
) {
|
|
6321
6383
|
bumpDeep(t);
|
|
6322
|
-
|
|
6384
|
+
bumped = true;
|
|
6323
6385
|
}
|
|
6324
6386
|
if (nodes !== null) {
|
|
6325
6387
|
const node = nodes[i];
|
|
@@ -6329,19 +6391,19 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6329
6391
|
}
|
|
6330
6392
|
}
|
|
6331
6393
|
}
|
|
6332
|
-
if (
|
|
6394
|
+
if (!bumped && i < incoming.length) bumpDeep(t);
|
|
6333
6395
|
const structStart = i; // misalignment point (== nlen on aligned ticks)
|
|
6334
6396
|
let prevByKey = null;
|
|
6335
|
-
for (; i <
|
|
6336
|
-
const nv =
|
|
6397
|
+
for (; i < incoming.length; i++) {
|
|
6398
|
+
const nv = incoming[i];
|
|
6337
6399
|
if (nv !== null && typeof nv === "object") {
|
|
6338
6400
|
const nk = keyFn(nv);
|
|
6339
6401
|
let pv;
|
|
6340
6402
|
if (nk !== undefined) {
|
|
6341
6403
|
if (prevByKey === null) {
|
|
6342
6404
|
prevByKey = new Map();
|
|
6343
|
-
for (let j = structStart; j <
|
|
6344
|
-
const p = unwrapValue(
|
|
6405
|
+
for (let j = structStart; j < prevView.length; j++) {
|
|
6406
|
+
const p = unwrapValue(prevView[j]);
|
|
6345
6407
|
if (p !== null && typeof p === "object") {
|
|
6346
6408
|
const pk = keyFn(p);
|
|
6347
6409
|
if (pk === undefined) continue;
|
|
@@ -6355,16 +6417,16 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6355
6417
|
const m = prevByKey.get(nk);
|
|
6356
6418
|
if (m === undefined) pv = undefined;
|
|
6357
6419
|
else if (Array.isArray(m)) {
|
|
6358
|
-
pv = unwrapValue(
|
|
6420
|
+
pv = unwrapValue(prevView[m.shift()]);
|
|
6359
6421
|
if (m.length === 1) prevByKey.set(nk, m[0]);
|
|
6360
6422
|
} else {
|
|
6361
|
-
pv = unwrapValue(
|
|
6423
|
+
pv = unwrapValue(prevView[m]);
|
|
6362
6424
|
prevByKey.delete(nk);
|
|
6363
6425
|
}
|
|
6364
6426
|
} else {
|
|
6365
|
-
pv = unwrapValue(
|
|
6427
|
+
pv = unwrapValue(prevView[i]); // keyless item: positional fallback
|
|
6366
6428
|
}
|
|
6367
|
-
descend(pv, nv, keyFn,
|
|
6429
|
+
descend(pv, nv, keyFn, proj, t, i);
|
|
6368
6430
|
}
|
|
6369
6431
|
if (nodes !== null) {
|
|
6370
6432
|
const node = nodes[i];
|
|
@@ -6375,22 +6437,20 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6375
6437
|
}
|
|
6376
6438
|
}
|
|
6377
6439
|
} else {
|
|
6378
|
-
const dlen = Math.min(
|
|
6379
|
-
const nlen =
|
|
6380
|
-
let dkBumpedP = false;
|
|
6440
|
+
const dlen = Math.min(prevView.length, incoming.length);
|
|
6441
|
+
const nlen = incoming.length;
|
|
6381
6442
|
for (let i = 0; i < nlen; i++) {
|
|
6382
|
-
const nvP =
|
|
6443
|
+
const nvP = incoming[i];
|
|
6383
6444
|
if (!shallow && i < dlen && nvP !== null && typeof nvP === "object")
|
|
6384
|
-
descend(unwrapValue(
|
|
6445
|
+
descend(unwrapValue(prevView[i]), nvP, keyFn, proj, t, i);
|
|
6385
6446
|
if (
|
|
6386
|
-
|
|
6387
|
-
!dkBumpedP &&
|
|
6447
|
+
!bumped &&
|
|
6388
6448
|
!(nvP !== null && typeof nvP === "object"
|
|
6389
|
-
? targetsEqual(
|
|
6390
|
-
: isEqual(
|
|
6449
|
+
? targetsEqual(prevView[i], nvP)
|
|
6450
|
+
: isEqual(prevView[i], nvP))
|
|
6391
6451
|
) {
|
|
6392
6452
|
bumpDeep(t);
|
|
6393
|
-
|
|
6453
|
+
bumped = true;
|
|
6394
6454
|
}
|
|
6395
6455
|
if (nodes !== null) {
|
|
6396
6456
|
const node = nodes[i];
|
|
@@ -6405,7 +6465,7 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6405
6465
|
if (nodes !== null && nodesHit < t.nc) {
|
|
6406
6466
|
for (const key of Reflect.ownKeys(nodes)) {
|
|
6407
6467
|
const idx = typeof key === "string" ? +key : NaN;
|
|
6408
|
-
if (!(idx >= 0 && idx <
|
|
6468
|
+
if (!(idx >= 0 && idx < incoming.length))
|
|
6409
6469
|
notifyKeyDiff(nodes[key], key, old, incoming, false);
|
|
6410
6470
|
}
|
|
6411
6471
|
}
|
|
@@ -6414,7 +6474,6 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6414
6474
|
}
|
|
6415
6475
|
const nodes = t.n;
|
|
6416
6476
|
let nodesHit = 0;
|
|
6417
|
-
let dkBumped = false;
|
|
6418
6477
|
for (const k in incoming) {
|
|
6419
6478
|
const nv = incoming[k];
|
|
6420
6479
|
const ov = old[k];
|
|
@@ -6424,13 +6483,13 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6424
6483
|
(!isObj || !isOwned(nv)) &&
|
|
6425
6484
|
(nodes === null || nodes[k] === undefined || !hasAccessorFlag(nodes[k]))
|
|
6426
6485
|
) {
|
|
6427
|
-
if (nodes
|
|
6486
|
+
if (nodes?.[k] !== undefined) nodesHit++;
|
|
6428
6487
|
continue;
|
|
6429
6488
|
}
|
|
6430
|
-
if (isObj && !shallow) descend(unwrapValue(prevView[k]), nv, keyFn,
|
|
6431
|
-
if (
|
|
6489
|
+
if (isObj && !shallow) descend(unwrapValue(prevView[k]), nv, keyFn, proj, t, k);
|
|
6490
|
+
if (!bumped && !(isObj ? targetsEqual(ov, nv) : isEqual(ov, nv))) {
|
|
6432
6491
|
bumpDeep(t);
|
|
6433
|
-
|
|
6492
|
+
bumped = true;
|
|
6434
6493
|
}
|
|
6435
6494
|
if (nodes !== null) {
|
|
6436
6495
|
const node = nodes[k];
|
|
@@ -6446,7 +6505,7 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6446
6505
|
if (k === $OWNER) continue;
|
|
6447
6506
|
const nv = incoming[k];
|
|
6448
6507
|
if (!shallow && nv !== null && typeof nv === "object")
|
|
6449
|
-
descend(unwrapValue(old[k]), nv, keyFn,
|
|
6508
|
+
descend(unwrapValue(old[k]), nv, keyFn, proj, t, k);
|
|
6450
6509
|
if (nodes !== null) {
|
|
6451
6510
|
const node = nodes[k];
|
|
6452
6511
|
if (node !== undefined) {
|
|
@@ -6466,16 +6525,39 @@ function applyAdopt(t, incoming, keyFn, proj = false) {
|
|
|
6466
6525
|
const hasOwnP = Object.prototype.hasOwnProperty;
|
|
6467
6526
|
/** Descend into a changed child pair — only where something is proxied
|
|
6468
6527
|
* below (§6d), never into a raw-marked leaf, never across array/object or a
|
|
6469
|
-
* key mismatch (that is a replacement, handled by the parent's slot).
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6528
|
+
* key mismatch (that is a replacement, handled by the parent's slot).
|
|
6529
|
+
*
|
|
6530
|
+
* A raw in both frames (the same objects swapped, reordered or shifted,
|
|
6531
|
+
* #4001) must not leave two slots on one row. A raw already a live row keeps
|
|
6532
|
+
* it at the slot it moved to; a row's `pk` follows the slot it sits at (a
|
|
6533
|
+
* move or a pairing writes it), so a row whose raw is at that slot of the
|
|
6534
|
+
* incoming frame is spoken for and not paired again here; a raw still
|
|
6535
|
+
* resolving to a row that moved on drops the lookup (it wraps fresh, or is
|
|
6536
|
+
* paired here). One lookup of `nv` on the common path, no allocation. */
|
|
6537
|
+
function descend(pv, nv, keyFn, proj, t, k) {
|
|
6538
|
+
// `nv` is an object at every call site.
|
|
6539
|
+
if (pv === null || typeof pv !== "object") return;
|
|
6540
|
+
const fam = t.fam;
|
|
6541
|
+
nv = unwrapValue(nv);
|
|
6542
|
+
let ct = lookupTarget(pv, fam);
|
|
6543
|
+
const nt = lookupTarget(nv, fam);
|
|
6544
|
+
if (ct !== undefined && ct.pk !== k) {
|
|
6545
|
+
if (ct.pk != k && t.v[ct.pk] === ct.v) ct = undefined;
|
|
6546
|
+
else if (ct.u === t) ct.pk = k;
|
|
6547
|
+
}
|
|
6548
|
+
if (nt !== undefined && nt !== ct) {
|
|
6549
|
+
if (nt.v === nv) {
|
|
6550
|
+
if (nt.u === t) nt.pk = k;
|
|
6551
|
+
return;
|
|
6552
|
+
}
|
|
6553
|
+
(fam?.map ?? storeLookup).delete(nv);
|
|
6554
|
+
}
|
|
6473
6555
|
if (ct === undefined) return; // nothing proxied below this pair
|
|
6474
6556
|
if (!isWrappable(nv)) return;
|
|
6475
6557
|
if (rawValuesUsed && isRawValue(nv)) return;
|
|
6476
|
-
nv = unwrapValue(nv);
|
|
6477
6558
|
if (Array.isArray(pv) !== Array.isArray(nv)) return;
|
|
6478
|
-
|
|
6559
|
+
// An array slot was paired by key already (or both rows are keyless).
|
|
6560
|
+
if (typeof k !== "number" && keyFn) {
|
|
6479
6561
|
const pk = keyFn(pv);
|
|
6480
6562
|
const nk = keyFn(nv);
|
|
6481
6563
|
if (pk !== undefined && nk !== undefined && !sameKey(pk, nk)) return;
|
|
@@ -6585,8 +6667,14 @@ function cloneState(v, shallow) {
|
|
|
6585
6667
|
* pending derivatively on the derive (its readers hold; it re-runs at the
|
|
6586
6668
|
* landing). The leaves do not subscribe to the derive, so the family does
|
|
6587
6669
|
* the walk. */
|
|
6588
|
-
function wakeFamily(fam
|
|
6670
|
+
function wakeFamily(fam) {
|
|
6589
6671
|
const fw = fam.node;
|
|
6672
|
+
// Pending on the derive itself, whatever source took it there (an upstream
|
|
6673
|
+
// memo's flight reaches it as a NotReady of that memo, #4271): the leaves
|
|
6674
|
+
// are not that source's dependents, so only the family's settle — which
|
|
6675
|
+
// retires the derive (`settleFamily`) — reaches the readers parked here. A
|
|
6676
|
+
// reader the landing leaves unchanged does not re-run to clear it (#4305).
|
|
6677
|
+
const error = new NotReadyError(fw);
|
|
6590
6678
|
fam.woke = true;
|
|
6591
6679
|
forEachFamilyNode(fam, n => {
|
|
6592
6680
|
if (n._subs === null) return;
|
|
@@ -6624,6 +6712,13 @@ function settleFamily(fam) {
|
|
|
6624
6712
|
if (n._subs !== null) settlePendingSource(n, fw);
|
|
6625
6713
|
});
|
|
6626
6714
|
}
|
|
6715
|
+
/** A source's status reached the derive by propagation (core
|
|
6716
|
+
* `propagateStatus`), its pass not re-run: the family learns it as from the
|
|
6717
|
+
* pass's own throw. */
|
|
6718
|
+
function familyStatus(status) {
|
|
6719
|
+
if (status & STATUS_PENDING) wakeFamily(this);
|
|
6720
|
+
else if (status & STATUS_ERROR) errorFamily(this);
|
|
6721
|
+
}
|
|
6627
6722
|
function forEachFamilyNode(fam, fn) {
|
|
6628
6723
|
for (const t of fam.live) {
|
|
6629
6724
|
const nodes = t.n;
|
|
@@ -6710,7 +6805,7 @@ function runProjectionComputed(wrappedStore, fn, key, wrapCommit, aroundDraftWri
|
|
|
6710
6805
|
// #2897) — and the flush that runs them advances the clock, so a
|
|
6711
6806
|
// later tracked re-read may retry (core `read`).
|
|
6712
6807
|
if (first && owner._statusFlags & STATUS_UNINITIALIZED) return;
|
|
6713
|
-
if (pending) wakeFamily(fam
|
|
6808
|
+
if (pending) wakeFamily(fam);
|
|
6714
6809
|
else errorFamily(fam);
|
|
6715
6810
|
});
|
|
6716
6811
|
if (!owner._loading) commit(sync);
|
|
@@ -6725,7 +6820,7 @@ function runProjectionComputed(wrappedStore, fn, key, wrapCommit, aroundDraftWri
|
|
|
6725
6820
|
// they do not subscribe to the derive.
|
|
6726
6821
|
// A synchronous error the same way (#3956: a rerun that finds the
|
|
6727
6822
|
// entry failed throws): every live reader re-derives and meets it.
|
|
6728
|
-
if (!first) e instanceof NotReadyError ? wakeFamily(fam
|
|
6823
|
+
if (!first) e instanceof NotReadyError ? wakeFamily(fam) : errorFamily(fam);
|
|
6729
6824
|
throw e;
|
|
6730
6825
|
}
|
|
6731
6826
|
// A pass that returned with a flight up (an iterator's sync first yield):
|
|
@@ -6738,7 +6833,7 @@ function runProjectionComputed(wrappedStore, fn, key, wrapCommit, aroundDraftWri
|
|
|
6738
6833
|
!owner._loading &&
|
|
6739
6834
|
!first
|
|
6740
6835
|
)
|
|
6741
|
-
wakeFamily(fam
|
|
6836
|
+
wakeFamily(fam);
|
|
6742
6837
|
return owner;
|
|
6743
6838
|
}
|
|
6744
6839
|
function createProjectionInternal(fn, seed, options) {
|
|
@@ -6750,6 +6845,7 @@ function createProjectionInternal(fn, seed, options) {
|
|
|
6750
6845
|
map: new WeakMap(),
|
|
6751
6846
|
node: null,
|
|
6752
6847
|
live: new Set(),
|
|
6848
|
+
st: familyStatus,
|
|
6753
6849
|
shallow: !!options?.shallow,
|
|
6754
6850
|
// The row key, for a child paired after the adoption (store.ts
|
|
6755
6851
|
// `adoptChild`, #3712) as the adoption pairs them.
|
|
@@ -8289,7 +8385,9 @@ function merge(...sources) {
|
|
|
8289
8385
|
count++;
|
|
8290
8386
|
only = s;
|
|
8291
8387
|
if (typeof s === "function") {
|
|
8292
|
-
|
|
8388
|
+
// Observe tiers name it and exempt it from UNSTABLE_MEMO_OUTPUT (see
|
|
8389
|
+
// CONFIG_KEY_BAG); prod passes no options.
|
|
8390
|
+
flattened[n] = createMemo(s, { name: "merge", _extraConfig: CONFIG_KEY_BAG });
|
|
8293
8391
|
kinds[n++] = SOURCE_MEMO;
|
|
8294
8392
|
continue;
|
|
8295
8393
|
}
|
|
@@ -8596,7 +8694,7 @@ function walk(value, entry, found, fam, visited) {
|
|
|
8596
8694
|
if (t !== undefined) {
|
|
8597
8695
|
if (t.pb !== null) entry._scope.add(t.v);
|
|
8598
8696
|
// The writer's view: the tick's own unflushed guesses are in motion too.
|
|
8599
|
-
if (t.fam?.opt === true) raw = optHooks.view(t, raw, true);
|
|
8697
|
+
if (optHooks && t.fam?.opt === true) raw = optHooks.view(t, raw, true);
|
|
8600
8698
|
const nodes = t.n;
|
|
8601
8699
|
if (nodes !== null)
|
|
8602
8700
|
for (const k of Reflect.ownKeys(nodes))
|
|
@@ -8904,46 +9002,282 @@ function createDeferred(fn, options) {
|
|
|
8904
9002
|
// Sticky, and read before this wrapper ever opens the window itself.
|
|
8905
9003
|
const open = (answered ||= !(node._statusFlags & STATUS_UNINITIALIZED) && !node._loading);
|
|
8906
9004
|
if (open) ((node._loading = true), (node._config |= CONFIG_LAGS));
|
|
8907
|
-
let r;
|
|
8908
9005
|
try {
|
|
8909
|
-
r = fn(prev);
|
|
9006
|
+
const r = fn(prev);
|
|
9007
|
+
if (open && typeof r === "object" && r !== null) {
|
|
9008
|
+
if (isThenable(r)) {
|
|
9009
|
+
ask(r);
|
|
9010
|
+
const done = () => land(r, true);
|
|
9011
|
+
r.then(done, done);
|
|
9012
|
+
return serve(r);
|
|
9013
|
+
}
|
|
9014
|
+
const iterate = r[Symbol.asyncIterator];
|
|
9015
|
+
if (iterate) {
|
|
9016
|
+
// A stream's flight ends at its first yield (or its end).
|
|
9017
|
+
const it = iterate.call(r);
|
|
9018
|
+
let first = true;
|
|
9019
|
+
const done = () => land(s, true);
|
|
9020
|
+
const s = {
|
|
9021
|
+
// Registered ahead of the engine's own reaction to the same
|
|
9022
|
+
// promise, as for a plain flight: verdict and value in one flush.
|
|
9023
|
+
next: v => {
|
|
9024
|
+
const p = it.next(v);
|
|
9025
|
+
if (first) {
|
|
9026
|
+
first = false;
|
|
9027
|
+
if (isThenable(p)) p.then(done, done);
|
|
9028
|
+
else done();
|
|
9029
|
+
}
|
|
9030
|
+
return p;
|
|
9031
|
+
},
|
|
9032
|
+
return: v => it.return?.(v) ?? Promise.resolve({ done: true, value: v })
|
|
9033
|
+
};
|
|
9034
|
+
ask(s);
|
|
9035
|
+
return serve({ [Symbol.asyncIterator]: () => s });
|
|
9036
|
+
}
|
|
9037
|
+
}
|
|
9038
|
+
land(flight);
|
|
9039
|
+
return r;
|
|
8910
9040
|
} catch (e) {
|
|
8911
9041
|
// The wrapping form: an unready source parks the node in the window,
|
|
8912
9042
|
// and the wait is its flight until the source's settle re-runs it.
|
|
8913
9043
|
if (open && e instanceof NotReadyError) ask(e);
|
|
9044
|
+
else land(flight);
|
|
8914
9045
|
throw e;
|
|
8915
9046
|
}
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
9047
|
+
}, options)
|
|
9048
|
+
);
|
|
9049
|
+
}
|
|
9050
|
+
|
|
9051
|
+
// `from` — a push source (callback subscription, Observable, AsyncIterable)
|
|
9052
|
+
// as an AsyncIterable a computation can return or `for await` over:
|
|
9053
|
+
//
|
|
9054
|
+
// const value = createMemo(() => from(source));
|
|
9055
|
+
//
|
|
9056
|
+
// A subscription keeps the latest value: values that arrive between pulls
|
|
9057
|
+
// coalesce. An AsyncIterable streams through unchanged. Either way the source
|
|
9058
|
+
// opens when the result is iterated and closes on return(), on supersede (the
|
|
9059
|
+
// owning computation re-runs) and on owner disposal, whichever comes first.
|
|
9060
|
+
function toSubscribe(source) {
|
|
9061
|
+
if (typeof source === "function") return source;
|
|
9062
|
+
// TC39 / RxJS interop: `Symbol.observable` (or "@@observable") returns the observable.
|
|
9063
|
+
// Read per call, not at module scope, so the module stays side-effect free.
|
|
9064
|
+
const $$observable = Symbol.observable;
|
|
9065
|
+
const interop = ($$observable && source?.[$$observable]) ?? source?.["@@observable"];
|
|
9066
|
+
const obs = typeof interop === "function" ? interop.call(source) : source;
|
|
9067
|
+
if (obs && typeof obs.subscribe === "function")
|
|
9068
|
+
return (next, error, complete) => obs.subscribe({ next, error, complete });
|
|
9069
|
+
return null;
|
|
9070
|
+
}
|
|
9071
|
+
const DONE = { value: undefined, done: true };
|
|
9072
|
+
/**
|
|
9073
|
+
* Ties an iterator's `finish` to the computation iterating it. Solid reaches
|
|
9074
|
+
* an iterator's return() only when the compute returns the iterable directly.
|
|
9075
|
+
* Inside an async generator the generator's own return() is queued behind its
|
|
9076
|
+
* pending `await`, and owner cleanups wait for the replacing pass to commit,
|
|
9077
|
+
* so close on supersede, with owner disposal as the backstop. No owner (an
|
|
9078
|
+
* async compute iterating it after its first `await`, or plain iteration):
|
|
9079
|
+
* no registration and no diagnostic, so only return() closes it. Scopes that
|
|
9080
|
+
* forbid cleanups rely on supersede and return().
|
|
9081
|
+
*/
|
|
9082
|
+
function bindToOwner(finish) {
|
|
9083
|
+
const owner = getOwner();
|
|
9084
|
+
if (!owner) return;
|
|
9085
|
+
onSupersede(finish);
|
|
9086
|
+
if (!(owner._config & CONFIG_CHILDREN_FORBIDDEN)) cleanup(finish);
|
|
9087
|
+
}
|
|
9088
|
+
/**
|
|
9089
|
+
* An AsyncIterable as a stream bound to the iterating computation: every
|
|
9090
|
+
* value passes through (no coalescing). `finish` calls the inner return()
|
|
9091
|
+
* once and settles pending pulls as done, so a `for await` over it exits as
|
|
9092
|
+
* soon as the computation re-runs — even when the inner iterator is an async
|
|
9093
|
+
* generator whose own return() the language queues behind its pending
|
|
9094
|
+
* `await`.
|
|
9095
|
+
*/
|
|
9096
|
+
function wrapIterable(source) {
|
|
9097
|
+
return {
|
|
9098
|
+
[Symbol.asyncIterator]() {
|
|
9099
|
+
const it = source[Symbol.asyncIterator]();
|
|
9100
|
+
let closed = false;
|
|
9101
|
+
const pending = new Set();
|
|
9102
|
+
const finish = () => {
|
|
9103
|
+
if (closed) return DONE;
|
|
9104
|
+
closed = true;
|
|
9105
|
+
for (const resolve of pending) resolve(DONE);
|
|
9106
|
+
pending.clear();
|
|
9107
|
+
try {
|
|
9108
|
+
const returned = it.return?.();
|
|
9109
|
+
if (isThenable(returned)) returned.then(undefined, () => {});
|
|
9110
|
+
} catch {}
|
|
9111
|
+
return DONE;
|
|
9112
|
+
};
|
|
9113
|
+
bindToOwner(finish);
|
|
9114
|
+
return {
|
|
9115
|
+
next(...args) {
|
|
9116
|
+
if (closed) return Promise.resolve(DONE);
|
|
9117
|
+
const step = it.next(...args);
|
|
9118
|
+
// A bare result (the producer's buffered fast path) passes through.
|
|
9119
|
+
if (!isThenable(step)) return step;
|
|
9120
|
+
return new Promise((resolve, reject) => {
|
|
9121
|
+
pending.add(resolve);
|
|
9122
|
+
step.then(
|
|
9123
|
+
r => pending.delete(resolve) && resolve(r),
|
|
9124
|
+
e => pending.delete(resolve) && reject(e)
|
|
9125
|
+
);
|
|
9126
|
+
});
|
|
9127
|
+
},
|
|
9128
|
+
return() {
|
|
9129
|
+
return Promise.resolve(finish());
|
|
8922
9130
|
}
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
9131
|
+
};
|
|
9132
|
+
}
|
|
9133
|
+
};
|
|
9134
|
+
}
|
|
9135
|
+
const toCleanup = t =>
|
|
9136
|
+
typeof t === "function"
|
|
9137
|
+
? t
|
|
9138
|
+
: t && typeof t.unsubscribe === "function"
|
|
9139
|
+
? () => t.unsubscribe()
|
|
9140
|
+
: () => {};
|
|
9141
|
+
/**
|
|
9142
|
+
* Converts a callback subscription, an Observable or an AsyncIterable into an
|
|
9143
|
+
* AsyncIterable for a Solid computation. A subscription keeps only the latest
|
|
9144
|
+
* value, and a value the source emits during `subscribe` is served without a
|
|
9145
|
+
* loading frame; an AsyncIterable streams every value through.
|
|
9146
|
+
*
|
|
9147
|
+
* The source closes on supersede — the owning computation re-running — and
|
|
9148
|
+
* on owner disposal. Inside an async generator memo that holds for a
|
|
9149
|
+
* `for await` that starts before the first `await`; one that starts after it
|
|
9150
|
+
* has no owner to bind to, so its source closes only when the loop ends.
|
|
9151
|
+
* Prefer returning `from(...)` from its own memo, with awaited inputs read
|
|
9152
|
+
* from upstream memos and transforms done in a downstream memo.
|
|
9153
|
+
*
|
|
9154
|
+
* Iterate the result with `for await` or return it from a computation: its
|
|
9155
|
+
* `next()` returns an already-buffered value as a bare result, not a promise.
|
|
9156
|
+
*
|
|
9157
|
+
* @example
|
|
9158
|
+
* ```ts
|
|
9159
|
+
* const position = createMemo(() =>
|
|
9160
|
+
* from<Position>((next, error) => {
|
|
9161
|
+
* const id = navigator.geolocation.watchPosition(next, error);
|
|
9162
|
+
* return () => navigator.geolocation.clearWatch(id);
|
|
9163
|
+
* })
|
|
9164
|
+
* );
|
|
9165
|
+
* const ticks = createMemo(() => from(interval$(rate())));
|
|
9166
|
+
* ```
|
|
9167
|
+
*/
|
|
9168
|
+
function from(source) {
|
|
9169
|
+
const subscribe = toSubscribe(source);
|
|
9170
|
+
if (!subscribe) {
|
|
9171
|
+
if (source && typeof source[Symbol.asyncIterator] === "function") return wrapIterable(source);
|
|
9172
|
+
throw new TypeError("from(): expected a subscribe function, an Observable or an AsyncIterable");
|
|
9173
|
+
}
|
|
9174
|
+
return {
|
|
9175
|
+
[Symbol.asyncIterator]() {
|
|
9176
|
+
let latest;
|
|
9177
|
+
let has = false;
|
|
9178
|
+
let done = false;
|
|
9179
|
+
let failed = false;
|
|
9180
|
+
let failure;
|
|
9181
|
+
let waiting = null;
|
|
9182
|
+
let unsubscribe = null;
|
|
9183
|
+
let closed = false;
|
|
9184
|
+
const close = () => {
|
|
9185
|
+
if (closed) return;
|
|
9186
|
+
closed = true;
|
|
9187
|
+
done = true;
|
|
9188
|
+
const c = unsubscribe;
|
|
9189
|
+
unsubscribe = null;
|
|
9190
|
+
// A throwing unsubscribe must not escape: at supersede this runs
|
|
9191
|
+
// inside flush(), ahead of the replacing pass (the returned-iterator
|
|
9192
|
+
// path swallows it the same way).
|
|
9193
|
+
try {
|
|
9194
|
+
c?.();
|
|
9195
|
+
} catch {}
|
|
9196
|
+
};
|
|
9197
|
+
const settle = () => {
|
|
9198
|
+
if (!waiting) return;
|
|
9199
|
+
const w = waiting;
|
|
9200
|
+
if (has) {
|
|
9201
|
+
waiting = null;
|
|
9202
|
+
has = false;
|
|
9203
|
+
w.resolve({ value: latest, done: false });
|
|
9204
|
+
} else if (failed) {
|
|
9205
|
+
waiting = null;
|
|
9206
|
+
failed = false;
|
|
9207
|
+
w.reject(failure);
|
|
9208
|
+
} else if (done) {
|
|
9209
|
+
waiting = null;
|
|
9210
|
+
w.resolve({ value: undefined, done: true });
|
|
8941
9211
|
}
|
|
9212
|
+
};
|
|
9213
|
+
const teardown = subscribe(
|
|
9214
|
+
value => {
|
|
9215
|
+
if (done) return;
|
|
9216
|
+
latest = value;
|
|
9217
|
+
has = true;
|
|
9218
|
+
settle();
|
|
9219
|
+
},
|
|
9220
|
+
err => {
|
|
9221
|
+
if (done) return;
|
|
9222
|
+
failed = true;
|
|
9223
|
+
failure = err;
|
|
9224
|
+
done = true;
|
|
9225
|
+
settle();
|
|
9226
|
+
close();
|
|
9227
|
+
},
|
|
9228
|
+
() => {
|
|
9229
|
+
if (done) return;
|
|
9230
|
+
done = true;
|
|
9231
|
+
settle();
|
|
9232
|
+
close();
|
|
9233
|
+
}
|
|
9234
|
+
);
|
|
9235
|
+
unsubscribe = toCleanup(teardown);
|
|
9236
|
+
// Completed or errored during subscribe: release what it just returned.
|
|
9237
|
+
if (closed) {
|
|
9238
|
+
const c = unsubscribe;
|
|
9239
|
+
unsubscribe = null;
|
|
9240
|
+
try {
|
|
9241
|
+
c();
|
|
9242
|
+
} catch {}
|
|
8942
9243
|
}
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
9244
|
+
// Idempotent: return(), supersede and disposal may all reach it.
|
|
9245
|
+
const finish = () => {
|
|
9246
|
+
close();
|
|
9247
|
+
has = false;
|
|
9248
|
+
if (waiting) {
|
|
9249
|
+
const w = waiting;
|
|
9250
|
+
waiting = null;
|
|
9251
|
+
w.resolve(DONE);
|
|
9252
|
+
}
|
|
9253
|
+
return DONE;
|
|
9254
|
+
};
|
|
9255
|
+
bindToOwner(finish);
|
|
9256
|
+
return {
|
|
9257
|
+
next() {
|
|
9258
|
+
// A buffered value is returned as a bare result, not a promise: the
|
|
9259
|
+
// iterator consumer lands it in the same pass, so a source that
|
|
9260
|
+
// emits during subscribe (BehaviorSubject-style) shows its value
|
|
9261
|
+
// with no loading frame. `for await` accepts either form.
|
|
9262
|
+
if (has) {
|
|
9263
|
+
has = false;
|
|
9264
|
+
return { value: latest, done: false };
|
|
9265
|
+
}
|
|
9266
|
+
if (failed) {
|
|
9267
|
+
failed = false;
|
|
9268
|
+
return Promise.reject(failure);
|
|
9269
|
+
}
|
|
9270
|
+
if (done) return Promise.resolve({ value: undefined, done: true });
|
|
9271
|
+
return new Promise((resolve, reject) => {
|
|
9272
|
+
waiting = { resolve, reject };
|
|
9273
|
+
});
|
|
9274
|
+
},
|
|
9275
|
+
return() {
|
|
9276
|
+
return Promise.resolve(finish());
|
|
9277
|
+
}
|
|
9278
|
+
};
|
|
9279
|
+
}
|
|
9280
|
+
};
|
|
8947
9281
|
}
|
|
8948
9282
|
|
|
8949
9283
|
// CARVE 4: `flatten` lived in boundaries.ts but is the general children
|
|
@@ -9062,6 +9396,9 @@ const BOUNDARY = Symbol("boundary");
|
|
|
9062
9396
|
* slot of (reveal.ts); a boundary clears it for its content — only direct
|
|
9063
9397
|
* children are slots. */
|
|
9064
9398
|
const REVEAL = Symbol("reveal");
|
|
9399
|
+
// Every call site checks the hooks themselves first (not only the slot or
|
|
9400
|
+
// controller, which are never set without them): a build that does not
|
|
9401
|
+
// import `createRevealOrder` folds the reveal-order arms away.
|
|
9065
9402
|
let revealHooks = null;
|
|
9066
9403
|
function setRevealHooks(h) {
|
|
9067
9404
|
revealHooks = h;
|
|
@@ -9099,7 +9436,7 @@ function caught(b, node, error) {
|
|
|
9099
9436
|
// nothing on screen. The fallback is instantiated once per show — a later
|
|
9100
9437
|
// error reaches it through the `error` accessor, which is why it is one.
|
|
9101
9438
|
if (!b._fallback) redraw(b);
|
|
9102
|
-
if (b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9439
|
+
if (revealHooks && b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9103
9440
|
}
|
|
9104
9441
|
/** A re-armed boundary found something pending: fresh again, and the swap
|
|
9105
9442
|
* the arming pass asked for is that pass's lane's (if any): the output is
|
|
@@ -9111,7 +9448,8 @@ function flip(b) {
|
|
|
9111
9448
|
b._lane = b._armLane;
|
|
9112
9449
|
const o = b._output;
|
|
9113
9450
|
if (!b._lane && o._config & CONFIG_HELD && txOf(o) !== flushTransaction) unhold(o);
|
|
9114
|
-
if (b._lane !== null && b._output !== null)
|
|
9451
|
+
if (laneStageHook && b._lane !== null && b._output !== null)
|
|
9452
|
+
laneStageHook(b._output, b._lane, true, false);
|
|
9115
9453
|
}
|
|
9116
9454
|
function redraw(b) {
|
|
9117
9455
|
if (b._output !== null && !(b._output._flags & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
|
|
@@ -9139,7 +9477,7 @@ function catchStatus(node, flags, error) {
|
|
|
9139
9477
|
b._readers.size === 0
|
|
9140
9478
|
) {
|
|
9141
9479
|
if (b._fallback) redraw(b);
|
|
9142
|
-
if (b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9480
|
+
if (revealHooks && b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9143
9481
|
}
|
|
9144
9482
|
return false;
|
|
9145
9483
|
}
|
|
@@ -9273,7 +9611,7 @@ function boundarySeam() {
|
|
|
9273
9611
|
if (b._readers.size === 0) {
|
|
9274
9612
|
collecting.delete(b);
|
|
9275
9613
|
if (b._fallback) redraw(b);
|
|
9276
|
-
if (b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9614
|
+
if (revealHooks && b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9277
9615
|
}
|
|
9278
9616
|
}
|
|
9279
9617
|
}
|
|
@@ -9352,6 +9690,8 @@ function arm(b) {
|
|
|
9352
9690
|
return;
|
|
9353
9691
|
}
|
|
9354
9692
|
b._armed = true;
|
|
9693
|
+
// The swap is the change's work: the output joins it (D-13, #3540).
|
|
9694
|
+
b._output._config &= ~CONFIG_FRAME;
|
|
9355
9695
|
// A verdict read in `on` (`isPending`, or a memo of one — #3528's
|
|
9356
9696
|
// `createMemo(() => isPending(m2))`: the mark carries, core.ts `read`)
|
|
9357
9697
|
// whose frame has no transaction yet: the swap is still display-ahead — the frame gets one now (it lands
|
|
@@ -9431,7 +9771,7 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9431
9771
|
if (getOwner())
|
|
9432
9772
|
cleanup(() => {
|
|
9433
9773
|
collecting.delete(b);
|
|
9434
|
-
if (controller) revealHooks._unregister(controller, b);
|
|
9774
|
+
if (revealHooks && controller) revealHooks._unregister(controller, b);
|
|
9435
9775
|
});
|
|
9436
9776
|
// The `on` dependencies live OUTSIDE the boundary, as the condition of a
|
|
9437
9777
|
// `<Show>` wrapping it would: created before the owner's context names it.
|
|
@@ -9470,9 +9810,16 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9470
9810
|
b._tree = tree;
|
|
9471
9811
|
// A slot of the reveal order in its context (reveal.ts): gated until the
|
|
9472
9812
|
// controller releases it — before the output's first pass.
|
|
9473
|
-
if (controller) revealHooks._register(controller, b);
|
|
9813
|
+
if (revealHooks && controller) revealHooks._register(controller, b);
|
|
9474
9814
|
const output = computed(
|
|
9475
9815
|
() => {
|
|
9816
|
+
// Structure, not a derivation of its content (CONFIG_FRAME, #4283): it
|
|
9817
|
+
// reads a hold it is not part of as the screen, as the render effect
|
|
9818
|
+
// flattening the same children without a boundary would. Not while a
|
|
9819
|
+
// re-arm's swap is the change's work (`arm`), until that swap commits
|
|
9820
|
+
// (D-13, #3540).
|
|
9821
|
+
const self = getOwner();
|
|
9822
|
+
if (!b._armed && !(self._config & CONFIG_HELD)) self._config |= CONFIG_FRAME;
|
|
9476
9823
|
// Reveal order (reveal.ts): a gated slot shows its fallback — or, in a
|
|
9477
9824
|
// collapsed tail, nothing — whatever its content's state; the
|
|
9478
9825
|
// controller is told the pass ran (its readiness may have changed) and
|
|
@@ -9483,8 +9830,8 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9483
9830
|
// It may release this very slot (the frontier whose content just
|
|
9484
9831
|
// settled): the pass goes on to show it — the redraw it asks for is
|
|
9485
9832
|
// refused mid-pass and not needed.
|
|
9486
|
-
link(tree,
|
|
9487
|
-
if (b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9833
|
+
link(tree, self);
|
|
9834
|
+
if (revealHooks && b._reveal !== null) revealHooks._changed(b._reveal);
|
|
9488
9835
|
if (b._gated) {
|
|
9489
9836
|
if (b._collapsed) {
|
|
9490
9837
|
b._fallback = true;
|
|
@@ -9502,7 +9849,7 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9502
9849
|
// once clean.
|
|
9503
9850
|
const status = tree._statusFlags & (STATUS_PENDING | STATUS_ERROR);
|
|
9504
9851
|
if (status !== 0) {
|
|
9505
|
-
link(tree,
|
|
9852
|
+
link(tree, self);
|
|
9506
9853
|
if (status & type) {
|
|
9507
9854
|
if (isCollecting(b)) {
|
|
9508
9855
|
caught(b, tree, undefined);
|
|
@@ -9528,7 +9875,6 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9528
9875
|
// enters: the outside sees its committed value until the landing,
|
|
9529
9876
|
// which reveals the content — a fallback staged earlier is replaced
|
|
9530
9877
|
// ahead of the commit and never shown.
|
|
9531
|
-
const self = getOwner();
|
|
9532
9878
|
if (
|
|
9533
9879
|
b._readers.size !== 0 &&
|
|
9534
9880
|
self._statusFlags & STATUS_UNINITIALIZED &&
|
|
@@ -9569,19 +9915,27 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9569
9915
|
// landing when the frame parks (the run is held with it, as the old
|
|
9570
9916
|
// children of a held pass are) — not now: a held swap keeps the
|
|
9571
9917
|
// fallback on screen, live, until it commits. A pass back to the
|
|
9572
|
-
// fallback before then finds it still built and keeps it.
|
|
9918
|
+
// fallback before then finds it still built and keeps it. A fallback
|
|
9919
|
+
// the screen does not show (a swap staged with a held frame, replaced
|
|
9920
|
+
// before it commits) dies now: what it reads holds nothing (S-22 row
|
|
9921
|
+
// 3′, #4268).
|
|
9573
9922
|
const fb = b._fb;
|
|
9923
|
+
const drop = () => {
|
|
9924
|
+
if (!b._fallback && b._fb === fb) {
|
|
9925
|
+
// A fallback that threw (a rethrow the boundary does not
|
|
9926
|
+
// handle) is what an enclosing boundary waits on: it died, and
|
|
9927
|
+
// the seam of another round drops it there (#4270).
|
|
9928
|
+
if (fb._statusFlags & STATUS_ERROR) schedule();
|
|
9929
|
+
dispose(fb);
|
|
9930
|
+
b._fb = null;
|
|
9931
|
+
}
|
|
9932
|
+
};
|
|
9574
9933
|
if (fb !== null)
|
|
9575
|
-
globalQueue.enqueue(EFFECT_RENDER,
|
|
9576
|
-
if (!b._fallback && b._fb === fb) {
|
|
9577
|
-
dispose(fb);
|
|
9578
|
-
b._fb = null;
|
|
9579
|
-
}
|
|
9580
|
-
});
|
|
9934
|
+
b._output._value !== fb._value ? drop() : globalQueue.enqueue(EFFECT_RENDER, drop);
|
|
9581
9935
|
release(b);
|
|
9582
9936
|
// Shown: the slot leaves its reveal order (a later re-arm gates nobody
|
|
9583
9937
|
// else; the frontier moves on).
|
|
9584
|
-
if (b._reveal !== null) {
|
|
9938
|
+
if (revealHooks && b._reveal !== null) {
|
|
9585
9939
|
const controller = b._reveal;
|
|
9586
9940
|
b._reveal = null;
|
|
9587
9941
|
revealHooks._changed(controller);
|
|
@@ -9594,6 +9948,8 @@ function createBoundary(type, fn, show, onFn) {
|
|
|
9594
9948
|
{ name: "value", _noSnapshot: true }
|
|
9595
9949
|
);
|
|
9596
9950
|
output._config |= CONFIG_REDERIVE;
|
|
9951
|
+
// The tree is structure too (the output's pass marks the output).
|
|
9952
|
+
tree._config |= CONFIG_FRAME;
|
|
9597
9953
|
b._output = output;
|
|
9598
9954
|
return accessor(output);
|
|
9599
9955
|
}
|
|
@@ -9962,6 +10318,7 @@ export {
|
|
|
9962
10318
|
TimeoutError,
|
|
9963
10319
|
action,
|
|
9964
10320
|
affects,
|
|
10321
|
+
clock,
|
|
9965
10322
|
configureClientErrors,
|
|
9966
10323
|
createContext,
|
|
9967
10324
|
createDeferred,
|
|
@@ -9984,6 +10341,7 @@ export {
|
|
|
9984
10341
|
enableExternalSource,
|
|
9985
10342
|
flatten,
|
|
9986
10343
|
flush,
|
|
10344
|
+
from,
|
|
9987
10345
|
getContext,
|
|
9988
10346
|
getObserver,
|
|
9989
10347
|
getOwner,
|
|
@@ -10002,6 +10360,7 @@ export {
|
|
|
10002
10360
|
omit,
|
|
10003
10361
|
omitView,
|
|
10004
10362
|
onCleanup,
|
|
10363
|
+
onFlush,
|
|
10005
10364
|
onSettled,
|
|
10006
10365
|
reconcile,
|
|
10007
10366
|
refresh,
|