@solidjs/signals 2.0.0-rc.13 → 2.0.0-rc.15

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.
Files changed (121) hide show
  1. package/dist/dev-shared.js +2059 -3768
  2. package/dist/dev.attribution.js +237 -101
  3. package/dist/dev.js +7466 -8497
  4. package/dist/observe/affects.js +128 -104
  5. package/dist/observe/boundaries.js +492 -665
  6. package/dist/observe/core/action.js +63 -66
  7. package/dist/observe/core/async.js +243 -379
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +394 -253
  10. package/dist/observe/core/constants.js +120 -187
  11. package/dist/observe/core/context.js +3 -3
  12. package/dist/observe/core/core.js +874 -1440
  13. package/dist/observe/core/dev.js +3 -3
  14. package/dist/observe/core/effect.js +73 -84
  15. package/dist/observe/core/external.js +5 -5
  16. package/dist/observe/core/graph.js +62 -71
  17. package/dist/observe/core/heap.js +75 -98
  18. package/dist/observe/core/invariants.js +1 -34
  19. package/dist/observe/core/lanes.js +750 -181
  20. package/dist/observe/core/owner.js +80 -101
  21. package/dist/observe/core/scheduler.js +719 -1356
  22. package/dist/observe/core/verdict.js +314 -621
  23. package/dist/observe/flatten.js +79 -0
  24. package/dist/observe/index.js +15 -13
  25. package/dist/observe/map.js +232 -235
  26. package/dist/observe/reveal.js +212 -0
  27. package/dist/observe/signals.js +144 -216
  28. package/dist/observe/store/affects.js +142 -0
  29. package/dist/observe/store/hooks.js +13 -0
  30. package/dist/observe/store/index.js +27 -24
  31. package/dist/observe/store/optimistic.js +571 -0
  32. package/dist/observe/store/projection.js +308 -0
  33. package/dist/observe/store/reconcile.js +259 -0
  34. package/dist/observe/store/store.js +1988 -277
  35. package/dist/observe/store/storePath.js +1 -1
  36. package/dist/{prod/store/next → observe/store}/target.js +17 -18
  37. package/dist/observe/store/types.js +73 -0
  38. package/dist/observe/store/utils.js +1 -13
  39. package/dist/prod/affects.js +130 -106
  40. package/dist/prod/boundaries.js +476 -654
  41. package/dist/prod/core/action.js +71 -74
  42. package/dist/prod/core/async.js +306 -444
  43. package/dist/prod/core/constants.js +117 -186
  44. package/dist/prod/core/context.js +3 -3
  45. package/dist/prod/core/core.js +888 -1462
  46. package/dist/prod/core/effect.js +88 -99
  47. package/dist/prod/core/external.js +5 -5
  48. package/dist/prod/core/graph.js +62 -71
  49. package/dist/prod/core/heap.js +75 -98
  50. package/dist/prod/core/invariants.js +1 -32
  51. package/dist/prod/core/lanes.js +712 -181
  52. package/dist/prod/core/owner.js +75 -96
  53. package/dist/prod/core/scheduler.js +686 -1335
  54. package/dist/prod/core/verdict.js +309 -621
  55. package/dist/prod/flatten.js +77 -0
  56. package/dist/prod/index.js +15 -13
  57. package/dist/prod/map.js +142 -145
  58. package/dist/prod/reveal.js +212 -0
  59. package/dist/prod/signals.js +155 -227
  60. package/dist/prod/store/affects.js +142 -0
  61. package/dist/prod/store/hooks.js +13 -0
  62. package/dist/prod/store/index.js +26 -19
  63. package/dist/prod/store/optimistic.js +566 -0
  64. package/dist/prod/store/projection.js +301 -0
  65. package/dist/prod/store/reconcile.js +259 -0
  66. package/dist/prod/store/store.js +1883 -272
  67. package/dist/prod/store/storePath.js +1 -1
  68. package/dist/{observe/store/next → prod/store}/target.js +13 -17
  69. package/dist/prod/store/types.js +73 -0
  70. package/dist/prod/store/utils.js +1 -11
  71. package/dist/types/affects.d.ts +11 -30
  72. package/dist/types/boundaries.d.ts +66 -176
  73. package/dist/types/carved.d.ts +1 -0
  74. package/dist/types/core/action.d.ts +5 -6
  75. package/dist/types/core/async.d.ts +10 -5
  76. package/dist/types/core/attribution-hooks.d.ts +20 -1
  77. package/dist/types/core/attribution.d.ts +26 -11
  78. package/dist/types/core/constants.d.ts +130 -180
  79. package/dist/types/core/core.d.ts +134 -264
  80. package/dist/types/core/dev.d.ts +2 -2
  81. package/dist/types/core/effect.d.ts +0 -7
  82. package/dist/types/core/graph.d.ts +7 -1
  83. package/dist/types/core/heap.d.ts +5 -7
  84. package/dist/types/core/index.d.ts +5 -5
  85. package/dist/types/core/invariants.d.ts +0 -29
  86. package/dist/types/core/lanes.d.ts +74 -86
  87. package/dist/types/core/owner.d.ts +3 -3
  88. package/dist/types/core/scheduler.d.ts +255 -331
  89. package/dist/types/core/types.d.ts +45 -85
  90. package/dist/types/core/verdict.d.ts +3 -0
  91. package/dist/types/flatten.d.ts +8 -0
  92. package/dist/types/index.d.ts +6 -4
  93. package/dist/types/reveal.d.ts +19 -0
  94. package/dist/types/signals.d.ts +35 -91
  95. package/dist/types/store/affects.d.ts +12 -0
  96. package/dist/types/store/hooks.d.ts +48 -0
  97. package/dist/types/store/index.d.ts +34 -15
  98. package/dist/types/store/optimistic.d.ts +23 -0
  99. package/dist/types/store/projection.d.ts +18 -0
  100. package/dist/types/store/reconcile.d.ts +3 -0
  101. package/dist/types/store/store.d.ts +132 -145
  102. package/dist/types/store/storePath.d.ts +1 -1
  103. package/dist/types/store/target.d.ts +151 -0
  104. package/dist/types/store/types.d.ts +84 -0
  105. package/package.json +1 -1
  106. package/dist/observe/core/optimistic.js +0 -669
  107. package/dist/observe/store/next/optimistic.js +0 -717
  108. package/dist/observe/store/next/projection.js +0 -266
  109. package/dist/observe/store/next/reconcile.js +0 -375
  110. package/dist/observe/store/next/store.js +0 -2608
  111. package/dist/prod/core/optimistic.js +0 -658
  112. package/dist/prod/store/next/optimistic.js +0 -710
  113. package/dist/prod/store/next/projection.js +0 -257
  114. package/dist/prod/store/next/reconcile.js +0 -373
  115. package/dist/prod/store/next/store.js +0 -2484
  116. package/dist/types/core/optimistic.d.ts +0 -6
  117. package/dist/types/store/next/optimistic.d.ts +0 -25
  118. package/dist/types/store/next/projection.d.ts +0 -27
  119. package/dist/types/store/next/reconcile.d.ts +0 -10
  120. package/dist/types/store/next/store.d.ts +0 -145
  121. package/dist/types/store/next/target.d.ts +0 -196
@@ -1,257 +0,0 @@
1
- import { computed } from "../../core/core.js";
2
-
3
- import { isDisposed, getOwner } from "../../core/owner.js";
4
-
5
- import { setProjectionWriteActive, projectionWriteActive, scheduleWithheld } from "../../core/scheduler.js";
6
-
7
- import { handleAsync } from "../../core/async.js";
8
-
9
- import "../../core/verdict.js";
10
-
11
- import "../../core/effect.js";
12
-
13
- import { CONFIG_AUTO_DISPOSE, STATUS_PENDING } from "../../core/constants.js";
14
-
15
- import { $TARGET, STORE_VALUE, markRawIngest, setWriteOverride } from "../store.js";
16
-
17
- import { reconcileNextState } from "./reconcile.js";
18
-
19
- import { storeSetterNext, wrapNext, derivedStoreWrite } from "./store.js";
20
-
21
- /**
22
- * Store rewrite — projections (§7/§7b): a projection is a computed store.
23
- * The derive runs inside a computed whose recompute merges its output into
24
- * the projection's backing through the adoption channel (replace-mode root:
25
- * entity changes merge in place, the root proxy is stable for life). Children
26
- * wrap into the projection's own FAMILY (writes land here, never in a source
27
- * family), and every family node carries the projection computed as its
28
- * firewall — reads link the derive's status and lifecycle natively. The §6c
29
- * status gate in the traps makes an uninitialized async derive's seed
30
- * unobservable through every read surface.
31
- *
32
- * Mirrors the legacy runProjectionComputed shape (shadow runs for open
33
- * loading windows, handleAsync landings, commit-through-setter) on next
34
- * primitives; the generic draft write-traps are reused from the legacy
35
- * module unchanged.
36
- */
37
- /**
38
- * Wrap a store proxy as a projection DRAFT: every operation carries the write
39
- * override (the derive is the author — its ops must not hit the §6c firewall
40
- * gate, even in a continuation after an `await`/`yield` where the sync write
41
- * scope has closed).
42
- *
43
- * FAKE TARGET, not the store proxy itself (#3060): after a proxy trap
44
- * returns, the engine runs spec invariant validation against the proxy's
45
- * TARGET — [[OwnPropertyKeys]] after ownKeys, [[GetOwnProperty]] after
46
- * set/getOwnPropertyDescriptor/defineProperty. With the store proxy as
47
- * target those checks re-enter the store's traps OUTSIDE the override
48
- * bracket (the trap's finally has already run), so `Object.keys(state)` in
49
- * a derive continuation fired the firewall gate and re-threw the
50
- * projection's own pending NotReadyError into the derive. A dummy of
51
- * matching kind (array/object, same trick as the store's own TargetShape)
52
- * keeps invariant validation away from the store entirely; the traps
53
- * forward to the closed-over inner proxy inside the bracket.
54
- *
55
- * Save/restore projectionWriteActive, never hard-reset: the draft can be
56
- * driven from inside an enclosing authoritative-write scope (next-store
57
- * optimistic derives), and a hard `false` would clobber it mid-derive.
58
- */ function wrapDraft(e, t, r, o, i) {
59
- // One bracket for the three mutating traps. A write to a superseded or
60
- // disposed draft is dropped silently (proj R37 — the same fate as a
61
- // superseded async run's pending draft writes, R26). `afterWrite` runs
62
- // once the bracket has closed — outside it, so the scheduler's
63
- // projectionWriteActive guard no longer applies — and only for the
64
- // outermost bracket (a draft driven from inside an enclosing authoritative
65
- // scope, the optimistic derive, leaves scheduling to that scope's caller).
66
- const mutate = e => {
67
- if (!t()) return true;
68
- const o = projectionWriteActive;
69
- setWriteOverride(true);
70
- setProjectionWriteActive(true);
71
- try {
72
- r ? r(e) : e();
73
- } finally {
74
- setWriteOverride(false);
75
- setProjectionWriteActive(o);
76
- }
77
- if (!o && i) i();
78
- return true;
79
- };
80
- const n = {
81
- get(n, c) {
82
- let s;
83
- const l = projectionWriteActive;
84
- setWriteOverride(true);
85
- setProjectionWriteActive(true);
86
- try {
87
- s = e[c];
88
- } finally {
89
- setWriteOverride(false);
90
- setProjectionWriteActive(l);
91
- }
92
- // A shallow store's leaves are raw by contract (#3498): no draft proxy
93
- // over them, so identity holds and a frozen leaf is never trapped.
94
- return !o && typeof s === "object" && s !== null && c !== $TARGET ? wrapDraft(s, t, r, false, i) : s;
95
- },
96
- has(t, r) {
97
- let o;
98
- const i = projectionWriteActive;
99
- setWriteOverride(true);
100
- setProjectionWriteActive(true);
101
- try {
102
- o = r in e;
103
- } finally {
104
- setWriteOverride(false);
105
- setProjectionWriteActive(i);
106
- }
107
- return o;
108
- },
109
- set: (t, r, o) => mutate(() => {
110
- e[r] = o;
111
- }),
112
- deleteProperty: (t, r) => mutate(() => {
113
- delete e[r];
114
- }),
115
- ownKeys() {
116
- const t = projectionWriteActive;
117
- setWriteOverride(true);
118
- setProjectionWriteActive(true);
119
- try {
120
- return Reflect.ownKeys(e);
121
- } finally {
122
- setWriteOverride(false);
123
- setProjectionWriteActive(t);
124
- }
125
- },
126
- getOwnPropertyDescriptor(t, r) {
127
- let o;
128
- const i = projectionWriteActive;
129
- setWriteOverride(true);
130
- setProjectionWriteActive(true);
131
- try {
132
- o = Reflect.getOwnPropertyDescriptor(e, r);
133
- } finally {
134
- setWriteOverride(false);
135
- setProjectionWriteActive(i);
136
- }
137
- // The dummy target doesn't hold the key, so a non-configurable report
138
- // would violate the proxy invariant. Store descriptors are already
139
- // normalized configurable; enforce it for raw leaves too.
140
- if (o) o.configurable = true;
141
- return o;
142
- },
143
- defineProperty: (t, r, o) => mutate(() => {
144
- Reflect.defineProperty(e, r, o);
145
- })
146
- };
147
- // Matching-kind dummy so Array.isArray(draft) answers like the store.
148
- return new Proxy(Array.isArray(e) ? [] : {}, n);
149
- }
150
-
151
- function createProjectionNextInternal(e, t, r) {
152
- const o = {
153
- map: new WeakMap,
154
- node: null,
155
- shallow: !!r?.shallow
156
- };
157
- const i = wrapNext(t, null, null, o);
158
- if (o.shallow) {
159
- // Shallow projection: the root is the only wrapped level — slot values
160
- // serve raw, ingests sticky raw-mark (same t.s machinery as plain).
161
- i[$TARGET].s = true;
162
- markRawIngest(t);
163
- }
164
- let n;
165
- if (r?.seedLoadingValue) n = {
166
- loadingValue: undefined
167
- };
168
- const c = computed(() => {
169
- if (!o.node) o.node = getOwner();
170
- runProjectionComputedNext(i, e, r?.key === undefined ? "id" : r.key);
171
- }, n);
172
- c.C &= ~CONFIG_AUTO_DISPOSE;
173
- o.node = c;
174
- return {
175
- store: i,
176
- node: c
177
- };
178
- }
179
-
180
- function createProjectionNext(e, t, r) {
181
- return createProjectionNextInternal(e, t, r).store;
182
- }
183
-
184
- /** Derived writable store (legacy parity): a projection whose public setter
185
- * masks the recompute for the tick (core R31 — the manual write wins over a
186
- * same-flush dependency change). Across a hold the write is not a proposal:
187
- * a leaf another transaction holds as the fold's result re-runs the fold
188
- * under it, the write being the draft's prior state (A34 amendment, #3612;
189
- * core derivedWrite). */ function createStoreDerivedNext(e, t, r) {
190
- const {store: o, node: i} = createProjectionNextInternal(e, t, r);
191
- return [ o, e => derivedStoreWrite(i, o, e) ];
192
- }
193
-
194
- // A detached copy of projection state: the root container alone for a shallow
195
- // store — its leaves are raw references by contract and stay so (#3498) — the
196
- // whole tree otherwise.
197
- function cloneState(e, t) {
198
- return t ? Array.isArray(e) ? e.slice() : {
199
- ...e
200
- } : JSON.parse(JSON.stringify(e));
201
- }
202
-
203
- function runProjectionComputedNext(e, t, r, o, i) {
204
- const n = getOwner();
205
- const c = e[$TARGET];
206
- const s = c.fam;
207
- // Draft validity is per run (R37): live from here until the NEXT run starts
208
- // — this counter moves, whatever that run does (lands, throws NotReady,
209
- // awaits) — or the owner is disposed. Not "until no longer in flight": a
210
- // sync derive that subscribes to an external source and pushes into the
211
- // draft from the callback (#3585) is the model use, and the old gate
212
- // (`owner._x?._inFlight === result`) only admitted it by accident — while
213
- // nothing had read the projection yet (no `_x`), `undefined === undefined`.
214
- const l = s.run = (s.run || 0) + 1;
215
- let u;
216
- // Open loading window (seedLoadingValue): the observable store IS commit #0
217
- // for the whole first flight — the derive works a detached shadow of the
218
- // seed so draft writes cannot tear through to readers (#2988). Every commit
219
- // point reconciles the shadow through the normal commit path. (A callback
220
- // that closes over the shadow writes into a dead clone once the window has
221
- // closed — the one carve-out from R37's one-draft-per-run model.)
222
- const a = n.ve ? cloneState(c[STORE_VALUE], c.s) : null;
223
- const f = wrapDraft(e, () => s.run === l && !isDisposed(n), i, c.s,
224
- // A write after the run returned takes the override channel (pending
225
- // backing + per-op notify + fold) and arms the drain itself when no
226
- // landing will: a flight still up — pending, or the loading window's
227
- // first flight — drains at its landing, and must not be drained early.
228
- // Not gated on the run having returned: the body's own writes withhold
229
- // the microtask too, and a derive body only ever runs outside a flush at
230
- // creation (reruns are flush-driven) — a top-level sync projection in a
231
- // createRoot stranded the scheduler until an explicit flush(). Arming
232
- // mid-body is safe: the microtask fires after this synchronous slice,
233
- // when the body has returned or parked, and an initial async run's
234
- // pre-await half drains before its landing exactly as it does when the
235
- // creation ran inside a flush — nothing reads it before the landing
236
- // (the node is uninitialized, proj R23).
237
- () => {
238
- if (!(n.h & STATUS_PENDING) && !n.ve) scheduleWithheld();
239
- });
240
- storeSetterNext(f, i => {
241
- u = t(a ?? i);
242
- const commit = t => {
243
- // Shadow run: commit a detached snapshot, never the shadow itself
244
- // (adoption takes the value by identity — handing it the live shadow
245
- // would fuse the draft to the observable store).
246
- if (a && (t === undefined || t === a)) t = cloneState(a, c.s);
247
- if (t === i || t === undefined) return;
248
- const write = () => storeSetterNext(e, e => reconcileNextState(t, e, r, true), false);
249
- o ? o(write, t) : write();
250
- };
251
- const s = handleAsync(n, u, commit);
252
- if (!n.ve) commit(s);
253
- }, false);
254
- return n;
255
- }
256
-
257
- export { createProjectionNext, createStoreDerivedNext, runProjectionComputedNext };
@@ -1,373 +0,0 @@
1
- import { isEqual } from "../../core/core.js";
2
-
3
- import { projectionWriteActive } from "../../core/scheduler.js";
4
-
5
- import "../../core/invariants.js";
6
-
7
- import "../../core/verdict.js";
8
-
9
- import "../../core/effect.js";
10
-
11
- import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue, getWriteOverride } from "../store.js";
12
-
13
- import { materializePB, adoptPB, unwrapValue, notifyFold, targetsEqual, bumpDeep, notifyKeyValue, notifyKeyDiff, notifyFoldTail, hasAccessorFlag } from "./store.js";
14
-
15
- import { isOwned, $OWNER, storeNextLookup, optHooks, lookupTarget } from "./target.js";
16
-
17
- /**
18
- * Store rewrite — reconcile, the adoption channel (INTERNALS-STORE-STATE.md
19
- * §3, decision 2026-08-16c). Reconcile never merge-writes: it adopts `next`
20
- * as the authoritative pending backing at every proxied level (pointer swap
21
- * folded at flush commit), notification riding the fold's descriptor diff.
22
- *
23
- * Structural optimizations (all kept, per 2026-08-17 morning ruling):
24
- * - Identity skip with completed proof: `incoming === backing && !owned` —
25
- * sound because input is immutable by convention (R2a) and ownership marks
26
- * the only writer the convention doesn't cover (us). Fixes FINDING-1.
27
- * - Reachability pruning: descent happens only where a child TARGET exists
28
- * (proxies exist only where read) — never-subscribed subtrees are never
29
- * walked (recon-snap R17), while a subscriber deep below an untracked path
30
- * keeps its chain walkable because wrapping created the intermediate
31
- * targets (recon-snap R16).
32
- * - Keyed matching ported semantics: key-matched rows keep proxy identity;
33
- * key mismatch detaches (fresh proxy on next read, recon-snap R18);
34
- * keyless items fall back positional; null/primitive slots are legal
35
- * members (R11). Kind changes replace wholesale (R10).
36
- */ function reconcileNextState(e, t, n, o = false) {
37
- if (t == null) throw new Error("");
38
- const f = t?.[$TARGET];
39
- if (f === undefined || f.px !== t) throw new Error("");
40
- // Reconcile's diff walks need a REAL pending container — a prototype
41
- // overlay (#3044) materializes to the clone path first (edge: reconcile
42
- // inside a setter that already wrote this target).
43
- if (f.ovl) materializePB(f);
44
- let i = n === null ? null : typeof n === "string" ? e => e?.[n] : n;
45
- // §7b chained backing: a projection derive returning a LIVE store proxy
46
- // adopts the proxy itself as the backing — reads flow through the inner
47
- // store's traps, so consumers subscribe to the inner graph and updates
48
- // flow with no re-derive (#2941). The adoption diff still notifies THIS
49
- // store's existing subscribers of the swap.
50
- if (o && e !== t && e?.[$TARGET] !== undefined) {
51
- const t = f.pb ?? f.v;
52
- if (t === e) return;
53
- // already chained to this store
54
- adoptPB(f, e);
55
- return;
56
- }
57
- const l = unwrapValue(e);
58
- if (i) {
59
- // Root identity precondition — checked before ANY mutation, so a throwing
60
- // reconcile is atomic by construction (RUL-12 ruling). Projections
61
- // (replace=true) relax it: a root entity change merges in place — the
62
- // root proxy is stable for life (proj R5/R11) — and children are NOT
63
- // key-matched across the entity change (proj R7: keyFn drops to
64
- // positional so old-entity subtrees never merge into the new entity's).
65
- const e = f.pb ?? f.v;
66
- const t = i(e);
67
- if (t !== undefined && !sameKey(i(l), t)) {
68
- if (!o) throw new Error("");
69
- // Entity change: wholesale swap. The root proxy is stable for life
70
- // (proj R5) but NOTHING below survives — children are never matched
71
- // across an entity change even when their own keys align (proj R7).
72
- // Displaced-raw unregistration (proj R10): the outgoing raw stops
73
- // resolving to this proxy; re-handed later it wraps fresh.
74
- const e = f.pb ?? f.v;
75
- if (isOwned(e)) delete e[$OWNER]; // disown: wraps fresh if re-handed
76
- else (f.fam?.map ?? storeNextLookup).delete(e);
77
- adoptPB(f, l);
78
- return;
79
- }
80
- }
81
- // Tentative channel (§6b, RUL-5): a user-context reconcile on an optimistic
82
- // family parks as engine overrides — values, membership, and length ride
83
- // armed nodes (reverting with their transaction); committed raw is never
84
- // touched. Key-matched rows keep proxy identity by descending into the
85
- // existing child targets instead of overriding their parent slots.
86
- if (f.fam?.opt === true && !projectionWriteActive && !getWriteOverride()) {
87
- optHooks.applyTentative(f, l, i);
88
- return;
89
- }
90
- applyAdopt(f, l, i, o);
91
- }
92
-
93
- function applyAdopt(e, t, n, o = false) {
94
- const f = e.pb ?? e.v;
95
- // The sound identity skip (O7): same reference AND we never diverged it.
96
- if (t === f && !isOwned(f)) return;
97
- const i = e.fam;
98
- // §6b (R28): the diff's previous-arrangement baseline is the LANE VIEW —
99
- // optimistic rows must be visible to key matching so a landing carrying the
100
- // same key recycles their proxies. Raw `prev` keeps the identity/ownership
101
- // roles above; only matching reads the view.
102
- const l = i?.opt === true ? optHooks.optimisticView(e, f) : f;
103
- const r = Array.isArray(t);
104
- // Plain stores notify inline AFTER the descent (child registrations feed
105
- // the fold diff's identity-preservation check); projections keep deferred
106
- // folds (downstream holds can form later in the flush).
107
- const s = i === null;
108
- const u = e.s === true;
109
- // Node-notify base is the view the nodes were last told (#3296): a draft
110
- // preceding this reconcile already moved them to its pending backing at
111
- // setter exit (prev, materialized above), so diffing incoming against the
112
- // committed backing would skip a key the draft changed and the reconcile
113
- // restores — the node would commit the superseded draft value.
114
- const a = f;
115
- adoptPB(e, t, s);
116
- // Shallow adoption: records are slot values — sticky raw-mark the incoming
117
- // set (R41) and never descend; slot notification is the positional diff.
118
- if (u) markRawIngest(t);
119
- if (Array.isArray(l) !== r) {
120
- if (s) notifyFold(e, a, t);
121
- return;
122
- }
123
- if (r) {
124
- const f = l;
125
- const r = t;
126
- // Fused array walk (eager mode): per-index notification rides the same
127
- // loop as the descent (descend first — targetsEqual needs the child's
128
- // re-registration, R9). Length, trailing removed indexes, and any other
129
- // unvisited node keys land in the counted sweep below.
130
- const c = s ? e.n : null;
131
- let d = 0;
132
- if (n && !u) {
133
- // Positional-prefix fast path (legacy keyedMatch-walk parity): while
134
- // rows key-match in place — the steady-state polling shape — descend
135
- // directly with zero staging. The prevByKey map is built only for the
136
- // misaligned remainder, and never at all on aligned ticks.
137
- const l = f.length;
138
- const s = r.length;
139
- let u = false;
140
- let p = 0;
141
- for (const y = Math.min(l, s); p < y; p++) {
142
- const l = r[p];
143
- const s = f[p];
144
- // Routing heuristic only (aligned vs keyed remainder) — both routes
145
- // notify identically and descend() is the one authoritative
146
- // validator, so bare typeof gates suffice here; full isWrappable
147
- // per row was the walk's dominant residual cost.
148
- if (s !== l && !(s !== null && typeof s === "object" && l !== null && typeof l === "object" && sameKey(n(s), n(l)))) break;
149
- // misaligned: fall to the keyed remainder below
150
- // 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, i, o);
152
- if (e.dk !== null && !u && !(l !== null && typeof l === "object" ? targetsEqual(s, l) : isEqual(s, l))) {
153
- bumpDeep(e);
154
- u = true;
155
- }
156
- if (c !== null) {
157
- const e = c[p];
158
- if (e !== undefined) {
159
- d++;
160
- notifyKeyValue(e, p, a[p], l, a, t);
161
- }
162
- }
163
- }
164
- if (e.dk !== null && !u && p < r.length) bumpDeep(e);
165
- const y = p;
166
- // misalignment point (== nlen on aligned ticks)
167
- let w = null;
168
- for (;p < r.length; p++) {
169
- const e = r[p];
170
- // typeof gates route; descend validates (same contract as the prefix).
171
- if (e !== null && typeof e === "object") {
172
- const t = n(e);
173
- let l;
174
- if (t !== undefined) {
175
- if (w === null) {
176
- // Occurrence-aware (re-audit 2, P1-5): duplicate keys queue
177
- // their prev INDICES (rows can themselves be arrays, so index
178
- // queues are the unambiguous encoding — same as buildRowOps)
179
- // and each is consumed ONCE. First-wins would adopt two next
180
- // rows into the SAME prev target while row ops retain two
181
- // separate DOM rows (the second one stale).
182
- w = new Map;
183
- // From structStart, not 0 (re-audit 3, P1-2): prefix-aligned
184
- // rows already adopted their incoming counterparts — re-offering
185
- // them here let a duplicate key adopt a prefix row AGAIN while
186
- // row ops (which correctly window from structStart) retained
187
- // the later occurrence's DOM row against a never-adopted target.
188
- for (let e = y; e < f.length; e++) {
189
- const t = unwrapValue(f[e]);
190
- if (t !== null && typeof t === "object") {
191
- const o = n(t);
192
- if (o === undefined) continue;
193
- const f = w.get(o);
194
- if (f === undefined) w.set(o, e); else if (Array.isArray(f)) f.push(e); else w.set(o, [ f, e ]);
195
- }
196
- }
197
- }
198
- const e = w.get(t);
199
- if (e === undefined) l = undefined; else if (Array.isArray(e)) {
200
- l = unwrapValue(f[e.shift()]);
201
- if (e.length === 1) w.set(t, e[0]);
202
- } else {
203
- l = unwrapValue(f[e]);
204
- w.delete(t);
205
- }
206
- } else {
207
- l = unwrapValue(f[p]);
208
- // keyless item: positional fallback
209
- }
210
- descend(l, e, n, i, o);
211
- }
212
- if (c !== null) {
213
- const e = c[p];
214
- if (e !== undefined) {
215
- d++;
216
- notifyKeyDiff(e, p, a, t, false);
217
- }
218
- }
219
- }
220
- } else {
221
- const l = Math.min(f.length, r.length);
222
- const s = r.length;
223
- let p = false;
224
- for (let y = 0; y < s; y++) {
225
- const s = r[y];
226
- if (!u && y < l && s !== null && typeof s === "object") descend(unwrapValue(f[y]), s, n, i, o);
227
- if (e.dk !== null && !p && !(s !== null && typeof s === "object" ? targetsEqual(f[y], s) : isEqual(f[y], s))) {
228
- bumpDeep(e);
229
- p = true;
230
- }
231
- if (c !== null) {
232
- const e = c[y];
233
- if (e !== undefined) {
234
- d++;
235
- notifyKeyDiff(e, y, a, t, false);
236
- }
237
- }
238
- }
239
- }
240
- if (s) {
241
- if (c !== null && d < e.nc) {
242
- for (const e of Reflect.ownKeys(c)) {
243
- // visited indexes are < nextRows.length; everything else sweeps
244
- const n = typeof e === "string" ? +e : NaN;
245
- if (!(n >= 0 && n < r.length)) notifyKeyDiff(c[e], e, a, t, false);
246
- }
247
- }
248
- notifyFoldTail(e, a, t);
249
- }
250
- return;
251
- } else {
252
- // FUSED adoption walk (eager mode): one pass fetches each key's pair,
253
- // descends, then notifies its node inline — descend runs FIRST so the
254
- // child's re-registration is visible to targetsEqual (identity-preserved
255
- // slots must not notify, R9). This replaces the notifyFold re-walk that
256
- // doubled dbmon's diff cost. for-in covers own enumerable string keys
257
- // with no key-array allocation; symbols get a pass only when present.
258
- const f = s ? e.n : null;
259
- let r = 0;
260
- let c = false;
261
- // The per-key body is inlined on purpose (legacy applyStateFast parity:
262
- // an extracted helper costs a call per key on the hottest object-diff
263
- // site). Reference-identical values early-continue BEFORE any other
264
- // work — sound only with the ownership guard (FINDING-1: an owned
265
- // backing is setter-diverged and must still diff).
266
- for (const s in t) {
267
- const d = t[s];
268
- const p = a[s];
269
- const y = d !== null && typeof d === "object";
270
- if (p === d && (!y || !isOwned(d)) && (f === null || f[s] === undefined || !hasAccessorFlag(f[s]))) {
271
- if (f !== null && f[s] !== undefined) r++;
272
- continue;
273
- }
274
- if (y && !u) descend(unwrapValue(l[s]), d, n, i, o);
275
- // Deep-witness (dk): value changes must notify even with NO per-key
276
- // node — deep() subscribes one node per record. Checked after descend
277
- // so in-place adoptions (same logical slot) don't bump; child records
278
- // carry their own witness. One flag + null check when unused.
279
- if (e.dk !== null && !c && !(y ? targetsEqual(p, d) : isEqual(p, d))) {
280
- bumpDeep(e);
281
- c = true;
282
- }
283
- if (f !== null) {
284
- const e = f[s];
285
- if (e !== undefined) {
286
- r++;
287
- notifyKeyValue(e, s, p, d, a, t);
288
- }
289
- }
290
- }
291
- const d = Object.getOwnPropertySymbols(t);
292
- for (let e = 0; e < d.length; e++) {
293
- const s = d[e];
294
- if (s === $OWNER) continue;
295
- const c = t[s];
296
- if (!u && c !== null && typeof c === "object") descend(unwrapValue(l[s]), c, n, i, o);
297
- if (f !== null) {
298
- const e = f[s];
299
- if (e !== undefined) {
300
- r++;
301
- notifyKeyValue(e, s, a[s], c, a, t);
302
- }
303
- }
304
- }
305
- if (s) {
306
- // Deleted-key nodes (in the map but absent from incoming) — counted
307
- // fast-out: when every node was visited, skip the sweep entirely.
308
- if (f !== null && r < e.nc) {
309
- for (const e of Reflect.ownKeys(f)) {
310
- if (!hasOwnP.call(t, e)) notifyKeyDiff(f[e], e, a, t, false);
311
- }
312
- }
313
- notifyFoldTail(e, a, t);
314
- }
315
- return;
316
- }
317
- }
318
-
319
- const hasOwnP = Object.prototype.hasOwnProperty;
320
-
321
- /** Setter-channel row ops (the fold site calls this for array targets with
322
- * ops consumers): structural mutation through the setter — push/splice/index
323
- /** Key equality for EVERY key comparison in this module (re-audit 2, P1-5):
324
- * SameValueZero, matching the adoption window's Map-based matcher — NaN keys
325
- * are equal to themselves, so aligned NaN rows stay aligned in the prefix
326
- * walk instead of forever misaligning. */ function sameKey(e, t) {
327
- return e === t || e !== e && t !== t;
328
- }
329
-
330
- function descend(e, t, n, o, f = false) {
331
- if (e === null || typeof e !== "object" || t === null || typeof t !== "object") return;
332
- // Lookup FIRST: a hit implies pv was wrappable and never raw-marked (only
333
- // wrappables acquire targets; rawValues never wrap) — one WeakMap get
334
- // replaces isWrappable(pv) + isRawValue(pv), and a miss prunes untracked
335
- // subtrees before any further checks.
336
- const i = lookupTarget(e, o);
337
- if (i === undefined) return;
338
- // nothing proxied below this pair
339
- // The NEW side still validates fully: a frozen/platform/markRaw'd incoming
340
- // value is a leaf for reconcile — replaced by reference, never recursed
341
- // into (R42); the parent's slot notification covers the change.
342
- if (!isWrappable(t)) return;
343
- if (rawValuesUsed && isRawValue(t)) return;
344
- t = unwrapValue(t);
345
- // Kind change replaces wholesale, never merges (R10): a target's carrier
346
- // class (array vs object) is fixed at creation, so the slot detaches and a
347
- // fresh proxy of the right kind wraps the incoming value on next read.
348
- if (Array.isArray(e) !== Array.isArray(t)) return;
349
- if (n) {
350
- const o = n(e);
351
- const f = n(t);
352
- // Key mismatch detaches: the slot takes the new entity; the old proxy
353
- // keeps its (old) backing and a fresh proxy wraps the new value on read.
354
- // SameValueZero (re-audit 2, P1-5): NaN keys are self-equal — strict
355
- // inequality detached every NaN-keyed slot on every tick while the
356
- // Map-based row-ops matcher retained its DOM row (stale forever).
357
- if (o !== undefined && f !== undefined && !sameKey(o, f)) return;
358
- }
359
- // Reachability pruning (§6d) is MODE-dependent, both pinned:
360
- // - keyed matching descends only where subscriptions exist at/below (`d`) —
361
- // captured-but-unobserved proxies deliberately detach and go stale
362
- // (recon-snap R18; subscribing is what buys liveness);
363
- // - positional (key: null) pairing preserves slot identity unconditionally
364
- // (recon-snap R8 — the fixed-shape dashboard pattern).
365
- // Projection merges (replace mode) preserve key-matched identity
366
- // UNCONDITIONALLY (proj R6: the slot keeps its proxy without needing a
367
- // subscriber below); plain keyed reconcile detaches unobserved captures
368
- // (recon-snap R18 — staleness is the pinned pruning contract).
369
- if (!f && n !== null && !i.d) return;
370
- applyAdopt(i, t, n, f);
371
- }
372
-
373
- export { reconcileNextState, sameKey };