@solidjs/signals 2.0.0-rc.8 → 2.0.0-rc.9
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 +1399 -285
- package/dist/dev.attribution.js +459 -307
- package/dist/dev.js +1864 -435
- package/dist/observe/affects.js +3 -1
- package/dist/observe/attribution.js +7 -1
- package/dist/observe/boundaries.js +209 -154
- package/dist/observe/core/action.js +18 -8
- package/dist/observe/core/async.js +220 -110
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +282 -0
- package/dist/observe/core/attribution-hooks.js +23 -1
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +262 -485
- package/dist/observe/core/constants.js +34 -1
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +867 -367
- package/dist/observe/core/dev.js +78 -17
- package/dist/observe/core/effect.js +67 -51
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/external.js +4 -4
- package/dist/observe/core/graph.js +37 -37
- package/dist/observe/core/heap.js +45 -45
- package/dist/observe/core/invariants.js +2 -0
- package/dist/observe/core/lanes.js +86 -49
- package/dist/observe/core/optimistic.js +242 -95
- package/dist/observe/core/owner.js +98 -84
- package/dist/observe/core/scheduler.js +543 -305
- package/dist/observe/core/verdict.js +247 -129
- package/dist/observe/index.js +7 -3
- package/dist/observe/map.js +126 -124
- package/dist/observe/signals.js +33 -17
- package/dist/observe/store/index.js +2 -0
- package/dist/observe/store/next/optimistic.js +141 -132
- package/dist/observe/store/next/projection.js +34 -21
- package/dist/observe/store/next/reconcile.js +58 -56
- package/dist/observe/store/next/store.js +260 -146
- package/dist/observe/store/store.js +5 -3
- package/dist/observe/store/utils.js +948 -135
- package/dist/prod/attribution.js +26 -17
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +16 -8
- package/dist/prod/core/async.js +251 -143
- package/dist/prod/core/constants.js +34 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +843 -347
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +48 -34
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +37 -37
- package/dist/prod/core/heap.js +45 -45
- package/dist/prod/core/lanes.js +90 -53
- package/dist/prod/core/optimistic.js +247 -100
- package/dist/prod/core/owner.js +57 -45
- package/dist/prod/core/scheduler.js +543 -305
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +112 -112
- package/dist/prod/signals.js +28 -12
- package/dist/prod/store/next/optimistic.js +135 -128
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/store.js +325 -252
- package/dist/prod/store/store.js +2 -2
- package/dist/prod/store/utils.js +946 -135
- package/dist/types/attribution.d.ts +7 -2
- package/dist/types/attribution.prod.d.ts +10 -1
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +28 -3
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +51 -172
- package/dist/types/core/constants.d.ts +33 -0
- package/dist/types/core/core.d.ts +186 -5
- package/dist/types/core/dev.d.ts +129 -9
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +3 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +31 -4
- package/dist/types/core/scheduler.d.ts +95 -2
- package/dist/types/core/types.d.ts +3 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -1
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +6 -5
- package/dist/types/store/next/target.d.ts +1 -1
- package/dist/types/store/utils.d.ts +177 -6
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ import "../../core/effect.js";
|
|
|
12
12
|
|
|
13
13
|
import { CONFIG_AUTO_DISPOSE } from "../../core/constants.js";
|
|
14
14
|
|
|
15
|
+
import "../../core/dev.js";
|
|
16
|
+
|
|
15
17
|
import { $TARGET, STORE_VALUE, markRawIngest, setWriteOverride } from "../store.js";
|
|
16
18
|
|
|
17
19
|
import { reconcileNextState } from "./reconcile.js";
|
|
@@ -55,22 +57,23 @@ import { storeSetterNext, wrapNext } from "./store.js";
|
|
|
55
57
|
* Save/restore projectionWriteActive, never hard-reset: the draft can be
|
|
56
58
|
* driven from inside an enclosing authoritative-write scope (next-store
|
|
57
59
|
* optimistic derives), and a hard `false` would clobber it mid-derive.
|
|
58
|
-
*/ function wrapDraft(e, t, r) {
|
|
60
|
+
*/ function wrapDraft(e, t, r, i) {
|
|
59
61
|
const write = e => r ? r(e) : e();
|
|
60
|
-
const
|
|
61
|
-
get(
|
|
62
|
-
let
|
|
63
|
-
const
|
|
62
|
+
const o = {
|
|
63
|
+
get(o, n) {
|
|
64
|
+
let c;
|
|
65
|
+
const s = projectionWriteActive;
|
|
64
66
|
setWriteOverride(true);
|
|
65
67
|
setProjectionWriteActive(true);
|
|
66
68
|
try {
|
|
67
|
-
|
|
69
|
+
c = e[n];
|
|
68
70
|
} finally {
|
|
69
71
|
setWriteOverride(false);
|
|
70
|
-
setProjectionWriteActive(
|
|
72
|
+
setProjectionWriteActive(s);
|
|
71
73
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
// A shallow store's leaves are raw by contract (#3498): no draft proxy
|
|
75
|
+
// over them, so identity holds and a frozen leaf is never trapped.
|
|
76
|
+
return !i && typeof c === "object" && c !== null && n !== $TARGET ? wrapDraft(c, t, r) : c;
|
|
74
77
|
},
|
|
75
78
|
has(t, r) {
|
|
76
79
|
let i;
|
|
@@ -160,7 +163,7 @@ import { storeSetterNext, wrapNext } from "./store.js";
|
|
|
160
163
|
}
|
|
161
164
|
};
|
|
162
165
|
// Matching-kind dummy so Array.isArray(draft) answers like the store.
|
|
163
|
-
return new Proxy(Array.isArray(e) ? [] : {},
|
|
166
|
+
return new Proxy(Array.isArray(e) ? [] : {}, o);
|
|
164
167
|
}
|
|
165
168
|
|
|
166
169
|
function createProjectionNextInternal(e, t, r) {
|
|
@@ -188,7 +191,7 @@ function createProjectionNextInternal(e, t, r) {
|
|
|
188
191
|
if (!i.node) i.node = getOwner();
|
|
189
192
|
runProjectionComputedNext(o, e, r?.key === undefined ? "id" : r.key);
|
|
190
193
|
}, n);
|
|
191
|
-
c.
|
|
194
|
+
c.C &= ~CONFIG_AUTO_DISPOSE;
|
|
192
195
|
i.node = c;
|
|
193
196
|
return {
|
|
194
197
|
store: o,
|
|
@@ -211,30 +214,40 @@ function createProjectionNext(e, t, r) {
|
|
|
211
214
|
} ];
|
|
212
215
|
}
|
|
213
216
|
|
|
217
|
+
// A detached copy of projection state: the root container alone for a shallow
|
|
218
|
+
// store — its leaves are raw references by contract and stay so (#3498) — the
|
|
219
|
+
// whole tree otherwise.
|
|
220
|
+
function cloneState(e, t) {
|
|
221
|
+
return t ? Array.isArray(e) ? e.slice() : {
|
|
222
|
+
...e
|
|
223
|
+
} : JSON.parse(JSON.stringify(e));
|
|
224
|
+
}
|
|
225
|
+
|
|
214
226
|
function runProjectionComputedNext(e, t, r, i, o) {
|
|
215
227
|
const n = getOwner();
|
|
216
|
-
|
|
217
|
-
let s;
|
|
228
|
+
const c = e[$TARGET];
|
|
229
|
+
let s = false;
|
|
230
|
+
let u;
|
|
218
231
|
// Open loading window (seedLoadingValue): the observable store IS commit #0
|
|
219
232
|
// for the whole first flight — the derive works a detached shadow of the
|
|
220
233
|
// seed so draft writes cannot tear through to readers (#2988). Every commit
|
|
221
234
|
// point reconciles the shadow through the normal commit path.
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
storeSetterNext(
|
|
225
|
-
|
|
226
|
-
|
|
235
|
+
const l = n.Se ? cloneState(c[STORE_VALUE], c.s) : null;
|
|
236
|
+
const a = wrapDraft(e, () => !s || n.o?.ce === u, o, c.s);
|
|
237
|
+
storeSetterNext(a, o => {
|
|
238
|
+
u = t(l ?? o);
|
|
239
|
+
s = true;
|
|
227
240
|
const commit = t => {
|
|
228
241
|
// Shadow run: commit a detached snapshot, never the shadow itself
|
|
229
242
|
// (adoption takes the value by identity — handing it the live shadow
|
|
230
243
|
// would fuse the draft to the observable store).
|
|
231
|
-
if (
|
|
244
|
+
if (l && (t === undefined || t === l)) t = cloneState(l, c.s);
|
|
232
245
|
if (t === o || t === undefined) return;
|
|
233
246
|
const write = () => storeSetterNext(e, e => reconcileNextState(t, e, r, true), false);
|
|
234
247
|
i ? i(write, t) : write();
|
|
235
248
|
};
|
|
236
|
-
const
|
|
237
|
-
if (!n.
|
|
249
|
+
const a = handleAsync(n, u, commit);
|
|
250
|
+
if (!n.Se) commit(a);
|
|
238
251
|
}, false);
|
|
239
252
|
return n;
|
|
240
253
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isEqual } from "../../core/core.js";
|
|
2
2
|
|
|
3
|
+
import "../../core/dev.js";
|
|
4
|
+
|
|
3
5
|
import { projectionWriteActive } from "../../core/scheduler.js";
|
|
4
6
|
|
|
5
7
|
import "../../core/invariants.js";
|
|
@@ -35,46 +37,46 @@ import { isOwned, $OWNER, storeNextLookup, optHooks, lookupTarget } from "./targ
|
|
|
35
37
|
* members (R11). Kind changes replace wholesale (R10).
|
|
36
38
|
*/ function reconcileNextState(e, t, n, o = false) {
|
|
37
39
|
if (t == null) throw new Error("");
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
+
const i = t?.[$TARGET];
|
|
41
|
+
if (i === undefined || i.px !== t) throw new Error("");
|
|
40
42
|
// Reconcile's diff walks need a REAL pending container — a prototype
|
|
41
43
|
// overlay (#3044) materializes to the clone path first (edge: reconcile
|
|
42
44
|
// inside a setter that already wrote this target).
|
|
43
|
-
if (
|
|
44
|
-
let
|
|
45
|
+
if (i.ovl) materializePB(i);
|
|
46
|
+
let f = n === null ? null : typeof n === "string" ? e => e?.[n] : n;
|
|
45
47
|
// §7b chained backing: a projection derive returning a LIVE store proxy
|
|
46
48
|
// adopts the proxy itself as the backing — reads flow through the inner
|
|
47
49
|
// store's traps, so consumers subscribe to the inner graph and updates
|
|
48
50
|
// flow with no re-derive (#2941). The adoption diff still notifies THIS
|
|
49
51
|
// store's existing subscribers of the swap.
|
|
50
52
|
if (o && e !== t && e?.[$TARGET] !== undefined) {
|
|
51
|
-
const t =
|
|
53
|
+
const t = i.pb ?? i.v;
|
|
52
54
|
if (t === e) return;
|
|
53
55
|
// already chained to this store
|
|
54
|
-
adoptPB(
|
|
56
|
+
adoptPB(i, e);
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
57
59
|
const l = unwrapValue(e);
|
|
58
|
-
if (
|
|
60
|
+
if (f) {
|
|
59
61
|
// Root identity precondition — checked before ANY mutation, so a throwing
|
|
60
62
|
// reconcile is atomic by construction (RUL-12 ruling). Projections
|
|
61
63
|
// (replace=true) relax it: a root entity change merges in place — the
|
|
62
64
|
// root proxy is stable for life (proj R5/R11) — and children are NOT
|
|
63
65
|
// key-matched across the entity change (proj R7: keyFn drops to
|
|
64
66
|
// positional so old-entity subtrees never merge into the new entity's).
|
|
65
|
-
const e =
|
|
66
|
-
const t =
|
|
67
|
-
if (t !== undefined && !sameKey(
|
|
67
|
+
const e = i.pb ?? i.v;
|
|
68
|
+
const t = f(e);
|
|
69
|
+
if (t !== undefined && !sameKey(f(l), t)) {
|
|
68
70
|
if (!o) throw new Error("");
|
|
69
71
|
// Entity change: wholesale swap. The root proxy is stable for life
|
|
70
72
|
// (proj R5) but NOTHING below survives — children are never matched
|
|
71
73
|
// across an entity change even when their own keys align (proj R7).
|
|
72
74
|
// Displaced-raw unregistration (proj R10): the outgoing raw stops
|
|
73
75
|
// resolving to this proxy; re-handed later it wraps fresh.
|
|
74
|
-
const e =
|
|
76
|
+
const e = i.pb ?? i.v;
|
|
75
77
|
if (isOwned(e)) delete e[$OWNER]; // disown: wraps fresh if re-handed
|
|
76
|
-
else (
|
|
77
|
-
adoptPB(
|
|
78
|
+
else (i.fam?.map ?? storeNextLookup).delete(e);
|
|
79
|
+
adoptPB(i, l);
|
|
78
80
|
return;
|
|
79
81
|
}
|
|
80
82
|
}
|
|
@@ -83,35 +85,35 @@ import { isOwned, $OWNER, storeNextLookup, optHooks, lookupTarget } from "./targ
|
|
|
83
85
|
// armed nodes (reverting with their transaction); committed raw is never
|
|
84
86
|
// touched. Key-matched rows keep proxy identity by descending into the
|
|
85
87
|
// existing child targets instead of overriding their parent slots.
|
|
86
|
-
if (
|
|
87
|
-
optHooks.applyTentative(
|
|
88
|
+
if (i.fam?.opt === true && !projectionWriteActive && !getWriteOverride()) {
|
|
89
|
+
optHooks.applyTentative(i, l, f);
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
|
-
applyAdopt(
|
|
92
|
+
applyAdopt(i, l, f, o);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
function applyAdopt(e, t, n, o = false) {
|
|
94
|
-
const
|
|
96
|
+
const i = e.pb ?? e.v;
|
|
95
97
|
// The sound identity skip (O7): same reference AND we never diverged it.
|
|
96
|
-
if (t ===
|
|
97
|
-
const
|
|
98
|
+
if (t === i && !isOwned(i)) return;
|
|
99
|
+
const f = e.fam;
|
|
98
100
|
// §6b (R28): the diff's previous-arrangement baseline is the LANE VIEW —
|
|
99
101
|
// optimistic rows must be visible to key matching so a landing carrying the
|
|
100
102
|
// same key recycles their proxies. Raw `prev` keeps the identity/ownership
|
|
101
103
|
// roles above; only matching reads the view.
|
|
102
|
-
const l =
|
|
104
|
+
const l = f?.opt === true ? optHooks.optimisticView(e, i) : i;
|
|
103
105
|
const r = Array.isArray(t);
|
|
104
106
|
// Plain stores notify inline AFTER the descent (child registrations feed
|
|
105
107
|
// the fold diff's identity-preservation check); projections keep deferred
|
|
106
108
|
// folds (downstream holds can form later in the flush).
|
|
107
|
-
const s =
|
|
109
|
+
const s = f === null;
|
|
108
110
|
const u = e.s === true;
|
|
109
111
|
// Node-notify base is the view the nodes were last told (#3296): a draft
|
|
110
112
|
// preceding this reconcile already moved them to its pending backing at
|
|
111
113
|
// setter exit (prev, materialized above), so diffing incoming against the
|
|
112
114
|
// committed backing would skip a key the draft changed and the reconcile
|
|
113
115
|
// restores — the node would commit the superseded draft value.
|
|
114
|
-
const a =
|
|
116
|
+
const a = i;
|
|
115
117
|
adoptPB(e, t, s);
|
|
116
118
|
// Shallow adoption: records are slot values — sticky raw-mark the incoming
|
|
117
119
|
// set (R41) and never descend; slot notification is the positional diff.
|
|
@@ -121,7 +123,7 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
121
123
|
return;
|
|
122
124
|
}
|
|
123
125
|
if (r) {
|
|
124
|
-
const
|
|
126
|
+
const i = l;
|
|
125
127
|
const r = t;
|
|
126
128
|
// Fused array walk (eager mode): per-index notification rides the same
|
|
127
129
|
// loop as the descent (descend first — targetsEqual needs the child's
|
|
@@ -134,13 +136,13 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
134
136
|
// rows key-match in place — the steady-state polling shape — descend
|
|
135
137
|
// directly with zero staging. The prevByKey map is built only for the
|
|
136
138
|
// misaligned remainder, and never at all on aligned ticks.
|
|
137
|
-
const l =
|
|
139
|
+
const l = i.length;
|
|
138
140
|
const s = r.length;
|
|
139
141
|
let u = false;
|
|
140
142
|
let p = 0;
|
|
141
143
|
for (const y = Math.min(l, s); p < y; p++) {
|
|
142
144
|
const l = r[p];
|
|
143
|
-
const s =
|
|
145
|
+
const s = i[p];
|
|
144
146
|
// Routing heuristic only (aligned vs keyed remainder) — both routes
|
|
145
147
|
// notify identically and descend() is the one authoritative
|
|
146
148
|
// validator, so bare typeof gates suffice here; full isWrappable
|
|
@@ -148,7 +150,7 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
148
150
|
if (s !== l && !(s !== null && typeof s === "object" && l !== null && typeof l === "object" && sameKey(n(s), n(l)))) break;
|
|
149
151
|
// misaligned: fall to the keyed remainder below
|
|
150
152
|
// Identity skip inline (FINDING-1 guard), then descend the pair.
|
|
151
|
-
if ((s !== l || l !== null && typeof l === "object" && isOwned(l)) && l !== null && typeof l === "object") descend(unwrapValue(s), l, n,
|
|
153
|
+
if ((s !== l || l !== null && typeof l === "object" && isOwned(l)) && l !== null && typeof l === "object") descend(unwrapValue(s), l, n, f, o);
|
|
152
154
|
if (e.dk !== null && !u && !(l !== null && typeof l === "object" ? targetsEqual(s, l) : isEqual(s, l))) {
|
|
153
155
|
bumpDeep(e);
|
|
154
156
|
u = true;
|
|
@@ -185,29 +187,29 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
185
187
|
// them here let a duplicate key adopt a prefix row AGAIN while
|
|
186
188
|
// row ops (which correctly window from structStart) retained
|
|
187
189
|
// the later occurrence's DOM row against a never-adopted target.
|
|
188
|
-
for (let e = y; e <
|
|
189
|
-
const t = unwrapValue(
|
|
190
|
+
for (let e = y; e < i.length; e++) {
|
|
191
|
+
const t = unwrapValue(i[e]);
|
|
190
192
|
if (t !== null && typeof t === "object") {
|
|
191
193
|
const o = n(t);
|
|
192
194
|
if (o === undefined) continue;
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
+
const i = w.get(o);
|
|
196
|
+
if (i === undefined) w.set(o, e); else if (Array.isArray(i)) i.push(e); else w.set(o, [ i, e ]);
|
|
195
197
|
}
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
const e = w.get(t);
|
|
199
201
|
if (e === undefined) l = undefined; else if (Array.isArray(e)) {
|
|
200
|
-
l = unwrapValue(
|
|
202
|
+
l = unwrapValue(i[e.shift()]);
|
|
201
203
|
if (e.length === 1) w.set(t, e[0]);
|
|
202
204
|
} else {
|
|
203
|
-
l = unwrapValue(
|
|
205
|
+
l = unwrapValue(i[e]);
|
|
204
206
|
w.delete(t);
|
|
205
207
|
}
|
|
206
208
|
} else {
|
|
207
|
-
l = unwrapValue(
|
|
209
|
+
l = unwrapValue(i[p]);
|
|
208
210
|
// keyless item: positional fallback
|
|
209
211
|
}
|
|
210
|
-
descend(l, e, n,
|
|
212
|
+
descend(l, e, n, f, o);
|
|
211
213
|
}
|
|
212
214
|
if (c !== null) {
|
|
213
215
|
const e = c[p];
|
|
@@ -218,13 +220,13 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
218
220
|
}
|
|
219
221
|
}
|
|
220
222
|
} else {
|
|
221
|
-
const l = Math.min(
|
|
223
|
+
const l = Math.min(i.length, r.length);
|
|
222
224
|
const s = r.length;
|
|
223
225
|
let p = false;
|
|
224
226
|
for (let y = 0; y < s; y++) {
|
|
225
227
|
const s = r[y];
|
|
226
|
-
if (!u && y < l && s !== null && typeof s === "object") descend(unwrapValue(
|
|
227
|
-
if (e.dk !== null && !p && !(s !== null && typeof s === "object" ? targetsEqual(
|
|
228
|
+
if (!u && y < l && s !== null && typeof s === "object") descend(unwrapValue(i[y]), s, n, f, o);
|
|
229
|
+
if (e.dk !== null && !p && !(s !== null && typeof s === "object" ? targetsEqual(i[y], s) : isEqual(i[y], s))) {
|
|
228
230
|
bumpDeep(e);
|
|
229
231
|
p = true;
|
|
230
232
|
}
|
|
@@ -255,7 +257,7 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
255
257
|
// slots must not notify, R9). This replaces the notifyFold re-walk that
|
|
256
258
|
// doubled dbmon's diff cost. for-in covers own enumerable string keys
|
|
257
259
|
// with no key-array allocation; symbols get a pass only when present.
|
|
258
|
-
const
|
|
260
|
+
const i = s ? e.n : null;
|
|
259
261
|
let r = 0;
|
|
260
262
|
let c = false;
|
|
261
263
|
// The per-key body is inlined on purpose (legacy applyStateFast parity:
|
|
@@ -267,11 +269,11 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
267
269
|
const d = t[s];
|
|
268
270
|
const p = a[s];
|
|
269
271
|
const y = d !== null && typeof d === "object";
|
|
270
|
-
if (p === d && (!y || !isOwned(d)) && (
|
|
271
|
-
if (
|
|
272
|
+
if (p === d && (!y || !isOwned(d)) && (i === null || i[s] === undefined || !hasAccessorFlag(i[s]))) {
|
|
273
|
+
if (i !== null && i[s] !== undefined) r++;
|
|
272
274
|
continue;
|
|
273
275
|
}
|
|
274
|
-
if (y && !u) descend(unwrapValue(l[s]), d, n,
|
|
276
|
+
if (y && !u) descend(unwrapValue(l[s]), d, n, f, o);
|
|
275
277
|
// Deep-witness (dk): value changes must notify even with NO per-key
|
|
276
278
|
// node — deep() subscribes one node per record. Checked after descend
|
|
277
279
|
// so in-place adoptions (same logical slot) don't bump; child records
|
|
@@ -280,8 +282,8 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
280
282
|
bumpDeep(e);
|
|
281
283
|
c = true;
|
|
282
284
|
}
|
|
283
|
-
if (
|
|
284
|
-
const e =
|
|
285
|
+
if (i !== null) {
|
|
286
|
+
const e = i[s];
|
|
285
287
|
if (e !== undefined) {
|
|
286
288
|
r++;
|
|
287
289
|
notifyKeyValue(e, s, p, d, a, t);
|
|
@@ -293,9 +295,9 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
293
295
|
const s = d[e];
|
|
294
296
|
if (s === $OWNER) continue;
|
|
295
297
|
const c = t[s];
|
|
296
|
-
if (!u && c !== null && typeof c === "object") descend(unwrapValue(l[s]), c, n,
|
|
297
|
-
if (
|
|
298
|
-
const e =
|
|
298
|
+
if (!u && c !== null && typeof c === "object") descend(unwrapValue(l[s]), c, n, f, o);
|
|
299
|
+
if (i !== null) {
|
|
300
|
+
const e = i[s];
|
|
299
301
|
if (e !== undefined) {
|
|
300
302
|
r++;
|
|
301
303
|
notifyKeyValue(e, s, a[s], c, a, t);
|
|
@@ -305,9 +307,9 @@ function applyAdopt(e, t, n, o = false) {
|
|
|
305
307
|
if (s) {
|
|
306
308
|
// Deleted-key nodes (in the map but absent from incoming) — counted
|
|
307
309
|
// fast-out: when every node was visited, skip the sweep entirely.
|
|
308
|
-
if (
|
|
309
|
-
for (const e of Reflect.ownKeys(
|
|
310
|
-
if (!hasOwnP.call(t, e)) notifyKeyDiff(
|
|
310
|
+
if (i !== null && r < e.nc) {
|
|
311
|
+
for (const e of Reflect.ownKeys(i)) {
|
|
312
|
+
if (!hasOwnP.call(t, e)) notifyKeyDiff(i[e], e, a, t, false);
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
315
|
notifyFoldTail(e, a, t);
|
|
@@ -327,14 +329,14 @@ const hasOwnP = Object.prototype.hasOwnProperty;
|
|
|
327
329
|
return e === t || e !== e && t !== t;
|
|
328
330
|
}
|
|
329
331
|
|
|
330
|
-
function descend(e, t, n, o,
|
|
332
|
+
function descend(e, t, n, o, i = false) {
|
|
331
333
|
if (e === null || typeof e !== "object" || t === null || typeof t !== "object") return;
|
|
332
334
|
// Lookup FIRST: a hit implies pv was wrappable and never raw-marked (only
|
|
333
335
|
// wrappables acquire targets; rawValues never wrap) — one WeakMap get
|
|
334
336
|
// replaces isWrappable(pv) + isRawValue(pv), and a miss prunes untracked
|
|
335
337
|
// subtrees before any further checks.
|
|
336
|
-
const
|
|
337
|
-
if (
|
|
338
|
+
const f = lookupTarget(e, o);
|
|
339
|
+
if (f === undefined) return;
|
|
338
340
|
// nothing proxied below this pair
|
|
339
341
|
// The NEW side still validates fully: a frozen/platform/markRaw'd incoming
|
|
340
342
|
// value is a leaf for reconcile — replaced by reference, never recursed
|
|
@@ -348,13 +350,13 @@ function descend(e, t, n, o, f = false) {
|
|
|
348
350
|
if (Array.isArray(e) !== Array.isArray(t)) return;
|
|
349
351
|
if (n) {
|
|
350
352
|
const o = n(e);
|
|
351
|
-
const
|
|
353
|
+
const i = n(t);
|
|
352
354
|
// Key mismatch detaches: the slot takes the new entity; the old proxy
|
|
353
355
|
// keeps its (old) backing and a fresh proxy wraps the new value on read.
|
|
354
356
|
// SameValueZero (re-audit 2, P1-5): NaN keys are self-equal — strict
|
|
355
357
|
// inequality detached every NaN-keyed slot on every tick while the
|
|
356
358
|
// Map-based row-ops matcher retained its DOM row (stale forever).
|
|
357
|
-
if (o !== undefined &&
|
|
359
|
+
if (o !== undefined && i !== undefined && !sameKey(o, i)) return;
|
|
358
360
|
}
|
|
359
361
|
// Reachability pruning (§6d) is MODE-dependent, both pinned:
|
|
360
362
|
// - keyed matching descends only where subscriptions exist at/below (`d`) —
|
|
@@ -366,8 +368,8 @@ function descend(e, t, n, o, f = false) {
|
|
|
366
368
|
// UNCONDITIONALLY (proj R6: the slot keeps its proxy without needing a
|
|
367
369
|
// subscriber below); plain keyed reconcile detaches unobserved captures
|
|
368
370
|
// (recon-snap R18 — staleness is the pinned pruning contract).
|
|
369
|
-
if (!
|
|
370
|
-
applyAdopt(
|
|
371
|
+
if (!i && n !== null && !f.d) return;
|
|
372
|
+
applyAdopt(f, t, n, i);
|
|
371
373
|
}
|
|
372
374
|
|
|
373
375
|
export { reconcileNextState, sameKey };
|