@solidjs/signals 2.0.0-beta.18 → 2.0.0-beta.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.js +2743 -694
- package/dist/node.cjs +6419 -4234
- package/dist/prod/affects.js +222 -0
- package/dist/prod/boundaries.js +568 -0
- package/dist/prod/core/action.js +83 -0
- package/dist/prod/core/async.js +394 -0
- package/dist/prod/core/constants.js +78 -0
- package/dist/prod/core/context.js +67 -0
- package/dist/prod/core/core.js +772 -0
- package/dist/prod/core/dev.js +3 -0
- package/dist/prod/core/effect.js +145 -0
- package/dist/prod/core/error.js +59 -0
- package/dist/prod/core/external.js +98 -0
- package/dist/prod/core/graph.js +91 -0
- package/dist/prod/core/heap.js +132 -0
- package/dist/prod/core/invariants.js +42 -0
- package/dist/prod/core/lanes.js +130 -0
- package/dist/prod/core/optimistic.js +265 -0
- package/dist/prod/core/owner.js +293 -0
- package/dist/prod/core/scheduler.js +689 -0
- package/dist/prod/core/verdict.js +293 -0
- package/dist/prod/index.js +45 -0
- package/dist/prod/map.js +264 -0
- package/dist/prod/signals.js +389 -0
- package/dist/prod/store/optimistic.js +149 -0
- package/dist/prod/store/projection.js +184 -0
- package/dist/prod/store/reconcile.js +392 -0
- package/dist/prod/store/store.js +739 -0
- package/dist/prod/store/storePath.js +103 -0
- package/dist/prod/store/utils.js +297 -0
- package/dist/types/affects.d.ts +1 -1
- package/dist/types/boundaries.d.ts +6 -6
- package/dist/types/core/async.d.ts +4 -38
- package/dist/types/core/core.d.ts +12 -78
- package/dist/types/core/external.d.ts +0 -30
- package/dist/types/core/heap.d.ts +8 -0
- package/dist/types/core/index.d.ts +3 -2
- package/dist/types/core/optimistic.d.ts +6 -0
- package/dist/types/core/owner.d.ts +8 -0
- package/dist/types/core/scheduler.d.ts +39 -34
- package/dist/types/core/verdict.d.ts +2 -0
- package/dist/types/store/projection.d.ts +0 -1
- package/dist/types-cjs/affects.d.cts +1 -1
- package/dist/types-cjs/boundaries.d.cts +6 -6
- package/dist/types-cjs/core/async.d.cts +4 -38
- package/dist/types-cjs/core/core.d.cts +12 -78
- package/dist/types-cjs/core/external.d.cts +0 -30
- package/dist/types-cjs/core/heap.d.cts +8 -0
- package/dist/types-cjs/core/index.d.cts +3 -2
- package/dist/types-cjs/core/optimistic.d.cts +6 -0
- package/dist/types-cjs/core/owner.d.cts +8 -0
- package/dist/types-cjs/core/scheduler.d.cts +39 -34
- package/dist/types-cjs/core/verdict.d.cts +2 -0
- package/dist/types-cjs/store/projection.d.cts +0 -1
- package/package.json +8 -6
- package/dist/prod.js +0 -4637
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { computed } from "../core/core.js";
|
|
2
|
+
|
|
3
|
+
import { getOwner } from "../core/owner.js";
|
|
4
|
+
|
|
5
|
+
import { setProjectionWriteActive } 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 } from "../core/constants.js";
|
|
14
|
+
|
|
15
|
+
import { reconcile } from "./reconcile.js";
|
|
16
|
+
|
|
17
|
+
import { storeSetter, $TARGET, STORE_WRAP, createStoreProxy, storeTraps, setWriteOverride, STORE_LOOKUP, STORE_FIREWALL } from "./store.js";
|
|
18
|
+
|
|
19
|
+
function createProjectionInternal(e, r, t) {
|
|
20
|
+
let o;
|
|
21
|
+
const i = new WeakMap;
|
|
22
|
+
const wrapper = e => {
|
|
23
|
+
e[STORE_WRAP] = wrapProjection;
|
|
24
|
+
e[STORE_LOOKUP] = i;
|
|
25
|
+
Object.defineProperty(e, STORE_FIREWALL, {
|
|
26
|
+
get() {
|
|
27
|
+
return o;
|
|
28
|
+
},
|
|
29
|
+
configurable: true
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
const wrapProjection = e => {
|
|
33
|
+
if (i.has(e)) return i.get(e);
|
|
34
|
+
if (e[$TARGET]?.[STORE_WRAP] === wrapProjection) return e;
|
|
35
|
+
const r = createStoreProxy(e, storeTraps, wrapper);
|
|
36
|
+
i.set(e, r);
|
|
37
|
+
return r;
|
|
38
|
+
};
|
|
39
|
+
const n = wrapProjection(r);
|
|
40
|
+
o = computed(() => {
|
|
41
|
+
if (!o) o = getOwner();
|
|
42
|
+
runProjectionComputed(n, e, t?.key || "id");
|
|
43
|
+
}, undefined);
|
|
44
|
+
o.U &= ~CONFIG_AUTO_DISPOSE;
|
|
45
|
+
return {
|
|
46
|
+
store: n,
|
|
47
|
+
node: o
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates a derived (projected) store. Like `createMemo` but for stores: the
|
|
53
|
+
* derive function receives a mutable draft and either mutates it in place
|
|
54
|
+
* (canonical) or returns a new value. Either way the result is reconciled
|
|
55
|
+
* against the previous draft by `options.key` (default `"id"`), so surviving
|
|
56
|
+
* items keep their proxy identity — only added/removed items are
|
|
57
|
+
* created/disposed.
|
|
58
|
+
*
|
|
59
|
+
* Returns the projected store directly (no setter — reads only).
|
|
60
|
+
*
|
|
61
|
+
* Use this when you want the structural-sharing / per-property tracking
|
|
62
|
+
* behaviour of a store on top of a derived computation. For simple read-only
|
|
63
|
+
* derivations, `createMemo` is lighter.
|
|
64
|
+
*
|
|
65
|
+
* @param fn receives the current draft; mutate it in place or return new
|
|
66
|
+
* data. Return is convenient for filter/derive shapes where mutation is
|
|
67
|
+
* awkward.
|
|
68
|
+
* @param seed the backing store value to wrap and reconcile into
|
|
69
|
+
* @param options `ProjectionOptions` — `name`, `key`. `key` defaults to
|
|
70
|
+
* `"id"`; specify it only when your data uses a different identity field
|
|
71
|
+
* (e.g. `{ key: "uuid" }` or `{ key: u => u.slug }`).
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // Mutation form — update individual fields on the draft.
|
|
76
|
+
* const summary = createProjection<{ total: number; active: number }>(
|
|
77
|
+
* draft => {
|
|
78
|
+
* draft.total = users().length;
|
|
79
|
+
* draft.active = users().filter(u => u.active).length;
|
|
80
|
+
* },
|
|
81
|
+
* { total: 0, active: 0 }
|
|
82
|
+
* );
|
|
83
|
+
*
|
|
84
|
+
* // Return form — produce a derived collection. Reconciled by `id` so each
|
|
85
|
+
* // surviving user keeps the same store identity across recomputes.
|
|
86
|
+
* const activeUsers = createProjection<User[]>(
|
|
87
|
+
* () => allUsers().filter(u => u.active),
|
|
88
|
+
* []
|
|
89
|
+
* );
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @see {@link https://github.com/solidjs/x-reactivity#createprojection}
|
|
93
|
+
*/ function createProjection(e, r, t) {
|
|
94
|
+
return createProjectionInternal(e, r, t).store;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Shared projection computed body used by both `createProjection` and the derived
|
|
99
|
+
* form of `createOptimisticStore`. Encapsulates the write-trap draft, `storeSetter`
|
|
100
|
+
* wrapping, the `handleAsync` subscription with a setter callback, and the commit
|
|
101
|
+
* path (which must always go through `storeSetter` so the `writeOnly` guard is
|
|
102
|
+
* engaged during `reconcile`'s property reads).
|
|
103
|
+
*
|
|
104
|
+
* `wrapCommit` is invoked for every commit (sync return and each async yield) and
|
|
105
|
+
* lets callers layer extra context around the write — e.g. the optimistic store
|
|
106
|
+
* re-enters `setProjectionWriteActive` so reconciles target `STORE_OVERRIDE`
|
|
107
|
+
* instead of `STORE_OPTIMISTIC_OVERRIDE` even when an async yield fires outside
|
|
108
|
+
* the outer `setProjectionWriteActive` scope.
|
|
109
|
+
*/ function runProjectionComputed(e, r, t, o, i) {
|
|
110
|
+
const n = getOwner();
|
|
111
|
+
let c = false;
|
|
112
|
+
let s;
|
|
113
|
+
const u = new Proxy(e, createWriteTraps(() => !c || n.Ae === s, i));
|
|
114
|
+
storeSetter(u, i => {
|
|
115
|
+
s = r(i);
|
|
116
|
+
c = true;
|
|
117
|
+
const commit = r => {
|
|
118
|
+
if (r === i || r === undefined) return;
|
|
119
|
+
const write = () => storeSetter(e, reconcile(r, t));
|
|
120
|
+
o ? o(write) : write();
|
|
121
|
+
};
|
|
122
|
+
commit(handleAsync(n, s, commit));
|
|
123
|
+
});
|
|
124
|
+
return n;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createWriteTraps(e, r) {
|
|
128
|
+
const t = {
|
|
129
|
+
get(e, r) {
|
|
130
|
+
let o;
|
|
131
|
+
setWriteOverride(true);
|
|
132
|
+
setProjectionWriteActive(true);
|
|
133
|
+
try {
|
|
134
|
+
o = e[r];
|
|
135
|
+
} finally {
|
|
136
|
+
setWriteOverride(false);
|
|
137
|
+
setProjectionWriteActive(false);
|
|
138
|
+
}
|
|
139
|
+
if (r === $TARGET) return o;
|
|
140
|
+
return typeof o === "object" && o !== null ? new Proxy(o, t) : o;
|
|
141
|
+
},
|
|
142
|
+
has(e, r) {
|
|
143
|
+
let t;
|
|
144
|
+
setWriteOverride(true);
|
|
145
|
+
setProjectionWriteActive(true);
|
|
146
|
+
try {
|
|
147
|
+
t = r in e;
|
|
148
|
+
} finally {
|
|
149
|
+
setWriteOverride(false);
|
|
150
|
+
setProjectionWriteActive(false);
|
|
151
|
+
}
|
|
152
|
+
return t;
|
|
153
|
+
},
|
|
154
|
+
set(t, o, i) {
|
|
155
|
+
if (e && !e()) return true;
|
|
156
|
+
setWriteOverride(true);
|
|
157
|
+
setProjectionWriteActive(true);
|
|
158
|
+
try {
|
|
159
|
+
t[o] = i;
|
|
160
|
+
r?.();
|
|
161
|
+
} finally {
|
|
162
|
+
setWriteOverride(false);
|
|
163
|
+
setProjectionWriteActive(false);
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
},
|
|
167
|
+
deleteProperty(t, o) {
|
|
168
|
+
if (e && !e()) return true;
|
|
169
|
+
setWriteOverride(true);
|
|
170
|
+
setProjectionWriteActive(true);
|
|
171
|
+
try {
|
|
172
|
+
delete t[o];
|
|
173
|
+
r?.();
|
|
174
|
+
} finally {
|
|
175
|
+
setWriteOverride(false);
|
|
176
|
+
setProjectionWriteActive(false);
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return t;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export { createProjection, createProjectionInternal, createWriteTraps, runProjectionComputed };
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { setSignal, untrack } from "../core/core.js";
|
|
2
|
+
|
|
3
|
+
import "../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, STORE_OVERRIDE, STORE_OPTIMISTIC_OVERRIDE, STORE_VALUE, STORE_NODE, storeLookup, STORE_LOOKUP, $PROXY, isWrappable, wrap, notifySelf, $TRACK, STORE_HAS, getKeys, $DELETED, symbolKeyedRecords } from "./store.js";
|
|
12
|
+
|
|
13
|
+
// Enumerate a node record's keys. Keeps the common string-key path on the
|
|
14
|
+
// `Object.keys` fast path; only records currently holding a user symbol node
|
|
15
|
+
// (marked by `getNode`) pay for symbol enumeration. `$TRACK` is the only
|
|
16
|
+
// internal symbol a node record can hold and callers handle it separately.
|
|
17
|
+
function nodeKeys(e) {
|
|
18
|
+
const t = Object.keys(e);
|
|
19
|
+
if (symbolKeyedRecords.has(e)) {
|
|
20
|
+
const r = Object.getOwnPropertySymbols(e);
|
|
21
|
+
for (let e = 0, n = r.length; e < n; e++) {
|
|
22
|
+
if (r[e] !== $TRACK) t.push(r[e]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return t;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function unwrap(e) {
|
|
29
|
+
// Primitives can't be store proxies; skip the symbol lookups (which box the
|
|
30
|
+
// primitive) for the common leaf case.
|
|
31
|
+
if (e === null || typeof e !== "object") return e;
|
|
32
|
+
return e[$TARGET]?.[STORE_VALUE] ?? e;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getOverrideValue(e, t, r, n) {
|
|
36
|
+
if (n && r in n) return n[r];
|
|
37
|
+
return t && r in t ? t[r] : e[r];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Append `o`'s *enumerable* own symbol keys from a pre-fetched symbol list.
|
|
41
|
+
function addEnumSymbols(e, t, r) {
|
|
42
|
+
for (let n = 0, a = t.length; n < a; n++) {
|
|
43
|
+
if (Object.prototype.propertyIsEnumerable.call(e, t[n])) r.add(t[n]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getAllKeys(e, t, r) {
|
|
48
|
+
const n = getKeys(e, t);
|
|
49
|
+
const a = Object.keys(r);
|
|
50
|
+
// `value` can be a wrapped store (store-in-store) whose ownKeys trap tracks;
|
|
51
|
+
// mirror `getKeys` and enumerate its symbols untracked in that case.
|
|
52
|
+
const i = e[$TARGET] ? untrack(() => Object.getOwnPropertySymbols(e)) : Object.getOwnPropertySymbols(e);
|
|
53
|
+
const o = Object.getOwnPropertySymbols(r);
|
|
54
|
+
// Symbol-free diff (the overwhelmingly common case) stays on the exact
|
|
55
|
+
// pre-#2851 path, including the identical-key-sets fast path from #2756.
|
|
56
|
+
if (i.length === 0 && o.length === 0) {
|
|
57
|
+
if (n.length === a.length) {
|
|
58
|
+
let e = true;
|
|
59
|
+
for (let t = 0; t < n.length; t++) {
|
|
60
|
+
if (n[t] !== a[t]) {
|
|
61
|
+
e = false;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (e) return n;
|
|
66
|
+
}
|
|
67
|
+
const e = new Set(n);
|
|
68
|
+
for (let t = 0; t < a.length; t++) e.add(a[t]);
|
|
69
|
+
return Array.from(e);
|
|
70
|
+
}
|
|
71
|
+
// Symbol-aware diff (#2851): base symbols join the set, then override
|
|
72
|
+
// adds/deletes are re-applied so a `$DELETED` symbol stays deleted, then
|
|
73
|
+
// `next`'s keys (a key present in next is never deleted by the diff).
|
|
74
|
+
const l = new Set(n);
|
|
75
|
+
addEnumSymbols(e, i, l);
|
|
76
|
+
if (t) {
|
|
77
|
+
for (const e of Reflect.ownKeys(t)) {
|
|
78
|
+
t[e] === $DELETED ? l.delete(e) : l.add(e);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (let e = 0; e < a.length; e++) l.add(a[e]);
|
|
82
|
+
addEnumSymbols(r, o, l);
|
|
83
|
+
return Array.from(l);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Array entries can be `null`/`undefined`/primitives, not just keyed objects.
|
|
87
|
+
// These helpers keep the keyed paths from passing a non-object to `keyFn` (which
|
|
88
|
+
// assumes an object) or to `wrap()` (which assumes a wrappable value).
|
|
89
|
+
function wrapValue(e, t) {
|
|
90
|
+
return isWrappable(e) ? wrap(e, t) : e;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function itemKey(e, t) {
|
|
94
|
+
return isWrappable(e) ? t(e) : e;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function keyedMatch(e, t, r) {
|
|
98
|
+
return e === t || isWrappable(e) && isWrappable(t) && r(e) === r(t);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Array reconciliation updates the slots it visits, then swaps STORE_VALUE.
|
|
102
|
+
// Previously tracked keys that are absent from `next` still need invalidating,
|
|
103
|
+
// and `in` dependencies should follow the new value's membership. Use
|
|
104
|
+
// membership rather than length arithmetic so sparse arrays and named array
|
|
105
|
+
// props behave like normal property reads.
|
|
106
|
+
function syncArrayNodeMembership(e, t) {
|
|
107
|
+
let r = e[STORE_NODE];
|
|
108
|
+
if (r) {
|
|
109
|
+
const e = nodeKeys(r);
|
|
110
|
+
for (let n = 0, a = e.length; n < a; n++) {
|
|
111
|
+
const a = e[n];
|
|
112
|
+
a in t || setSignal(r[a], undefined);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (r = e[STORE_HAS]) {
|
|
116
|
+
const e = nodeKeys(r);
|
|
117
|
+
for (let n = 0, a = e.length; n < a; n++) {
|
|
118
|
+
const a = e[n];
|
|
119
|
+
setSignal(r[a], a in t);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Reconcile a single array slot: recurse into a wrappable pair, otherwise replace
|
|
125
|
+
// the node's value outright (covers object→primitive and primitive→object).
|
|
126
|
+
function applyArrayItem(e, t, r, n, a) {
|
|
127
|
+
if (isWrappable(e) && isWrappable(t)) {
|
|
128
|
+
const i = wrap(t, r);
|
|
129
|
+
n && setSignal(n, i);
|
|
130
|
+
applyState(e, i, a);
|
|
131
|
+
} else n && setSignal(n, wrapValue(e, r));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Dispatcher: every applyState call (including recursion) checks for the
|
|
135
|
+
// presence of override / optimistic-override slots once and routes to the
|
|
136
|
+
// appropriate body. The fast body never calls `getOverrideValue` and never
|
|
137
|
+
// branches on a `fastPath` boolean, so V8 sees a tighter, more inlinable
|
|
138
|
+
// shape for the overwhelmingly common case of plain stores.
|
|
139
|
+
function applyState(e, t, r) {
|
|
140
|
+
// Array items and root calls can pass a store proxy as `next`; normalize to
|
|
141
|
+
// its raw value or the swap would set a store's STORE_VALUE to its own proxy.
|
|
142
|
+
e = unwrap(e);
|
|
143
|
+
const n = t?.[$TARGET];
|
|
144
|
+
if (!n) return;
|
|
145
|
+
if (n[STORE_OVERRIDE] || n[STORE_OPTIMISTIC_OVERRIDE]) {
|
|
146
|
+
applyStateSlow(e, n, r);
|
|
147
|
+
} else {
|
|
148
|
+
applyStateFast(e, n, r);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function applyStateFast(e, t, r) {
|
|
153
|
+
const n = t[STORE_VALUE];
|
|
154
|
+
if (e === n) return;
|
|
155
|
+
const a = t[STORE_NODE];
|
|
156
|
+
// swap
|
|
157
|
+
(t[STORE_LOOKUP] || storeLookup).set(e, t[$PROXY]);
|
|
158
|
+
t[STORE_VALUE] = e;
|
|
159
|
+
// merge
|
|
160
|
+
if (Array.isArray(n)) {
|
|
161
|
+
let i = false;
|
|
162
|
+
const o = n.length;
|
|
163
|
+
if (e.length && o && isWrappable(e[0]) && r(e[0]) != null) {
|
|
164
|
+
let l, s, p, f, c, u, y, S;
|
|
165
|
+
for (p = 0, f = Math.min(o, e.length); p < f && keyedMatch(u = n[p], e[p], r); p++) {
|
|
166
|
+
isWrappable(u) && isWrappable(e[p]) && applyState(e[p], wrap(u, t), r);
|
|
167
|
+
}
|
|
168
|
+
const E = new Array(e.length), O = new Map;
|
|
169
|
+
for (f = o - 1, c = e.length - 1; f >= p && c >= p && keyedMatch(u = n[f], e[c], r); f--,
|
|
170
|
+
c--) {
|
|
171
|
+
E[c] = u;
|
|
172
|
+
}
|
|
173
|
+
if (p > c || p > f) {
|
|
174
|
+
for (s = p; s <= c; s++) {
|
|
175
|
+
i = true;
|
|
176
|
+
a?.[s] && setSignal(a[s], wrapValue(e[s], t));
|
|
177
|
+
}
|
|
178
|
+
for (;s < e.length; s++) {
|
|
179
|
+
i = true;
|
|
180
|
+
applyArrayItem(e[s], E[s], t, a?.[s], r);
|
|
181
|
+
}
|
|
182
|
+
syncArrayNodeMembership(t, e);
|
|
183
|
+
(i || o !== e.length) && notifySelf(t);
|
|
184
|
+
o !== e.length && a?.length && setSignal(a.length, e.length);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
y = new Array(c + 1);
|
|
188
|
+
for (s = c; s >= p; s--) {
|
|
189
|
+
u = e[s];
|
|
190
|
+
S = itemKey(u, r);
|
|
191
|
+
l = O.get(S);
|
|
192
|
+
y[s] = l === undefined ? -1 : l;
|
|
193
|
+
O.set(S, s);
|
|
194
|
+
}
|
|
195
|
+
for (l = p; l <= f; l++) {
|
|
196
|
+
u = n[l];
|
|
197
|
+
S = itemKey(u, r);
|
|
198
|
+
s = O.get(S);
|
|
199
|
+
if (s !== undefined && s !== -1) {
|
|
200
|
+
E[s] = u;
|
|
201
|
+
s = y[s];
|
|
202
|
+
O.set(S, s);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (s = p; s < e.length; s++) {
|
|
206
|
+
if (s in E) {
|
|
207
|
+
applyArrayItem(e[s], E[s], t, a?.[s], r);
|
|
208
|
+
} else a?.[s] && setSignal(a[s], wrapValue(e[s], t));
|
|
209
|
+
}
|
|
210
|
+
if (p < e.length) i = true;
|
|
211
|
+
} else if (e.length) {
|
|
212
|
+
for (let o = 0, l = e.length; o < l; o++) {
|
|
213
|
+
const l = n[o];
|
|
214
|
+
if (isWrappable(l) && isWrappable(e[o])) applyState(e[o], wrap(l, t), r); else {
|
|
215
|
+
if (l !== e[o]) i = true;
|
|
216
|
+
a?.[o] && setSignal(a[o], wrapValue(e[o], t));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
syncArrayNodeMembership(t, e);
|
|
221
|
+
if (o !== e.length) {
|
|
222
|
+
i = true;
|
|
223
|
+
a?.length && setSignal(a.length, e.length);
|
|
224
|
+
}
|
|
225
|
+
i && notifySelf(t);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
// values
|
|
229
|
+
let i = t[STORE_NODE];
|
|
230
|
+
if (i) {
|
|
231
|
+
const a = i[$TRACK];
|
|
232
|
+
const o = a ? getAllKeys(n, undefined, e) : nodeKeys(i);
|
|
233
|
+
for (let l = 0, s = o.length; l < s; l++) {
|
|
234
|
+
const s = o[l];
|
|
235
|
+
const p = i[s];
|
|
236
|
+
const f = unwrap(n[s]);
|
|
237
|
+
let c = unwrap(e[s]);
|
|
238
|
+
if (f === c) continue;
|
|
239
|
+
if (!f || !isWrappable(f) || !isWrappable(c) || Array.isArray(f) !== Array.isArray(c) || r(f) != null && r(f) !== r(c)) {
|
|
240
|
+
a && setSignal(a, void 0);
|
|
241
|
+
p && setSignal(p, isWrappable(c) ? wrap(c, t) : c);
|
|
242
|
+
} else applyState(c, wrap(f, t), r);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// has
|
|
246
|
+
if (i = t[STORE_HAS]) {
|
|
247
|
+
const t = nodeKeys(i);
|
|
248
|
+
for (let r = 0, n = t.length; r < n; r++) {
|
|
249
|
+
const n = t[r];
|
|
250
|
+
setSignal(i[n], n in e);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function applyStateSlow(e, t, r) {
|
|
256
|
+
const n = t[STORE_VALUE];
|
|
257
|
+
const a = t[STORE_OVERRIDE];
|
|
258
|
+
const i = t[STORE_OPTIMISTIC_OVERRIDE];
|
|
259
|
+
let o = t[STORE_NODE];
|
|
260
|
+
// swap
|
|
261
|
+
(t[STORE_LOOKUP] || storeLookup).set(e, t[$PROXY]);
|
|
262
|
+
t[STORE_VALUE] = e;
|
|
263
|
+
t[STORE_OVERRIDE] = undefined;
|
|
264
|
+
// merge
|
|
265
|
+
if (Array.isArray(n)) {
|
|
266
|
+
let l = false;
|
|
267
|
+
const s = getOverrideValue(n, a, "length", i);
|
|
268
|
+
if (e.length && s && isWrappable(e[0]) && r(e[0]) != null) {
|
|
269
|
+
let p, f, c, u, y, S, E, O;
|
|
270
|
+
for (c = 0, u = Math.min(s, e.length); c < u && keyedMatch(S = getOverrideValue(n, a, c, i), e[c], r); c++) {
|
|
271
|
+
isWrappable(S) && isWrappable(e[c]) && applyState(e[c], wrap(S, t), r);
|
|
272
|
+
}
|
|
273
|
+
const d = new Array(e.length), R = new Map;
|
|
274
|
+
for (u = s - 1, y = e.length - 1; u >= c && y >= c && keyedMatch(S = getOverrideValue(n, a, u, i), e[y], r); u--,
|
|
275
|
+
y--) {
|
|
276
|
+
d[y] = S;
|
|
277
|
+
}
|
|
278
|
+
if (c > y || c > u) {
|
|
279
|
+
for (f = c; f <= y; f++) {
|
|
280
|
+
l = true;
|
|
281
|
+
o?.[f] && setSignal(o[f], wrapValue(e[f], t));
|
|
282
|
+
}
|
|
283
|
+
for (;f < e.length; f++) {
|
|
284
|
+
l = true;
|
|
285
|
+
applyArrayItem(e[f], d[f], t, o?.[f], r);
|
|
286
|
+
}
|
|
287
|
+
const n = e.length;
|
|
288
|
+
syncArrayNodeMembership(t, e);
|
|
289
|
+
(l || s !== n) && notifySelf(t);
|
|
290
|
+
s !== n && o?.length && setSignal(o.length, n);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
E = new Array(y + 1);
|
|
294
|
+
for (f = y; f >= c; f--) {
|
|
295
|
+
S = e[f];
|
|
296
|
+
O = itemKey(S, r);
|
|
297
|
+
p = R.get(O);
|
|
298
|
+
E[f] = p === undefined ? -1 : p;
|
|
299
|
+
R.set(O, f);
|
|
300
|
+
}
|
|
301
|
+
for (p = c; p <= u; p++) {
|
|
302
|
+
S = getOverrideValue(n, a, p, i);
|
|
303
|
+
O = itemKey(S, r);
|
|
304
|
+
f = R.get(O);
|
|
305
|
+
if (f !== undefined && f !== -1) {
|
|
306
|
+
d[f] = S;
|
|
307
|
+
f = E[f];
|
|
308
|
+
R.set(O, f);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
for (f = c; f < e.length; f++) {
|
|
312
|
+
if (f in d) {
|
|
313
|
+
applyArrayItem(e[f], d[f], t, o?.[f], r);
|
|
314
|
+
} else o?.[f] && setSignal(o[f], wrapValue(e[f], t));
|
|
315
|
+
}
|
|
316
|
+
if (c < e.length) l = true;
|
|
317
|
+
} else if (e.length) {
|
|
318
|
+
for (let s = 0, p = e.length; s < p; s++) {
|
|
319
|
+
const p = getOverrideValue(n, a, s, i);
|
|
320
|
+
if (isWrappable(p) && isWrappable(e[s])) applyState(e[s], wrap(p, t), r); else {
|
|
321
|
+
if (p !== e[s]) l = true;
|
|
322
|
+
o?.[s] && setSignal(o[s], wrapValue(e[s], t));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const p = e.length;
|
|
327
|
+
syncArrayNodeMembership(t, e);
|
|
328
|
+
if (s !== p) {
|
|
329
|
+
l = true;
|
|
330
|
+
o?.length && setSignal(o.length, p);
|
|
331
|
+
}
|
|
332
|
+
l && notifySelf(t);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
// values
|
|
336
|
+
if (o) {
|
|
337
|
+
const l = o[$TRACK];
|
|
338
|
+
const s = l ? getAllKeys(n, a, e) : nodeKeys(o);
|
|
339
|
+
for (let p = 0, f = s.length; p < f; p++) {
|
|
340
|
+
const f = s[p];
|
|
341
|
+
const c = o[f];
|
|
342
|
+
const u = unwrap(getOverrideValue(n, a, f, i));
|
|
343
|
+
let y = unwrap(e[f]);
|
|
344
|
+
if (u === y) continue;
|
|
345
|
+
if (!u || !isWrappable(u) || !isWrappable(y) || Array.isArray(u) !== Array.isArray(y) || r(u) != null && r(u) !== r(y)) {
|
|
346
|
+
l && setSignal(l, void 0);
|
|
347
|
+
c && setSignal(c, isWrappable(y) ? wrap(y, t) : y);
|
|
348
|
+
} else applyState(y, wrap(u, t), r);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// has
|
|
352
|
+
if (o = t[STORE_HAS]) {
|
|
353
|
+
const t = nodeKeys(o);
|
|
354
|
+
for (let r = 0, n = t.length; r < n; r++) {
|
|
355
|
+
const n = t[r];
|
|
356
|
+
setSignal(o[n], n in e);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Returns a draft-mutating function that smart-merges `value` into a store,
|
|
363
|
+
* preserving the identity of items whose `key` field matches between old and
|
|
364
|
+
* new states. Useful when applying server payloads or full-replacement data
|
|
365
|
+
* onto an existing store without losing fine-grained reactivity.
|
|
366
|
+
*
|
|
367
|
+
* Items with the same key are updated in place (only changed properties
|
|
368
|
+
* trigger updates). Items added or removed update the corresponding signals.
|
|
369
|
+
*
|
|
370
|
+
* @param value the next state to merge in
|
|
371
|
+
* @param key property name (string) or extractor function for stable identity
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* const [todos, setTodos] = createStore<Todo[]>([]);
|
|
376
|
+
*
|
|
377
|
+
* async function refresh() {
|
|
378
|
+
* const fresh = await api.getTodos();
|
|
379
|
+
* setTodos(reconcile(fresh, "id")); // diff-merge by `id`
|
|
380
|
+
* }
|
|
381
|
+
* ```
|
|
382
|
+
*/ function reconcile(e, t) {
|
|
383
|
+
return r => {
|
|
384
|
+
if (r == null) throw new Error("");
|
|
385
|
+
const n = typeof t === "string" ? e => e[t] : t;
|
|
386
|
+
const a = n(r);
|
|
387
|
+
if (a !== undefined && n(e) !== a) throw new Error("");
|
|
388
|
+
applyState(e, r, n);
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export { reconcile };
|