mutts 1.0.9 → 1.0.10
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/README.md +58 -33
- package/dist/browser.cjs +1371 -1206
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +2 -1392
- package/dist/browser.dev.cjs +110 -0
- package/dist/browser.dev.cjs.map +1 -0
- package/dist/browser.dev.d.ts +2 -0
- package/dist/browser.dev.esm.js +5 -0
- package/dist/browser.dev.esm.js.map +1 -0
- package/dist/browser.esm.js +6 -87
- package/dist/browser.esm.js.map +1 -1
- package/dist/chunks/{async-browser-lvzLOCgk.cjs → async-browser-BU_IfxYD.cjs} +4 -85
- package/dist/chunks/async-browser-BU_IfxYD.cjs.map +1 -0
- package/dist/chunks/{async-node-C3DeIb0y.cjs → async-node-3PrbVAbB.cjs} +3 -1
- package/dist/chunks/async-node-3PrbVAbB.cjs.map +1 -0
- package/dist/chunks/{index-2vea86wD.esm.js → index-CaaQQlPJ.esm.js} +1116 -953
- package/dist/chunks/index-CaaQQlPJ.esm.js.map +1 -0
- package/dist/chunks/node-nKJBk8iJ.esm.js +96 -0
- package/dist/chunks/node-nKJBk8iJ.esm.js.map +1 -0
- package/dist/chunks/{index-VTO-b2vR.cjs → proxy-Dtg-bJ3T.cjs} +2285 -2983
- package/dist/chunks/proxy-Dtg-bJ3T.cjs.map +1 -0
- package/dist/chunks/{index-CtA2AWl3.esm.js → proxy-r7lARftl.esm.js} +2273 -2948
- package/dist/chunks/proxy-r7lARftl.esm.js.map +1 -0
- package/dist/debug.cjs +973 -28
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.d.ts +36 -37
- package/dist/debug.esm.js +949 -1
- package/dist/debug.esm.js.map +1 -1
- package/dist/devtools/manifest.json +1 -1
- package/dist/devtools/panel.html +1 -1
- package/dist/devtools/panel.js +107 -94
- package/dist/devtools/panel.js.map +1 -1
- package/dist/index.d.ts +1447 -1
- package/dist/mutts.umd.js +7167 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/node.cjs +62 -60
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.dev.cjs +110 -0
- package/dist/node.dev.cjs.map +1 -0
- package/dist/node.dev.d.ts +2 -0
- package/dist/node.dev.esm.js +6 -0
- package/dist/node.dev.esm.js.map +1 -0
- package/dist/node.esm.js +4 -97
- package/dist/node.esm.js.map +1 -1
- package/dist/{types-DaHFfhlN.d.ts → types-W5vD6m2n.d.ts} +99 -44
- package/docs/ai/api-reference.md +11 -18
- package/docs/ai/manual.md +308 -230
- package/docs/reactive/advanced.md +312 -2
- package/docs/reactive/attend.md +1 -2
- package/docs/reactive/collections.md +21 -39
- package/docs/reactive/core.md +7 -37
- package/docs/reactive/debugging.md +117 -10
- package/docs/reactive/error-handling.md +10 -10
- package/docs/reactive/resource.md +125 -0
- package/docs/reactive/scan.md +32 -1
- package/docs/reactive.md +3 -2
- package/docs/utils.md +70 -0
- package/package.json +59 -30
- package/dist/chunks/async-browser-lvzLOCgk.cjs.map +0 -1
- package/dist/chunks/async-node-C3DeIb0y.cjs.map +0 -1
- package/dist/chunks/index-2vea86wD.esm.js.map +0 -1
- package/dist/chunks/index-CtA2AWl3.esm.js.map +0 -1
- package/dist/chunks/index-VTO-b2vR.cjs.map +0 -1
- package/dist/debug/debug.d.ts +0 -122
- package/dist/debug/debug.d.ts.map +0 -1
- package/dist/debug/index.d.ts +0 -4
- package/dist/debug/index.d.ts.map +0 -1
- package/dist/debug/lineage-panel.d.ts +0 -5
- package/dist/debug/lineage-panel.d.ts.map +0 -1
- package/dist/debug/lineage.d.ts +0 -79
- package/dist/debug/lineage.d.ts.map +0 -1
- package/dist/src/async/browser.d.ts +0 -2
- package/dist/src/async/browser.d.ts.map +0 -1
- package/dist/src/async/index.d.ts +0 -19
- package/dist/src/async/index.d.ts.map +0 -1
- package/dist/src/async/node.d.ts +0 -2
- package/dist/src/async/node.d.ts.map +0 -1
- package/dist/src/decorator.d.ts +0 -106
- package/dist/src/decorator.d.ts.map +0 -1
- package/dist/src/destroyable.d.ts +0 -87
- package/dist/src/destroyable.d.ts.map +0 -1
- package/dist/src/entry-browser.d.ts +0 -3
- package/dist/src/entry-browser.d.ts.map +0 -1
- package/dist/src/entry-node.d.ts +0 -3
- package/dist/src/entry-node.d.ts.map +0 -1
- package/dist/src/eventful.d.ts +0 -20
- package/dist/src/eventful.d.ts.map +0 -1
- package/dist/src/flavored.d.ts +0 -33
- package/dist/src/flavored.d.ts.map +0 -1
- package/dist/src/index.d.ts +0 -14
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/indexable.d.ts +0 -243
- package/dist/src/indexable.d.ts.map +0 -1
- package/dist/src/introspection.d.ts +0 -27
- package/dist/src/introspection.d.ts.map +0 -1
- package/dist/src/iterableWeak.d.ts +0 -53
- package/dist/src/iterableWeak.d.ts.map +0 -1
- package/dist/src/mixins.d.ts +0 -25
- package/dist/src/mixins.d.ts.map +0 -1
- package/dist/src/promiseChain.d.ts +0 -20
- package/dist/src/promiseChain.d.ts.map +0 -1
- package/dist/src/reactive/array.d.ts +0 -48
- package/dist/src/reactive/array.d.ts.map +0 -1
- package/dist/src/reactive/buffer.d.ts +0 -120
- package/dist/src/reactive/buffer.d.ts.map +0 -1
- package/dist/src/reactive/change.d.ts +0 -29
- package/dist/src/reactive/change.d.ts.map +0 -1
- package/dist/src/reactive/deep-touch.d.ts +0 -28
- package/dist/src/reactive/deep-touch.d.ts.map +0 -1
- package/dist/src/reactive/deep-watch-state.d.ts +0 -25
- package/dist/src/reactive/deep-watch-state.d.ts.map +0 -1
- package/dist/src/reactive/deep-watch.d.ts +0 -20
- package/dist/src/reactive/deep-watch.d.ts.map +0 -1
- package/dist/src/reactive/describe.d.ts +0 -12
- package/dist/src/reactive/describe.d.ts.map +0 -1
- package/dist/src/reactive/effect-context.d.ts +0 -34
- package/dist/src/reactive/effect-context.d.ts.map +0 -1
- package/dist/src/reactive/effects.d.ts +0 -164
- package/dist/src/reactive/effects.d.ts.map +0 -1
- package/dist/src/reactive/index.d.ts +0 -19
- package/dist/src/reactive/index.d.ts.map +0 -1
- package/dist/src/reactive/map.d.ts +0 -28
- package/dist/src/reactive/map.d.ts.map +0 -1
- package/dist/src/reactive/memoize.d.ts +0 -28
- package/dist/src/reactive/memoize.d.ts.map +0 -1
- package/dist/src/reactive/non-reactive-state.d.ts +0 -9
- package/dist/src/reactive/non-reactive-state.d.ts.map +0 -1
- package/dist/src/reactive/non-reactive.d.ts +0 -11
- package/dist/src/reactive/non-reactive.d.ts.map +0 -1
- package/dist/src/reactive/project.d.ts +0 -40
- package/dist/src/reactive/project.d.ts.map +0 -1
- package/dist/src/reactive/proxy-state.d.ts +0 -8
- package/dist/src/reactive/proxy-state.d.ts.map +0 -1
- package/dist/src/reactive/proxy.d.ts +0 -23
- package/dist/src/reactive/proxy.d.ts.map +0 -1
- package/dist/src/reactive/record.d.ts +0 -115
- package/dist/src/reactive/record.d.ts.map +0 -1
- package/dist/src/reactive/register.d.ts +0 -125
- package/dist/src/reactive/register.d.ts.map +0 -1
- package/dist/src/reactive/registry.d.ts +0 -21
- package/dist/src/reactive/registry.d.ts.map +0 -1
- package/dist/src/reactive/set.d.ts +0 -26
- package/dist/src/reactive/set.d.ts.map +0 -1
- package/dist/src/reactive/tracking.d.ts +0 -7
- package/dist/src/reactive/tracking.d.ts.map +0 -1
- package/dist/src/reactive/types.d.ts +0 -424
- package/dist/src/reactive/types.d.ts.map +0 -1
- package/dist/src/reactive/watch.d.ts +0 -48
- package/dist/src/reactive/watch.d.ts.map +0 -1
- package/dist/src/std-decorators.d.ts +0 -45
- package/dist/src/std-decorators.d.ts.map +0 -1
- package/dist/src/utils.d.ts +0 -49
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/zone.d.ts +0 -40
- package/dist/src/zone.d.ts.map +0 -1
- package/docs/reactive/describe.md +0 -85
- package/docs/reactive/project.md +0 -93
- package/src/async/browser.ts +0 -323
- package/src/async/index.ts +0 -27
- package/src/async/node.ts +0 -92
- package/src/decorator.ts +0 -272
- package/src/destroyable.ts +0 -199
- package/src/entry-browser.ts +0 -5
- package/src/entry-node.ts +0 -5
- package/src/eventful.ts +0 -110
- package/src/flavored.ts +0 -106
- package/src/index.d.ts +0 -12
- package/src/index.ts +0 -64
- package/src/indexable.ts +0 -526
- package/src/introspection.ts +0 -59
- package/src/iterableWeak.ts +0 -233
- package/src/mixins.ts +0 -123
- package/src/promiseChain.ts +0 -110
- package/src/reactive/array.ts +0 -500
- package/src/reactive/buffer.ts +0 -328
- package/src/reactive/change.ts +0 -131
- package/src/reactive/deep-touch.ts +0 -273
- package/src/reactive/deep-watch-state.ts +0 -82
- package/src/reactive/deep-watch.ts +0 -171
- package/src/reactive/describe.ts +0 -39
- package/src/reactive/effect-context.ts +0 -83
- package/src/reactive/effects.ts +0 -1434
- package/src/reactive/index.ts +0 -72
- package/src/reactive/map.ts +0 -142
- package/src/reactive/memoize.ts +0 -186
- package/src/reactive/non-reactive-state.ts +0 -49
- package/src/reactive/non-reactive.ts +0 -43
- package/src/reactive/project.md +0 -107
- package/src/reactive/project.ts +0 -430
- package/src/reactive/proxy-state.ts +0 -27
- package/src/reactive/proxy.ts +0 -282
- package/src/reactive/record.ts +0 -181
- package/src/reactive/register.ts +0 -538
- package/src/reactive/registry.ts +0 -72
- package/src/reactive/set.ts +0 -117
- package/src/reactive/tracking.ts +0 -41
- package/src/reactive/types.ts +0 -520
- package/src/reactive/watch.ts +0 -180
- package/src/std-decorators.ts +0 -256
- package/src/utils.ts +0 -300
- package/src/zone.ts +0 -142
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as decorator, a3 as markWithRoot, a4 as
|
|
1
|
+
import { r as decorator, x as flavored, a3 as markWithRoot, O as named, a4 as rootFunctionSymbol, S as proxyToObject, a5 as getRoot, a6 as dependant, U as options, a0 as untracked, s as deepCompare, a7 as optionCall, W as root, u as effect, $ as touched1, K as isReactive, T as reactive, _ as touched, o as cleanedBy, C as getState, a8 as keysOf, a9 as __runInitializers, aa as __esDecorate, ab as FoolProof, ac as objectsWithDeepWatchers, ad as effectToDeepWatchedObjects, ae as deepWatchers, H as isNonReactive, a1 as unwrap, af as registerDeepWatcher, w as flavorOptions, X as stopped, ag as nonReactiveClass, ah as unreactiveProps, ai as addUnreactiveProps, aj as nonReactiveObjects, ak as __setFunctionName, al as __classPrivateFieldGet, am as __classPrivateFieldSet, l as atomic, an as contentRef, ao as notifyPropertyChange, ap as metaProtos, aq as wrapProtos, ar as objectParents, as as watchers, at as effectToReactiveObjects, P as objectToProxy } from './proxy-r7lARftl.esm.js';
|
|
2
2
|
|
|
3
3
|
// Integrated with `using` statement via Symbol.dispose
|
|
4
4
|
const fr = new FinalizationRegistry((f) => f());
|
|
@@ -105,6 +105,154 @@ function callOnGC(cb) {
|
|
|
105
105
|
return forward;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/** Max edit distance before bailing out to a single "replace all" patch */
|
|
109
|
+
const BAILOUT_D = 256;
|
|
110
|
+
/**
|
|
111
|
+
* Myers' diff producing grouped patches: `{indexA, indexB, sliceA, sliceB}[]`.
|
|
112
|
+
* - O(N) for identical or prefix/suffix-only differences
|
|
113
|
+
* - O(ND) for small D, with a hard bailout at D=BAILOUT_D → single replacement patch
|
|
114
|
+
*/
|
|
115
|
+
function arrayDiff(A, B) {
|
|
116
|
+
let start = 0;
|
|
117
|
+
let endA = A.length;
|
|
118
|
+
let endB = B.length;
|
|
119
|
+
// Trim common prefix
|
|
120
|
+
while (start < endA && start < endB && A[start] === B[start])
|
|
121
|
+
start++;
|
|
122
|
+
// Trim common suffix
|
|
123
|
+
while (endA > start && endB > start && A[endA - 1] === B[endB - 1]) {
|
|
124
|
+
endA--;
|
|
125
|
+
endB--;
|
|
126
|
+
}
|
|
127
|
+
const lenA = endA - start;
|
|
128
|
+
const lenB = endB - start;
|
|
129
|
+
if (lenA === 0 && lenB === 0)
|
|
130
|
+
return [];
|
|
131
|
+
if (lenA === 0)
|
|
132
|
+
return [{ indexA: start, indexB: start, sliceA: [], sliceB: B.slice(start, endB) }];
|
|
133
|
+
if (lenB === 0)
|
|
134
|
+
return [{ indexA: start, indexB: start, sliceA: A.slice(start, endA), sliceB: [] }];
|
|
135
|
+
// Myers with bailout
|
|
136
|
+
const maxD = Math.min(lenA + lenB, BAILOUT_D);
|
|
137
|
+
const vSize = 2 * maxD + 1;
|
|
138
|
+
const vOffset = maxD;
|
|
139
|
+
const V = new Int32Array(vSize);
|
|
140
|
+
V[vOffset + 1] = 0;
|
|
141
|
+
const history = [];
|
|
142
|
+
for (let d = 0; d <= maxD; d++) {
|
|
143
|
+
for (let k = -d; k <= d; k += 2) {
|
|
144
|
+
let x;
|
|
145
|
+
if (k === -d || (k !== d && V[vOffset + k - 1] < V[vOffset + k + 1])) {
|
|
146
|
+
x = V[vOffset + k + 1];
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
x = V[vOffset + k - 1] + 1;
|
|
150
|
+
}
|
|
151
|
+
let y = x - k;
|
|
152
|
+
while (x < lenA && y < lenB && A[start + x] === B[start + y]) {
|
|
153
|
+
x++;
|
|
154
|
+
y++;
|
|
155
|
+
}
|
|
156
|
+
V[vOffset + k] = x;
|
|
157
|
+
if (x >= lenA && y >= lenB)
|
|
158
|
+
return buildPatches(history, A, B, start, x, y, d, k, vOffset);
|
|
159
|
+
}
|
|
160
|
+
history.push(new Int32Array(V));
|
|
161
|
+
}
|
|
162
|
+
// Bailout: too many differences
|
|
163
|
+
return [
|
|
164
|
+
{ indexA: start, indexB: start, sliceA: A.slice(start, endA), sliceB: B.slice(start, endB) },
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
function buildPatches(history, A, B, offset, finalX, finalY, finalD, finalK, vOffset) {
|
|
168
|
+
// Backtrack from (finalX, finalY) at step finalD to step 0, collecting ops in reverse
|
|
169
|
+
const ops = []; // 0=eq, 1=ins, 2=del
|
|
170
|
+
let x = finalX;
|
|
171
|
+
let y = finalY;
|
|
172
|
+
let k = finalK;
|
|
173
|
+
for (let d = finalD; d > 0; d--) {
|
|
174
|
+
const prev = history[d - 1];
|
|
175
|
+
let prevK;
|
|
176
|
+
let down;
|
|
177
|
+
if (k === -d) {
|
|
178
|
+
prevK = k + 1;
|
|
179
|
+
down = true;
|
|
180
|
+
}
|
|
181
|
+
else if (k === d) {
|
|
182
|
+
prevK = k - 1;
|
|
183
|
+
down = false;
|
|
184
|
+
}
|
|
185
|
+
else if (prev[vOffset + k - 1] < prev[vOffset + k + 1]) {
|
|
186
|
+
prevK = k + 1;
|
|
187
|
+
down = true;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
prevK = k - 1;
|
|
191
|
+
down = false;
|
|
192
|
+
}
|
|
193
|
+
const prevXEnd = prev[vOffset + prevK];
|
|
194
|
+
const prevYEnd = prevXEnd - prevK;
|
|
195
|
+
const xStart = down ? prevXEnd : prevXEnd + 1;
|
|
196
|
+
const yStart = down ? prevYEnd + 1 : prevXEnd + 1 - k;
|
|
197
|
+
// Diagonal matches (pushed in reverse)
|
|
198
|
+
while (x > xStart && y > yStart) {
|
|
199
|
+
ops.push(0);
|
|
200
|
+
x--;
|
|
201
|
+
y--;
|
|
202
|
+
}
|
|
203
|
+
// The edit step
|
|
204
|
+
if (down) {
|
|
205
|
+
ops.push(1); // ins
|
|
206
|
+
y--;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
ops.push(2); // del
|
|
210
|
+
x--;
|
|
211
|
+
}
|
|
212
|
+
k = prevK;
|
|
213
|
+
}
|
|
214
|
+
// Walk ops forward (they were pushed in reverse), grouping contiguous edits
|
|
215
|
+
const patches = [];
|
|
216
|
+
let currA = offset;
|
|
217
|
+
let currB = offset;
|
|
218
|
+
let sliceA = [];
|
|
219
|
+
let sliceB = [];
|
|
220
|
+
let patchA = -1;
|
|
221
|
+
let patchB = -1;
|
|
222
|
+
const flush = () => {
|
|
223
|
+
if (patchA !== -1) {
|
|
224
|
+
patches.push({ indexA: patchA, indexB: patchB, sliceA, sliceB });
|
|
225
|
+
sliceA = [];
|
|
226
|
+
sliceB = [];
|
|
227
|
+
patchA = -1;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
for (let i = ops.length - 1; i >= 0; i--) {
|
|
231
|
+
const op = ops[i];
|
|
232
|
+
if (op === 0) {
|
|
233
|
+
flush();
|
|
234
|
+
currA++;
|
|
235
|
+
currB++;
|
|
236
|
+
}
|
|
237
|
+
else if (op === 1) {
|
|
238
|
+
if (patchA === -1) {
|
|
239
|
+
patchA = currA;
|
|
240
|
+
patchB = currB;
|
|
241
|
+
}
|
|
242
|
+
sliceB.push(B[currB++]);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
if (patchA === -1) {
|
|
246
|
+
patchA = currA;
|
|
247
|
+
patchB = currB;
|
|
248
|
+
}
|
|
249
|
+
sliceA.push(A[currA++]);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
flush();
|
|
253
|
+
return patches;
|
|
254
|
+
}
|
|
255
|
+
|
|
108
256
|
var _a, _b;
|
|
109
257
|
const events = Symbol('events');
|
|
110
258
|
const hooks = Symbol('hooks');
|
|
@@ -607,19 +755,21 @@ function getBranch(tree, key) {
|
|
|
607
755
|
}
|
|
608
756
|
return branch;
|
|
609
757
|
}
|
|
610
|
-
function memoizeFunction(fn) {
|
|
758
|
+
function memoizeFunction(fn, opts) {
|
|
611
759
|
const fnRoot = getRoot(fn);
|
|
612
760
|
const existing = memoizedRegistry.get(fnRoot);
|
|
613
761
|
if (existing)
|
|
614
762
|
return existing;
|
|
615
763
|
const cacheRoot = {};
|
|
616
|
-
const memoized = markWithRoot((...args)
|
|
617
|
-
|
|
618
|
-
|
|
764
|
+
const memoized = markWithRoot(function memoized(...args) {
|
|
765
|
+
if (args.some((arg) => !(arg && ['object', 'symbol', 'function'].includes(typeof arg)))) {
|
|
766
|
+
if (opts?.lenient)
|
|
767
|
+
return fn.apply(this, args);
|
|
619
768
|
throw new Error('memoize expects non-null object arguments');
|
|
769
|
+
}
|
|
620
770
|
let node = cacheRoot;
|
|
621
771
|
// Note: decorators add `this` as first argument
|
|
622
|
-
for (const arg of
|
|
772
|
+
for (const arg of args) {
|
|
623
773
|
node = getBranch(node, arg);
|
|
624
774
|
}
|
|
625
775
|
dependant(node, 'memoize');
|
|
@@ -628,9 +778,9 @@ function memoizeFunction(fn) {
|
|
|
628
778
|
const wasVerification = options.isVerificationRun;
|
|
629
779
|
options.isVerificationRun = true;
|
|
630
780
|
try {
|
|
631
|
-
const fresh = untracked(() => fn(
|
|
781
|
+
const fresh = untracked(() => fn.apply(this, args));
|
|
632
782
|
if (!deepCompare(node.result, fresh)) {
|
|
633
|
-
optionCall('onMemoizationDiscrepancy', node.result, fresh, fn,
|
|
783
|
+
optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'calculation');
|
|
634
784
|
}
|
|
635
785
|
}
|
|
636
786
|
finally {
|
|
@@ -641,18 +791,18 @@ function memoizeFunction(fn) {
|
|
|
641
791
|
}
|
|
642
792
|
// Create memoize internal effect to track dependencies and invalidate cache
|
|
643
793
|
// Use untracked to prevent the effect creation from being affected by parent effects
|
|
644
|
-
node.cleanup = root(() => effect(() => {
|
|
794
|
+
node.cleanup = root(() => effect.named('memoize')(() => {
|
|
645
795
|
// Execute the function and track its dependencies
|
|
646
796
|
// The function execution will automatically track dependencies on reactive objects
|
|
647
|
-
node.result = fn(
|
|
648
|
-
return () => {
|
|
797
|
+
node.result = fn.apply(this, args);
|
|
798
|
+
return (reason) => {
|
|
649
799
|
// When dependencies change, clear the cache and notify consumers
|
|
650
800
|
delete node.result;
|
|
651
|
-
touched1(node, { type: 'invalidate', prop:
|
|
801
|
+
touched1(node, { type: 'invalidate', prop: args }, 'memoize');
|
|
652
802
|
// Lazy memoization: stop the effect so it doesn't re-run immediately.
|
|
653
803
|
// It will be re-created on next access.
|
|
654
804
|
if (node.cleanup) {
|
|
655
|
-
node.cleanup();
|
|
805
|
+
node.cleanup({ type: 'invalidate', cause: reason });
|
|
656
806
|
node.cleanup = undefined;
|
|
657
807
|
}
|
|
658
808
|
};
|
|
@@ -661,9 +811,9 @@ function memoizeFunction(fn) {
|
|
|
661
811
|
const wasVerification = options.isVerificationRun;
|
|
662
812
|
options.isVerificationRun = true;
|
|
663
813
|
try {
|
|
664
|
-
const fresh = untracked(() => fn(
|
|
814
|
+
const fresh = untracked(() => fn.apply(this, args));
|
|
665
815
|
if (!deepCompare(node.result, fresh)) {
|
|
666
|
-
optionCall('onMemoizationDiscrepancy', node.result, fresh, fn,
|
|
816
|
+
optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'comparison');
|
|
667
817
|
}
|
|
668
818
|
}
|
|
669
819
|
finally {
|
|
@@ -676,6 +826,55 @@ function memoizeFunction(fn) {
|
|
|
676
826
|
memoizedRegistry.set(memoized, memoized);
|
|
677
827
|
return memoized;
|
|
678
828
|
}
|
|
829
|
+
function memoizeObject(target, opts) {
|
|
830
|
+
const existing = memoizedRegistry.get(target);
|
|
831
|
+
if (existing)
|
|
832
|
+
return existing;
|
|
833
|
+
const proxy = new Proxy(target, {
|
|
834
|
+
get(source, prop, receiver) {
|
|
835
|
+
// 1. Walk prototype chain to find descriptor
|
|
836
|
+
let current = source;
|
|
837
|
+
let desc;
|
|
838
|
+
while (current) {
|
|
839
|
+
desc = Object.getOwnPropertyDescriptor(current, prop);
|
|
840
|
+
if (desc)
|
|
841
|
+
break;
|
|
842
|
+
current = Object.getPrototypeOf(current);
|
|
843
|
+
}
|
|
844
|
+
if (!desc)
|
|
845
|
+
return Reflect.get(source, prop, receiver);
|
|
846
|
+
// 2. If getter, memoize
|
|
847
|
+
if (desc.get) {
|
|
848
|
+
const originalGetter = desc.get;
|
|
849
|
+
let wrapper = wrapperRegistry.get(originalGetter);
|
|
850
|
+
if (!wrapper) {
|
|
851
|
+
wrapper = markWithRoot(named(`${String(source?.constructor?.name ?? 'Object')}.${String(prop)}`, (that) => {
|
|
852
|
+
return originalGetter.call(that);
|
|
853
|
+
}), {
|
|
854
|
+
propertyKey: prop,
|
|
855
|
+
});
|
|
856
|
+
const origRoot = originalGetter[rootFunctionSymbol];
|
|
857
|
+
if (origRoot)
|
|
858
|
+
wrapper[rootFunctionSymbol] = origRoot;
|
|
859
|
+
wrapperRegistry.set(originalGetter, wrapper);
|
|
860
|
+
}
|
|
861
|
+
const memoized = memoizeFunction(wrapper, opts);
|
|
862
|
+
return memoized(receiver);
|
|
863
|
+
}
|
|
864
|
+
// 3. Otherwise forward
|
|
865
|
+
return Reflect.get(source, prop, receiver);
|
|
866
|
+
},
|
|
867
|
+
// Forward set to the target (source) to ensure it acts as the receiver for reactivity notifications
|
|
868
|
+
set(source, prop, value, _receiver) {
|
|
869
|
+
// By strictly passing `source` as receiver, we ensure that if `source` is a reactive proxy,
|
|
870
|
+
// it recognizes itself and triggers change notifications.
|
|
871
|
+
return Reflect.set(source, prop, value, source);
|
|
872
|
+
},
|
|
873
|
+
});
|
|
874
|
+
proxyToObject.set(proxy, target);
|
|
875
|
+
memoizedRegistry.set(target, proxy);
|
|
876
|
+
return proxy;
|
|
877
|
+
}
|
|
679
878
|
/**
|
|
680
879
|
* Decorator and function wrapper for memoizing computed values based on reactive dependencies.
|
|
681
880
|
*
|
|
@@ -699,46 +898,55 @@ function memoizeFunction(fn) {
|
|
|
699
898
|
* })
|
|
700
899
|
* ```
|
|
701
900
|
*/
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
901
|
+
function makeMemoizeDecorator(memoizeOpts) {
|
|
902
|
+
return decorator({
|
|
903
|
+
getter(original, target, propertyKey) {
|
|
904
|
+
return function () {
|
|
905
|
+
let wrapper = wrapperRegistry.get(original);
|
|
906
|
+
if (!wrapper) {
|
|
907
|
+
wrapper = markWithRoot(named(`${String(target?.constructor?.name ?? target?.name ?? 'Object')}.${String(propertyKey)}`, (that) => {
|
|
908
|
+
return original.call(that);
|
|
909
|
+
}), {
|
|
910
|
+
method: original,
|
|
911
|
+
propertyKey,
|
|
912
|
+
});
|
|
913
|
+
const origRoot = original[rootFunctionSymbol];
|
|
914
|
+
if (origRoot)
|
|
915
|
+
wrapper[rootFunctionSymbol] = origRoot;
|
|
916
|
+
wrapperRegistry.set(original, wrapper);
|
|
917
|
+
}
|
|
918
|
+
const memoized = memoizeFunction(wrapper, memoizeOpts);
|
|
919
|
+
return memoized(this);
|
|
920
|
+
};
|
|
921
|
+
},
|
|
922
|
+
method(original, target, name) {
|
|
923
|
+
return function (...args) {
|
|
924
|
+
let wrapper = wrapperRegistry.get(original);
|
|
925
|
+
if (!wrapper) {
|
|
926
|
+
wrapper = markWithRoot(named(`${String(target?.constructor?.name ?? target?.name ?? 'Object')}.${String(name)}`, (that, ...args) => {
|
|
927
|
+
return original.call(that, ...args);
|
|
928
|
+
}), {
|
|
929
|
+
method: original,
|
|
930
|
+
propertyKey: name,
|
|
931
|
+
});
|
|
932
|
+
const origRoot = original[rootFunctionSymbol];
|
|
933
|
+
if (origRoot)
|
|
934
|
+
wrapper[rootFunctionSymbol] = origRoot;
|
|
935
|
+
wrapperRegistry.set(original, wrapper);
|
|
936
|
+
}
|
|
937
|
+
const memoized = memoizeFunction(wrapper, memoizeOpts);
|
|
938
|
+
return memoized(this, ...args);
|
|
939
|
+
};
|
|
940
|
+
},
|
|
941
|
+
default: (target) => typeof target === 'object'
|
|
942
|
+
? memoizeObject(target, memoizeOpts)
|
|
943
|
+
: memoizeFunction(target, memoizeOpts),
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
const memoize = flavored(makeMemoizeDecorator(), {
|
|
947
|
+
get lenient() {
|
|
948
|
+
return makeMemoizeDecorator({ lenient: true });
|
|
740
949
|
},
|
|
741
|
-
default: memoizeFunction,
|
|
742
950
|
});
|
|
743
951
|
|
|
744
952
|
function attend(source, callback) {
|
|
@@ -768,10 +976,10 @@ function attend(source, callback) {
|
|
|
768
976
|
}
|
|
769
977
|
}
|
|
770
978
|
});
|
|
771
|
-
return () => {
|
|
772
|
-
outer();
|
|
979
|
+
return (reason) => {
|
|
980
|
+
outer(reason);
|
|
773
981
|
for (const stop of keyEffects.values())
|
|
774
|
-
stop();
|
|
982
|
+
stop(reason);
|
|
775
983
|
keyEffects.clear();
|
|
776
984
|
};
|
|
777
985
|
}
|
|
@@ -807,7 +1015,8 @@ function attend(source, callback) {
|
|
|
807
1015
|
*/
|
|
808
1016
|
function scan(source, callback, initialValue) {
|
|
809
1017
|
const observedSource = reactive(source);
|
|
810
|
-
const
|
|
1018
|
+
const rawResult = [];
|
|
1019
|
+
const result = reactive(rawResult);
|
|
811
1020
|
// Track effects for each index to dispose them when the array shrinks
|
|
812
1021
|
const indexEffects = new Map();
|
|
813
1022
|
// Mapping from index to its current intermediate object
|
|
@@ -840,117 +1049,473 @@ function scan(source, callback, initialValue) {
|
|
|
840
1049
|
if (stop) {
|
|
841
1050
|
stop();
|
|
842
1051
|
indexEffects.delete(index);
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
Reflect.deleteProperty(result, index);
|
|
846
|
-
});
|
|
1052
|
+
Reflect.deleteProperty(indexToIntermediate, index);
|
|
1053
|
+
Reflect.deleteProperty(result, index);
|
|
847
1054
|
}
|
|
848
1055
|
}
|
|
849
1056
|
const mainEffect = effect(function scanMainEffect({ ascend }) {
|
|
850
1057
|
const length = observedSource.length;
|
|
851
1058
|
const occurrenceCount = new Map();
|
|
852
1059
|
let prev;
|
|
853
|
-
for (let i = 0; i < length; i++)
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
occurrenceCount.set(val, count + 1);
|
|
860
|
-
let list = intermediaries.get(val);
|
|
861
|
-
if (!list) {
|
|
862
|
-
list = [];
|
|
863
|
-
intermediaries.set(val, list);
|
|
864
|
-
}
|
|
865
|
-
let intermediate = list[count];
|
|
866
|
-
if (!intermediate) {
|
|
867
|
-
intermediate = reactive(new Intermediate(val, prev));
|
|
868
|
-
list[count] = intermediate;
|
|
869
|
-
}
|
|
870
|
-
else {
|
|
871
|
-
// Update the link.
|
|
872
|
-
if (untracked(() => intermediate.prev) !== prev) {
|
|
873
|
-
intermediate.prev = prev;
|
|
1060
|
+
for (let i = 0; i < length; i++)
|
|
1061
|
+
if (i in observedSource) {
|
|
1062
|
+
const val = observedSource[i];
|
|
1063
|
+
if (!(val &&
|
|
1064
|
+
(typeof val === 'object' || typeof val === 'function' || typeof val === 'symbol'))) {
|
|
1065
|
+
throw new Error('scan: items must be objects (WeakKey) for intermediate caching');
|
|
874
1066
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1067
|
+
const count = occurrenceCount.get(val) ?? 0;
|
|
1068
|
+
occurrenceCount.set(val, count + 1);
|
|
1069
|
+
let list = intermediaries.get(val);
|
|
1070
|
+
if (!list) {
|
|
1071
|
+
list = [];
|
|
1072
|
+
intermediaries.set(val, list);
|
|
1073
|
+
}
|
|
1074
|
+
let intermediate = list[count];
|
|
1075
|
+
if (!intermediate) {
|
|
1076
|
+
intermediate = reactive(new Intermediate(val, prev));
|
|
1077
|
+
list[count] = intermediate;
|
|
1078
|
+
}
|
|
1079
|
+
else {
|
|
1080
|
+
// Update the link.
|
|
1081
|
+
if (untracked(() => intermediate.prev) !== prev) {
|
|
1082
|
+
intermediate.prev = prev;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
// Update the reactive mapping for this index
|
|
1086
|
+
if (indexToIntermediate[i] !== intermediate) {
|
|
1087
|
+
indexToIntermediate[i] = intermediate;
|
|
1088
|
+
}
|
|
1089
|
+
// If we don't have an effect for this index yet, create one
|
|
1090
|
+
if (!indexEffects.has(i)) {
|
|
1091
|
+
ascend(() => {
|
|
1092
|
+
const index = i;
|
|
1093
|
+
const stop = effect(function scanIndexSyncEffect() {
|
|
1094
|
+
const inter = indexToIntermediate[index];
|
|
1095
|
+
if (inter) {
|
|
1096
|
+
const accValue = inter.acc;
|
|
889
1097
|
result[index] = accValue;
|
|
890
|
-
}
|
|
891
|
-
}
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
indexEffects.set(index, stop);
|
|
892
1101
|
});
|
|
893
|
-
|
|
894
|
-
|
|
1102
|
+
}
|
|
1103
|
+
prev = intermediate;
|
|
895
1104
|
}
|
|
896
|
-
prev = intermediate;
|
|
897
|
-
}
|
|
898
1105
|
// Cleanup trailing indices
|
|
899
1106
|
for (const index of Array.from(indexEffects.keys())) {
|
|
900
1107
|
if (index >= length)
|
|
901
1108
|
disposeIndex(index);
|
|
902
1109
|
}
|
|
903
1110
|
// Ensure result length matches source length
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
908
|
-
});
|
|
1111
|
+
if (rawResult.length !== length) {
|
|
1112
|
+
FoolProof.set(result, 'length', length, result);
|
|
1113
|
+
}
|
|
909
1114
|
});
|
|
910
|
-
return cleanedBy(result, (() => {
|
|
911
|
-
mainEffect();
|
|
1115
|
+
return cleanedBy(result, ((reason) => {
|
|
1116
|
+
mainEffect(reason);
|
|
912
1117
|
for (const stop of indexEffects.values())
|
|
913
|
-
stop();
|
|
1118
|
+
stop(reason);
|
|
914
1119
|
indexEffects.clear();
|
|
915
1120
|
}));
|
|
916
1121
|
}
|
|
917
1122
|
function lift(cb) {
|
|
918
1123
|
let result;
|
|
919
|
-
|
|
1124
|
+
let rawResult;
|
|
1125
|
+
const liftCleanup = effect.named('lift')(markWithRoot((access) => {
|
|
920
1126
|
const source = cb(access);
|
|
921
1127
|
if (!source || typeof source !== 'object')
|
|
922
1128
|
throw new Error('lift callback must return an array or object');
|
|
1129
|
+
const sourceProto = Object.getPrototypeOf(source);
|
|
923
1130
|
if (!result) {
|
|
924
|
-
|
|
925
|
-
|
|
1131
|
+
rawResult = Array.isArray(source) ? [] : Object.create(sourceProto);
|
|
1132
|
+
result = reactive(rawResult);
|
|
1133
|
+
}
|
|
1134
|
+
if (sourceProto !== Object.getPrototypeOf(result))
|
|
1135
|
+
throw new Error('lift callback must return the same type as the previous result');
|
|
1136
|
+
if (Array.isArray(source)) {
|
|
1137
|
+
const res = result;
|
|
1138
|
+
const patches = arrayDiff(res, source);
|
|
1139
|
+
for (let i = patches.length - 1; i >= 0; i--) {
|
|
1140
|
+
const { indexA, sliceA, sliceB } = patches[i];
|
|
1141
|
+
res.splice(indexA, sliceA.length, ...sliceB);
|
|
926
1142
|
}
|
|
927
|
-
|
|
928
|
-
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
for (const key of Object.keys(source)) {
|
|
1146
|
+
const had = key in rawResult;
|
|
1147
|
+
const newDesc = Object.getOwnPropertyDescriptor(source, key);
|
|
1148
|
+
if (had) {
|
|
1149
|
+
const oldDesc = Object.getOwnPropertyDescriptor(rawResult, key);
|
|
1150
|
+
const sameAccessor = oldDesc && newDesc.get && oldDesc.get === newDesc.get;
|
|
1151
|
+
Object.defineProperty(rawResult, key, newDesc);
|
|
1152
|
+
if (!sameAccessor &&
|
|
1153
|
+
rawResult[key] !==
|
|
1154
|
+
(oldDesc ? (oldDesc.get ? oldDesc.get() : oldDesc.value) : undefined))
|
|
1155
|
+
touched1(rawResult, { type: 'set', prop: key }, key);
|
|
1156
|
+
}
|
|
1157
|
+
else {
|
|
1158
|
+
Object.defineProperty(rawResult, key, newDesc);
|
|
1159
|
+
touched1(rawResult, { type: 'add', prop: key }, key);
|
|
1160
|
+
}
|
|
929
1161
|
}
|
|
1162
|
+
for (const key of Object.keys(rawResult))
|
|
1163
|
+
if (!(key in source)) {
|
|
1164
|
+
delete rawResult[key];
|
|
1165
|
+
touched1(rawResult, { type: 'del', prop: key }, key);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}, cb));
|
|
1169
|
+
return cleanedBy(result, liftCleanup);
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Reactively maps an array source through `fn`, producing a lazy reactive output array.
|
|
1173
|
+
*
|
|
1174
|
+
* Each source item gets its own isolated effect (via `root()`) so that changes to one item
|
|
1175
|
+
* only recompute that item's projection. Structural changes (push, splice, reorder) are detected
|
|
1176
|
+
* via `arrayDiff` and surgically applied to the output cache.
|
|
1177
|
+
*
|
|
1178
|
+
* The source can be a reactive array or a function returning an array. When a function is provided,
|
|
1179
|
+
* the function is re-evaluated inside an effect whenever its dependencies change.
|
|
1180
|
+
*
|
|
1181
|
+
* Output elements are computed lazily — accessing `result[i]` triggers computation if not yet cached.
|
|
1182
|
+
*
|
|
1183
|
+
* @param source - A reactive array or a function returning an array
|
|
1184
|
+
* @param fn - Mapping function applied to each element
|
|
1185
|
+
* @param options - Optional purity hints to skip per-item effects
|
|
1186
|
+
* @returns A readonly reactive array with a `[cleanup]` method to dispose all effects
|
|
1187
|
+
*/
|
|
1188
|
+
function morphArray(source, fn, options) {
|
|
1189
|
+
if (typeof source !== 'function' && !isReactive(source) && options?.pure === true) {
|
|
1190
|
+
return source.map(fn);
|
|
1191
|
+
}
|
|
1192
|
+
let track;
|
|
1193
|
+
const itemEffects = new Map();
|
|
1194
|
+
const cache = [];
|
|
1195
|
+
let input = [];
|
|
1196
|
+
function stopItem(key) {
|
|
1197
|
+
const entry = itemEffects.get(key);
|
|
1198
|
+
if (entry) {
|
|
1199
|
+
entry.stop({ type: 'stopped' });
|
|
1200
|
+
itemEffects.delete(key);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
function computeItem(key, input) {
|
|
1204
|
+
const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(input));
|
|
1205
|
+
if (isPure) {
|
|
1206
|
+
track(() => {
|
|
1207
|
+
cache[key] = fn(input);
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
else {
|
|
1211
|
+
const indexRef = { value: key };
|
|
1212
|
+
let stop;
|
|
1213
|
+
track(() => {
|
|
1214
|
+
stop = effect.opaque(() => {
|
|
1215
|
+
cache[indexRef.value] = fn(input);
|
|
1216
|
+
return (reason) => {
|
|
1217
|
+
delete cache[indexRef.value];
|
|
1218
|
+
touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
|
|
1219
|
+
stop({ type: 'invalidate', cause: reason });
|
|
1220
|
+
};
|
|
1221
|
+
});
|
|
1222
|
+
});
|
|
1223
|
+
itemEffects.set(key, { stop, index: indexRef });
|
|
930
1224
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1225
|
+
}
|
|
1226
|
+
const proxy = reactive(cache, {
|
|
1227
|
+
get(cache, prop) {
|
|
1228
|
+
const n = typeof prop === 'string' ? Number(prop) : NaN;
|
|
1229
|
+
if (Number.isNaN(n))
|
|
1230
|
+
return cache[prop];
|
|
1231
|
+
if (!(n in cache))
|
|
1232
|
+
computeItem(n, input[n]);
|
|
1233
|
+
return cache[n];
|
|
1234
|
+
},
|
|
1235
|
+
has(_cache, prop) {
|
|
1236
|
+
return Reflect.has(input, prop);
|
|
1237
|
+
},
|
|
1238
|
+
});
|
|
1239
|
+
const stopMain = effect.named('morph:array')(({ ascend }) => {
|
|
1240
|
+
track = ascend;
|
|
1241
|
+
const newInput = [...(typeof source === 'function' ? source() : source)];
|
|
1242
|
+
const diffs = arrayDiff(input, newInput).toSorted((a, b) => b.indexA - a.indexA);
|
|
1243
|
+
if (diffs.length > 0) {
|
|
1244
|
+
for (const diff of diffs) {
|
|
1245
|
+
// Stop items in removed range
|
|
1246
|
+
for (let i = diff.indexA; i < diff.indexA + diff.sliceA.length; i++)
|
|
1247
|
+
stopItem(i);
|
|
1248
|
+
// Shift existing itemEffects in the Map to match the new indices
|
|
1249
|
+
const shift = diff.sliceB.length - diff.sliceA.length;
|
|
1250
|
+
if (shift !== 0) {
|
|
1251
|
+
// We need to move entries in the Map.
|
|
1252
|
+
const entries = Array.from(itemEffects.entries()).sort((a, b) => a[0] - b[0]);
|
|
1253
|
+
// Remove entries that will be shifted
|
|
1254
|
+
for (const [idx, entry] of entries) {
|
|
1255
|
+
if (idx >= diff.indexA + diff.sliceA.length) {
|
|
1256
|
+
itemEffects.delete(idx);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
// Re-add them with shifted indices
|
|
1260
|
+
for (const [idx, entry] of entries) {
|
|
1261
|
+
if (idx >= diff.indexA + diff.sliceA.length) {
|
|
1262
|
+
const newIdx = idx + shift;
|
|
1263
|
+
entry.index.value = newIdx;
|
|
1264
|
+
itemEffects.set(newIdx, entry);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
// Splice the cache
|
|
1269
|
+
cache.splice(diff.indexA, diff.sliceA.length, ...new Array(diff.sliceB.length).fill(undefined));
|
|
1270
|
+
// Make holes for lazy computation
|
|
1271
|
+
for (let i = diff.indexA; i < diff.indexA + diff.sliceB.length; i++)
|
|
1272
|
+
delete cache[i];
|
|
1273
|
+
}
|
|
1274
|
+
const invalidates = new Set([keysOf]);
|
|
1275
|
+
if (input.length !== newInput.length)
|
|
1276
|
+
invalidates.add('length');
|
|
1277
|
+
for (const diff of diffs) {
|
|
1278
|
+
const max = Math.max(diff.sliceA.length, diff.sliceB.length);
|
|
1279
|
+
for (let i = 0; i < max; i++)
|
|
1280
|
+
invalidates.add(String(diff.indexA + i));
|
|
1281
|
+
}
|
|
1282
|
+
touched(cache, { type: 'bunch', method: 'morph-input' }, invalidates);
|
|
1283
|
+
}
|
|
1284
|
+
input = newInput;
|
|
1285
|
+
});
|
|
1286
|
+
return cleanedBy(proxy, (reason) => {
|
|
1287
|
+
stopMain(reason);
|
|
1288
|
+
for (const entry of itemEffects.values())
|
|
1289
|
+
entry.stop(reason);
|
|
1290
|
+
itemEffects.clear();
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Reactively maps a `Map` source through `fn`, producing a reactive output Map.
|
|
1295
|
+
*
|
|
1296
|
+
* Each key gets its own isolated effect so that value changes for one key only recompute
|
|
1297
|
+
* that key's projection. Key additions and removals are tracked via `keysOf` dependency.
|
|
1298
|
+
*
|
|
1299
|
+
* @param source - A reactive Map
|
|
1300
|
+
* @param fn - Mapping function applied to each value
|
|
1301
|
+
* @param options - Optional purity hints to skip per-key effects
|
|
1302
|
+
* @returns A reactive Map with a `[cleanup]` method to dispose all effects
|
|
1303
|
+
*/
|
|
1304
|
+
function morphMap(source, fn, options) {
|
|
1305
|
+
if (!isReactive(source) && options?.pure === true) {
|
|
1306
|
+
const res = new Map();
|
|
1307
|
+
for (const [k, v] of source)
|
|
1308
|
+
res.set(k, fn(v));
|
|
1309
|
+
return res;
|
|
1310
|
+
}
|
|
1311
|
+
let track;
|
|
1312
|
+
const itemEffects = new Map();
|
|
1313
|
+
const cache = new Map();
|
|
1314
|
+
Object.defineProperty(cache, 'constructor', { value: Object, enumerable: false });
|
|
1315
|
+
function stopItem(key) {
|
|
1316
|
+
const stop = itemEffects.get(key);
|
|
1317
|
+
if (stop) {
|
|
1318
|
+
stop({ type: 'stopped' });
|
|
1319
|
+
itemEffects.delete(key);
|
|
937
1320
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1321
|
+
}
|
|
1322
|
+
function computeItem(key, val) {
|
|
1323
|
+
const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(val));
|
|
1324
|
+
if (isPure) {
|
|
1325
|
+
cache.set(key, track(() => fn(val)));
|
|
943
1326
|
}
|
|
944
1327
|
else {
|
|
945
|
-
|
|
1328
|
+
const stop = track(() => effect.opaque(() => {
|
|
1329
|
+
cache.set(key, fn(source.get(key)));
|
|
1330
|
+
return (reason) => {
|
|
1331
|
+
cache.delete(key);
|
|
1332
|
+
touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
|
|
1333
|
+
stop({ type: 'invalidate', cause: reason });
|
|
1334
|
+
};
|
|
1335
|
+
}));
|
|
1336
|
+
itemEffects.set(key, stop);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
const proxy = reactive(cache, {
|
|
1340
|
+
get(cache, prop) {
|
|
1341
|
+
if (prop === 'get')
|
|
1342
|
+
return (key) => {
|
|
1343
|
+
if (!cache.has(key) && source.has(key))
|
|
1344
|
+
computeItem(key, source.get(key));
|
|
1345
|
+
return cache.get(key);
|
|
1346
|
+
};
|
|
1347
|
+
if (prop === 'has')
|
|
1348
|
+
return (key) => {
|
|
1349
|
+
return source.has(key);
|
|
1350
|
+
};
|
|
1351
|
+
if (prop === 'keys')
|
|
1352
|
+
return () => {
|
|
1353
|
+
return source.keys();
|
|
1354
|
+
};
|
|
1355
|
+
if (prop === 'values')
|
|
1356
|
+
return function* () {
|
|
1357
|
+
for (const key of source.keys()) {
|
|
1358
|
+
yield proxy.get(key);
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
if (prop === 'entries')
|
|
1362
|
+
return function* () {
|
|
1363
|
+
for (const key of source.keys()) {
|
|
1364
|
+
yield [key, proxy.get(key)];
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
if (prop === Symbol.iterator)
|
|
1368
|
+
return function* () {
|
|
1369
|
+
for (const key of source.keys()) {
|
|
1370
|
+
yield [key, proxy.get(key)];
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
return cache[prop];
|
|
1374
|
+
},
|
|
1375
|
+
});
|
|
1376
|
+
let stateSnapshot = getState(source);
|
|
1377
|
+
const stopMain = effect.named('morph:map')(({ ascend }) => {
|
|
1378
|
+
track = ascend;
|
|
1379
|
+
dependant(source, keysOf);
|
|
1380
|
+
while ('evolution' in stateSnapshot) {
|
|
1381
|
+
const { evolution } = stateSnapshot;
|
|
1382
|
+
stateSnapshot = stateSnapshot.next;
|
|
1383
|
+
if (evolution.type === 'add') {
|
|
1384
|
+
touched1(cache, evolution, evolution.prop);
|
|
1385
|
+
}
|
|
1386
|
+
else if (evolution.type === 'del') {
|
|
1387
|
+
stopItem(evolution.prop);
|
|
1388
|
+
cache.delete(evolution.prop);
|
|
1389
|
+
touched1(cache, evolution, evolution.prop);
|
|
1390
|
+
}
|
|
946
1391
|
}
|
|
947
1392
|
});
|
|
948
|
-
return cleanedBy(
|
|
1393
|
+
return cleanedBy(proxy, (reason) => {
|
|
1394
|
+
stopMain(reason);
|
|
1395
|
+
for (const stop of itemEffects.values())
|
|
1396
|
+
stop(reason);
|
|
1397
|
+
itemEffects.clear();
|
|
1398
|
+
});
|
|
949
1399
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1400
|
+
/**
|
|
1401
|
+
* Reactively maps a record/object source through `fn`, producing a reactive output record.
|
|
1402
|
+
*
|
|
1403
|
+
* Each key gets its own isolated effect so that value changes for one key only recompute
|
|
1404
|
+
* that key's projection. Key additions and removals are tracked automatically.
|
|
1405
|
+
*
|
|
1406
|
+
* @param source - A reactive record
|
|
1407
|
+
* @param fn - Mapping function applied to each value
|
|
1408
|
+
* @param options - Optional purity hints to skip per-key effects
|
|
1409
|
+
* @returns A reactive record with a `[cleanup]` method to dispose all effects
|
|
1410
|
+
*/
|
|
1411
|
+
function morphRecord(source, fn, options) {
|
|
1412
|
+
if (!isReactive(source) && options?.pure === true) {
|
|
1413
|
+
const res = {};
|
|
1414
|
+
for (const k of Object.keys(source))
|
|
1415
|
+
res[k] = fn(source[k]);
|
|
1416
|
+
return res;
|
|
1417
|
+
}
|
|
1418
|
+
let track;
|
|
1419
|
+
const itemEffects = new Map();
|
|
1420
|
+
const cache = {};
|
|
1421
|
+
function stopItem(key) {
|
|
1422
|
+
const stop = itemEffects.get(key);
|
|
1423
|
+
if (stop) {
|
|
1424
|
+
stop({ type: 'stopped' });
|
|
1425
|
+
itemEffects.delete(key);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
function computeItem(key, val) {
|
|
1429
|
+
const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(val));
|
|
1430
|
+
if (isPure) {
|
|
1431
|
+
cache[key] = track(() => fn(val));
|
|
1432
|
+
}
|
|
1433
|
+
else {
|
|
1434
|
+
const stop = track(() => effect.opaque(() => {
|
|
1435
|
+
cache[key] = fn(source[key]);
|
|
1436
|
+
return (reason) => {
|
|
1437
|
+
delete cache[key];
|
|
1438
|
+
touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
|
|
1439
|
+
stop({ type: 'invalidate', cause: reason });
|
|
1440
|
+
};
|
|
1441
|
+
}));
|
|
1442
|
+
itemEffects.set(key, stop);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
function get(prop) {
|
|
1446
|
+
if (!(prop in cache) && prop in source)
|
|
1447
|
+
computeItem(prop, source[prop]);
|
|
1448
|
+
return cache[prop];
|
|
1449
|
+
}
|
|
1450
|
+
const proxy = reactive(cache, {
|
|
1451
|
+
get(_, prop) {
|
|
1452
|
+
return get(prop);
|
|
1453
|
+
},
|
|
1454
|
+
has(_, prop) {
|
|
1455
|
+
return prop in source;
|
|
1456
|
+
},
|
|
1457
|
+
ownKeys() {
|
|
1458
|
+
return Reflect.ownKeys(source);
|
|
1459
|
+
},
|
|
1460
|
+
getOwnPropertyDescriptor(_cache, prop) {
|
|
1461
|
+
if (prop in source)
|
|
1462
|
+
return { configurable: true, enumerable: true, get: () => get(prop) };
|
|
1463
|
+
},
|
|
1464
|
+
});
|
|
1465
|
+
let stateSnapshot = getState(source);
|
|
1466
|
+
const stopMain = effect.named('morph:record')(({ ascend }) => {
|
|
1467
|
+
track = ascend;
|
|
1468
|
+
// Track only structural changes on source
|
|
1469
|
+
dependant(source, keysOf);
|
|
1470
|
+
while ('evolution' in stateSnapshot) {
|
|
1471
|
+
const { evolution } = stateSnapshot;
|
|
1472
|
+
stateSnapshot = stateSnapshot.next;
|
|
1473
|
+
if (evolution.type === 'add') {
|
|
1474
|
+
touched1(cache, evolution, evolution.prop);
|
|
1475
|
+
}
|
|
1476
|
+
else if (evolution.type === 'del') {
|
|
1477
|
+
stopItem(evolution.prop);
|
|
1478
|
+
delete cache[evolution.prop];
|
|
1479
|
+
touched1(cache, evolution, evolution.prop);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
return cleanedBy(proxy, (reason) => {
|
|
1484
|
+
stopMain(reason);
|
|
1485
|
+
for (const stop of itemEffects.values())
|
|
1486
|
+
stop(reason);
|
|
1487
|
+
itemEffects.clear();
|
|
1488
|
+
});
|
|
953
1489
|
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Reactively maps a collection (array, Map, or record) through a per-entry function.
|
|
1492
|
+
*
|
|
1493
|
+
* Each entry in the source gets its own reactive context — when only one entry's dependencies
|
|
1494
|
+
* change, only that entry's projection recomputes. Structural changes (additions, removals,
|
|
1495
|
+
* reorders) are detected via diffing and applied surgically.
|
|
1496
|
+
*
|
|
1497
|
+
* Use `morph.pure(source, fn)` when `fn` has no reactive reads (skips per-item effects).
|
|
1498
|
+
*
|
|
1499
|
+
* @example
|
|
1500
|
+
* ```ts
|
|
1501
|
+
* const users = reactive([{ name: 'John' }, { name: 'Jane' }])
|
|
1502
|
+
* const names = morph(users, u => u.name.toUpperCase())
|
|
1503
|
+
* // names[0] = 'JOHN', names[1] = 'JANE'
|
|
1504
|
+
* // Changing users[0].name only recomputes names[0]
|
|
1505
|
+
* ```
|
|
1506
|
+
*/
|
|
1507
|
+
const morph = flavored(function morph(source, fn, options) {
|
|
1508
|
+
if (Array.isArray(source) || typeof source === 'function')
|
|
1509
|
+
return morphArray(source, fn, options);
|
|
1510
|
+
if (source instanceof Map)
|
|
1511
|
+
return morphMap(source, fn, options);
|
|
1512
|
+
return morphRecord(source, fn, options);
|
|
1513
|
+
}, {
|
|
1514
|
+
get pure() {
|
|
1515
|
+
return (source, fn, _opt) => this(source, fn, { pure: true });
|
|
1516
|
+
},
|
|
1517
|
+
});
|
|
1518
|
+
|
|
954
1519
|
/**
|
|
955
1520
|
* Deep watch an object and all its nested properties
|
|
956
1521
|
* @param target - The object to watch deeply
|
|
@@ -972,6 +1537,7 @@ function deepWatch(target, callback, { immediate = false } = {}) {
|
|
|
972
1537
|
throw new Error('Target of deep watching must be an object');
|
|
973
1538
|
// Create a wrapper callback that matches EffectTrigger signature
|
|
974
1539
|
const wrappedCallback = markWithRoot((() => callback(target)), callback);
|
|
1540
|
+
registerDeepWatcher();
|
|
975
1541
|
// Use the existing effect system to register dependencies
|
|
976
1542
|
return effect(() => {
|
|
977
1543
|
// Mark the target object as having deep watchers
|
|
@@ -988,7 +1554,7 @@ function deepWatch(target, callback, { immediate = false } = {}) {
|
|
|
988
1554
|
const visited = new WeakSet();
|
|
989
1555
|
function traverseAndTrack(obj, depth = 0) {
|
|
990
1556
|
// Prevent infinite recursion and excessive depth
|
|
991
|
-
if (!obj || visited.has(obj) ||
|
|
1557
|
+
if (!obj || visited.has(obj) || typeof obj !== 'object' || depth > options.maxDeepWatchDepth)
|
|
992
1558
|
return;
|
|
993
1559
|
// Do not traverse into unreactive objects
|
|
994
1560
|
if (isNonReactive(obj))
|
|
@@ -1047,8 +1613,9 @@ function deepWatch(target, callback, { immediate = false } = {}) {
|
|
|
1047
1613
|
// This will register dependencies on all current properties and array elements
|
|
1048
1614
|
traverseAndTrack(target);
|
|
1049
1615
|
// Only call the callback if immediate is true or if it's not the first run
|
|
1050
|
-
if (immediate)
|
|
1051
|
-
callback(target);
|
|
1616
|
+
if (immediate) {
|
|
1617
|
+
untracked(() => callback(target));
|
|
1618
|
+
}
|
|
1052
1619
|
immediate = true;
|
|
1053
1620
|
// Return a cleanup function that properly removes deep watcher tracking
|
|
1054
1621
|
return () => {
|
|
@@ -1081,30 +1648,103 @@ function deepWatch(target, callback, { immediate = false } = {}) {
|
|
|
1081
1648
|
}
|
|
1082
1649
|
|
|
1083
1650
|
/**
|
|
1084
|
-
*
|
|
1651
|
+
* Organizes a source object's properties into a target object using a callback function.
|
|
1652
|
+
* This creates a reactive mapping between source properties and a target object,
|
|
1653
|
+
* automatically handling property additions, updates, and removals.
|
|
1654
|
+
*
|
|
1655
|
+
* @template Source - The type of the source object
|
|
1656
|
+
* @template Target - The type of the target object (defaults to Record<PropertyKey, any>)
|
|
1657
|
+
*
|
|
1658
|
+
* @param {Source} source - The source object to organize
|
|
1659
|
+
* @param {OrganizedCallback<Source, Target>} apply - Callback function that defines how each source property is mapped to the target
|
|
1660
|
+
* @param {Target} [baseTarget={}] - Optional base target object to use (will be made reactive if not already)
|
|
1661
|
+
*
|
|
1662
|
+
* @returns {OrganizedResult<Target>} The target object with cleanup capability
|
|
1663
|
+
*
|
|
1664
|
+
* @example
|
|
1665
|
+
* // Organize user permissions into role-based access
|
|
1666
|
+
* const user = reactive({ isAdmin: true, canEdit: false });
|
|
1667
|
+
* const permissions = organized(
|
|
1668
|
+
* user,
|
|
1669
|
+
* (access, target) => {
|
|
1670
|
+
* if (access.key === 'isAdmin') {
|
|
1671
|
+
* target.hasFullAccess = access.value;
|
|
1672
|
+
* }
|
|
1673
|
+
* target[`can${access.key.charAt(0).toUpperCase() + access.key.slice(1)}`] = access.value;
|
|
1674
|
+
* }
|
|
1675
|
+
* );
|
|
1676
|
+
*
|
|
1677
|
+
* @example
|
|
1678
|
+
* // Transform object structure with cleanup
|
|
1679
|
+
* const source = reactive({ firstName: 'John', lastName: 'Doe' });
|
|
1680
|
+
* const formatted = organized(
|
|
1681
|
+
* source,
|
|
1682
|
+
* (access, target) => {
|
|
1683
|
+
* if (access.key === 'firstName' || access.key === 'lastName') {
|
|
1684
|
+
* target.fullName = `${source.firstName} ${source.lastName}`.trim();
|
|
1685
|
+
* }
|
|
1686
|
+
* }
|
|
1687
|
+
* );
|
|
1688
|
+
*
|
|
1689
|
+
* @example
|
|
1690
|
+
* // Using with cleanup in a component
|
|
1691
|
+
* effect(() => {
|
|
1692
|
+
* const data = fetchData();
|
|
1693
|
+
* const organizedData = organized(data, (access, target) => {
|
|
1694
|
+
* // Transform data
|
|
1695
|
+
* });
|
|
1696
|
+
*
|
|
1697
|
+
* // The cleanup will be called automatically when the effect is disposed
|
|
1698
|
+
* return () => organizedData[cleanup]();
|
|
1699
|
+
* });
|
|
1085
1700
|
*/
|
|
1086
|
-
function
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1701
|
+
function organized(source, apply, baseTarget = {}) {
|
|
1702
|
+
const observedSource = reactive(source);
|
|
1703
|
+
const target = reactive(baseTarget);
|
|
1704
|
+
const stop = attend(() => {
|
|
1705
|
+
const keys = [];
|
|
1706
|
+
for (const key in observedSource)
|
|
1707
|
+
keys.push(key);
|
|
1708
|
+
return keys;
|
|
1709
|
+
}, (key) => {
|
|
1710
|
+
const sourceKey = key;
|
|
1711
|
+
const accessBase = {
|
|
1712
|
+
key: sourceKey,
|
|
1713
|
+
get: () => FoolProof.get(observedSource, sourceKey, observedSource),
|
|
1714
|
+
set: (value) => FoolProof.set(observedSource, sourceKey, value, observedSource),
|
|
1715
|
+
};
|
|
1716
|
+
Object.defineProperty(accessBase, 'value', {
|
|
1717
|
+
get: accessBase.get,
|
|
1718
|
+
set: accessBase.set,
|
|
1719
|
+
configurable: true,
|
|
1720
|
+
enumerable: true,
|
|
1721
|
+
});
|
|
1722
|
+
return apply(accessBase, target);
|
|
1723
|
+
});
|
|
1724
|
+
return cleanedBy(target, (reason) => stop(reason));
|
|
1092
1725
|
}
|
|
1093
1726
|
/**
|
|
1094
|
-
*
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1727
|
+
* Organizes a property on a target object
|
|
1728
|
+
* Shortcut for defineProperty/delete with touched signal
|
|
1729
|
+
* @param target - The target object
|
|
1730
|
+
* @param property - The property to organize
|
|
1731
|
+
* @param access - The access object
|
|
1732
|
+
* @returns The property descriptor
|
|
1733
|
+
*/
|
|
1734
|
+
function organize(target, property, access) {
|
|
1735
|
+
Object.defineProperty(target, property, {
|
|
1736
|
+
get: access.get,
|
|
1737
|
+
set: access.set,
|
|
1738
|
+
configurable: true,
|
|
1739
|
+
enumerable: true,
|
|
1740
|
+
});
|
|
1741
|
+
touched1(target, { type: 'set', prop: property }, property);
|
|
1742
|
+
return () => delete target[property];
|
|
1103
1743
|
}
|
|
1104
1744
|
|
|
1105
1745
|
//#region watch
|
|
1106
1746
|
const unsetYet = Symbol('unset-yet');
|
|
1107
|
-
function watch(value, //object | ((dep: DependencyAccess) => object),
|
|
1747
|
+
const watch = flavored(function watch(value, //object | ((dep: DependencyAccess) => object),
|
|
1108
1748
|
changed, options = {}) {
|
|
1109
1749
|
return typeof value === 'function'
|
|
1110
1750
|
? watchCallBack(value, changed, options)
|
|
@@ -1113,7 +1753,14 @@ changed, options = {}) {
|
|
|
1113
1753
|
: (() => {
|
|
1114
1754
|
throw new Error('watch: value must be a function or an object');
|
|
1115
1755
|
})();
|
|
1116
|
-
}
|
|
1756
|
+
}, {
|
|
1757
|
+
get deep() {
|
|
1758
|
+
return flavorOptions(this, { deep: true });
|
|
1759
|
+
},
|
|
1760
|
+
get immediate() {
|
|
1761
|
+
return flavorOptions(this, { immediate: true });
|
|
1762
|
+
},
|
|
1763
|
+
});
|
|
1117
1764
|
function watchObject(value, changed, { immediate = false, deep = false } = {}) {
|
|
1118
1765
|
if (deep)
|
|
1119
1766
|
return deepWatch(value, changed, { immediate });
|
|
@@ -1129,13 +1776,15 @@ function watchCallBack(value, changed, { immediate = false, deep = false } = {})
|
|
|
1129
1776
|
let deepCleanup;
|
|
1130
1777
|
const cbCleanup = effect(markWithRoot(function watchCallBackEffect(access) {
|
|
1131
1778
|
const newValue = value(access);
|
|
1132
|
-
if (oldValue !== newValue)
|
|
1133
|
-
|
|
1779
|
+
if (oldValue !== newValue) {
|
|
1780
|
+
const old = oldValue;
|
|
1781
|
+
if (old === unsetYet) {
|
|
1134
1782
|
if (immediate)
|
|
1135
|
-
changed(newValue);
|
|
1783
|
+
untracked(() => changed(newValue));
|
|
1136
1784
|
}
|
|
1137
1785
|
else
|
|
1138
|
-
changed(newValue,
|
|
1786
|
+
untracked(() => changed(newValue, old));
|
|
1787
|
+
}
|
|
1139
1788
|
oldValue = newValue;
|
|
1140
1789
|
if (deep) {
|
|
1141
1790
|
if (deepCleanup)
|
|
@@ -1152,6 +1801,45 @@ function watchCallBack(value, changed, { immediate = false, deep = false } = {})
|
|
|
1152
1801
|
});
|
|
1153
1802
|
}
|
|
1154
1803
|
//#endregion
|
|
1804
|
+
//#region when
|
|
1805
|
+
/**
|
|
1806
|
+
* Returns a promise that resolves when the predicate returns a truthy value.
|
|
1807
|
+
* The predicate is evaluated reactively — it re-runs whenever its dependencies change.
|
|
1808
|
+
* @param predicate - Reactive function that returns a value; resolves when truthy
|
|
1809
|
+
* @param timeout - Optional timeout in milliseconds — rejects if condition is not met within this duration
|
|
1810
|
+
* @returns Promise that resolves with the first truthy return value
|
|
1811
|
+
*/
|
|
1812
|
+
function when(predicate, timeout) {
|
|
1813
|
+
return new Promise((resolve, reject) => {
|
|
1814
|
+
let timer;
|
|
1815
|
+
const stop = effect(function whenEffect(access) {
|
|
1816
|
+
try {
|
|
1817
|
+
const value = predicate(access);
|
|
1818
|
+
if (value) {
|
|
1819
|
+
if (timer !== undefined)
|
|
1820
|
+
clearTimeout(timer);
|
|
1821
|
+
timer = undefined;
|
|
1822
|
+
queueMicrotask(() => stop());
|
|
1823
|
+
resolve(value);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
catch (error) {
|
|
1827
|
+
if (timer !== undefined)
|
|
1828
|
+
clearTimeout(timer);
|
|
1829
|
+
timer = undefined;
|
|
1830
|
+
reject(error);
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
if (timeout !== undefined) {
|
|
1834
|
+
timer = setTimeout(() => {
|
|
1835
|
+
stop();
|
|
1836
|
+
timer = undefined;
|
|
1837
|
+
reject(new Error(`when: timed out after ${timeout}ms`));
|
|
1838
|
+
}, timeout);
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
//#endregion
|
|
1155
1843
|
//#region nonReactive
|
|
1156
1844
|
/**
|
|
1157
1845
|
* Mark an object as non-reactive. This object and all its properties will never be made reactive.
|
|
@@ -1161,19 +1849,7 @@ function shallowNonReactive(obj) {
|
|
|
1161
1849
|
obj = unwrap(obj);
|
|
1162
1850
|
if (isNonReactive(obj))
|
|
1163
1851
|
return obj;
|
|
1164
|
-
|
|
1165
|
-
Object.defineProperty(obj, nonReactiveMark, {
|
|
1166
|
-
value: true,
|
|
1167
|
-
writable: false,
|
|
1168
|
-
enumerable: false,
|
|
1169
|
-
configurable: true,
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1172
|
-
catch { }
|
|
1173
|
-
if (!(nonReactiveMark in obj))
|
|
1174
|
-
nonReactiveObjects.add(obj);
|
|
1175
|
-
// Finally, not deep
|
|
1176
|
-
//for (const key in obj) deepNonReactive(obj[key])
|
|
1852
|
+
nonReactiveObjects.add(obj);
|
|
1177
1853
|
return obj;
|
|
1178
1854
|
}
|
|
1179
1855
|
function unreactiveApplication(arg1, ...args) {
|
|
@@ -1181,11 +1857,13 @@ function unreactiveApplication(arg1, ...args) {
|
|
|
1181
1857
|
? shallowNonReactive(arg1)
|
|
1182
1858
|
: ((original) => {
|
|
1183
1859
|
// Copy the parent's unreactive properties if they exist
|
|
1184
|
-
|
|
1860
|
+
const parentSet = unreactiveProps.get(original.prototype);
|
|
1861
|
+
const set = new Set(parentSet || []);
|
|
1185
1862
|
// Add all arguments (including the first one)
|
|
1186
|
-
|
|
1863
|
+
set.add(arg1);
|
|
1187
1864
|
for (const arg of args)
|
|
1188
|
-
|
|
1865
|
+
set.add(arg);
|
|
1866
|
+
addUnreactiveProps(original.prototype, set);
|
|
1189
1867
|
return original; // Return the class
|
|
1190
1868
|
});
|
|
1191
1869
|
}
|
|
@@ -1200,6 +1878,60 @@ const unreactive = decorator({
|
|
|
1200
1878
|
},
|
|
1201
1879
|
default: unreactiveApplication,
|
|
1202
1880
|
});
|
|
1881
|
+
/**
|
|
1882
|
+
* Creates a reactive resource that automatically tracks async state.
|
|
1883
|
+
* @param fetcher - Async function that returns the value. Reactive dependencies are tracked.
|
|
1884
|
+
* @param options - Resource options (initialValue)
|
|
1885
|
+
* @returns Reactive Resource object with value, loading, error, latest properties
|
|
1886
|
+
*/
|
|
1887
|
+
function resource(fetcher, options = {}) {
|
|
1888
|
+
const state = reactive({
|
|
1889
|
+
value: options.initialValue,
|
|
1890
|
+
loading: true,
|
|
1891
|
+
error: undefined,
|
|
1892
|
+
latest: options.initialValue,
|
|
1893
|
+
reload: () => {
|
|
1894
|
+
reloadSignal.value++;
|
|
1895
|
+
},
|
|
1896
|
+
});
|
|
1897
|
+
const reloadSignal = reactive({ value: 0 });
|
|
1898
|
+
let counter = 0;
|
|
1899
|
+
const stop = effect(function resourceEffect(access) {
|
|
1900
|
+
// Track reload signal to enable manual reloading
|
|
1901
|
+
if (reloadSignal.value) ;
|
|
1902
|
+
const id = ++counter;
|
|
1903
|
+
state.loading = true;
|
|
1904
|
+
state.error = undefined;
|
|
1905
|
+
try {
|
|
1906
|
+
const result = fetcher(access);
|
|
1907
|
+
if (result instanceof Promise) {
|
|
1908
|
+
result.then((val) => {
|
|
1909
|
+
if (id === counter) {
|
|
1910
|
+
state.value = val;
|
|
1911
|
+
state.latest = val;
|
|
1912
|
+
state.loading = false;
|
|
1913
|
+
}
|
|
1914
|
+
}, (err) => {
|
|
1915
|
+
if (id === counter) {
|
|
1916
|
+
state.error = err;
|
|
1917
|
+
state.loading = false;
|
|
1918
|
+
}
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
else {
|
|
1922
|
+
state.value = result;
|
|
1923
|
+
state.latest = result;
|
|
1924
|
+
state.loading = false;
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
catch (err) {
|
|
1928
|
+
state.error = err;
|
|
1929
|
+
state.loading = false;
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
state.stop = stop;
|
|
1933
|
+
return state;
|
|
1934
|
+
}
|
|
1203
1935
|
//#endregion
|
|
1204
1936
|
|
|
1205
1937
|
// Helper to work around TypeScript limitation: base class expressions cannot reference class type parameters
|
|
@@ -1670,410 +2402,25 @@ function register(keyFn, initial) {
|
|
|
1670
2402
|
}
|
|
1671
2403
|
|
|
1672
2404
|
/**
|
|
1673
|
-
*
|
|
1674
|
-
*/
|
|
1675
|
-
const effectProjectionMetadata = new WeakMap();
|
|
1676
|
-
/**
|
|
1677
|
-
* Returns the projection context of the currently running effect, if any.
|
|
2405
|
+
* Converts an iterator to a generator that yields reactive values
|
|
1678
2406
|
*/
|
|
1679
|
-
function
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
const active = getActiveEffect();
|
|
1685
|
-
if (active)
|
|
1686
|
-
effectProjectionMetadata.set(active, projection);
|
|
1687
|
-
}
|
|
1688
|
-
function defineAccessValue(access) {
|
|
1689
|
-
Object.defineProperty(access, 'value', {
|
|
1690
|
-
get: access.get,
|
|
1691
|
-
set: access.set,
|
|
1692
|
-
configurable: true,
|
|
1693
|
-
enumerable: true,
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
function makeCleanup(target, effectMap, onDispose, metadata) {
|
|
1697
|
-
if (metadata) {
|
|
1698
|
-
Object.defineProperty(target, projectionInfo, {
|
|
1699
|
-
value: metadata,
|
|
1700
|
-
writable: false,
|
|
1701
|
-
enumerable: false,
|
|
1702
|
-
configurable: true,
|
|
1703
|
-
});
|
|
1704
|
-
}
|
|
1705
|
-
return cleanedBy(target, () => {
|
|
1706
|
-
onDispose();
|
|
1707
|
-
for (const stop of effectMap.values())
|
|
1708
|
-
stop?.();
|
|
1709
|
-
effectMap.clear();
|
|
1710
|
-
});
|
|
1711
|
-
}
|
|
1712
|
-
function projectArray(source, apply) {
|
|
1713
|
-
source = reactive(source);
|
|
1714
|
-
const target = reactive([]);
|
|
1715
|
-
const indexEffects = new Map();
|
|
1716
|
-
function normalizeTargetLength(length) {
|
|
1717
|
-
FoolProof.set(target, 'length', length, target);
|
|
1718
|
-
}
|
|
1719
|
-
function disposeIndex(index) {
|
|
1720
|
-
const stopEffect = indexEffects.get(index);
|
|
1721
|
-
if (stopEffect) {
|
|
1722
|
-
indexEffects.delete(index);
|
|
1723
|
-
stopEffect();
|
|
1724
|
-
Reflect.deleteProperty(target, index);
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
const parent = getActiveProjection();
|
|
1728
|
-
const depth = parent ? parent.depth + 1 : 0;
|
|
1729
|
-
const cleanupLength = effect(named(`project:${source[Symbol.toStringTag]}.length`, ({ ascend }) => {
|
|
1730
|
-
const length = source.length;
|
|
1731
|
-
normalizeTargetLength(length);
|
|
1732
|
-
const existing = Array.from(indexEffects.keys());
|
|
1733
|
-
for (let i = 0; i < length; i++) {
|
|
1734
|
-
if (indexEffects.has(i))
|
|
1735
|
-
continue;
|
|
1736
|
-
ascend(function projectArrayIndexAscend() {
|
|
1737
|
-
const index = i;
|
|
1738
|
-
const stop = effect(named(`project:${source[Symbol.toStringTag]}[${index}]`, ({ reaction }) => {
|
|
1739
|
-
if (!reaction)
|
|
1740
|
-
setActiveProjection({ source, key: index, target, depth, parent });
|
|
1741
|
-
const previous = untracked(() => target[index]);
|
|
1742
|
-
const accessBase = {
|
|
1743
|
-
key: index,
|
|
1744
|
-
source,
|
|
1745
|
-
get: () => FoolProof.get(source, index, source),
|
|
1746
|
-
set: (value) => FoolProof.set(source, index, value, source),
|
|
1747
|
-
old: previous,
|
|
1748
|
-
};
|
|
1749
|
-
defineAccessValue(accessBase);
|
|
1750
|
-
target[index] = apply(accessBase, target);
|
|
1751
|
-
}));
|
|
1752
|
-
setEffectName(stop, `project[${depth}]:${index}`);
|
|
1753
|
-
indexEffects.set(i, stop);
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
for (const index of existing)
|
|
1757
|
-
if (index >= length)
|
|
1758
|
-
disposeIndex(index);
|
|
1759
|
-
}));
|
|
1760
|
-
return makeCleanup(target, indexEffects, () => cleanupLength(), {
|
|
1761
|
-
source,
|
|
1762
|
-
target,
|
|
1763
|
-
apply,
|
|
1764
|
-
depth,
|
|
1765
|
-
parent,
|
|
1766
|
-
});
|
|
1767
|
-
}
|
|
1768
|
-
function projectRegister(source, apply) {
|
|
1769
|
-
source = reactive(source);
|
|
1770
|
-
const rawTarget = new Map();
|
|
1771
|
-
const target = reactive(rawTarget);
|
|
1772
|
-
const keyEffects = new Map();
|
|
1773
|
-
function disposeKey(key) {
|
|
1774
|
-
const stopEffect = keyEffects.get(key);
|
|
1775
|
-
if (stopEffect) {
|
|
1776
|
-
stopEffect();
|
|
1777
|
-
keyEffects.delete(key);
|
|
1778
|
-
target.delete(key);
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
const parent = getActiveProjection();
|
|
1782
|
-
const depth = parent ? parent.depth + 1 : 0;
|
|
1783
|
-
const cleanupKeys = effect(function projectRegisterEffect({ ascend }) {
|
|
1784
|
-
const keys = new Set();
|
|
1785
|
-
for (const key of source.mapKeys())
|
|
1786
|
-
keys.add(key);
|
|
1787
|
-
for (const key of keys) {
|
|
1788
|
-
if (keyEffects.has(key))
|
|
1789
|
-
continue;
|
|
1790
|
-
ascend(() => {
|
|
1791
|
-
const stop = effect(function projectRegisterKeyEffect({ reaction }) {
|
|
1792
|
-
if (!reaction)
|
|
1793
|
-
setActiveProjection({ source, key, target, depth, parent });
|
|
1794
|
-
const previous = untracked(() => target.get(key));
|
|
1795
|
-
const accessBase = {
|
|
1796
|
-
key,
|
|
1797
|
-
source: source,
|
|
1798
|
-
get: () => source.get(key),
|
|
1799
|
-
set: (value) => {
|
|
1800
|
-
source.set(key, value);
|
|
1801
|
-
return true;
|
|
1802
|
-
},
|
|
1803
|
-
old: previous,
|
|
1804
|
-
};
|
|
1805
|
-
defineAccessValue(accessBase);
|
|
1806
|
-
const produced = apply(accessBase, target);
|
|
1807
|
-
target.set(key, produced);
|
|
1808
|
-
});
|
|
1809
|
-
setEffectName(stop, `project[${depth}]:${String(key)}`);
|
|
1810
|
-
keyEffects.set(key, stop);
|
|
1811
|
-
});
|
|
1812
|
-
}
|
|
1813
|
-
for (const key of Array.from(keyEffects.keys()))
|
|
1814
|
-
if (!keys.has(key))
|
|
1815
|
-
disposeKey(key);
|
|
1816
|
-
});
|
|
1817
|
-
return makeCleanup(target, keyEffects, () => cleanupKeys(), {
|
|
1818
|
-
source: source,
|
|
1819
|
-
target,
|
|
1820
|
-
apply,
|
|
1821
|
-
depth,
|
|
1822
|
-
parent,
|
|
1823
|
-
});
|
|
1824
|
-
}
|
|
1825
|
-
function projectRecord(source, apply) {
|
|
1826
|
-
source = reactive(source);
|
|
1827
|
-
const target = reactive({});
|
|
1828
|
-
const keyEffects = new Map();
|
|
1829
|
-
function disposeKey(key) {
|
|
1830
|
-
const stopEffect = keyEffects.get(key);
|
|
1831
|
-
if (stopEffect) {
|
|
1832
|
-
stopEffect();
|
|
1833
|
-
keyEffects.delete(key);
|
|
1834
|
-
Reflect.deleteProperty(target, key);
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
const parent = getActiveProjection();
|
|
1838
|
-
const depth = parent ? parent.depth + 1 : 0;
|
|
1839
|
-
const cleanupKeys = effect(function projectRecordEffect({ ascend }) {
|
|
1840
|
-
const keys = new Set();
|
|
1841
|
-
for (const key in source)
|
|
1842
|
-
keys.add(key);
|
|
1843
|
-
const observed = Reflect.ownKeys(source);
|
|
1844
|
-
for (const key of observed)
|
|
1845
|
-
keys.add(key);
|
|
1846
|
-
for (const key of keys) {
|
|
1847
|
-
if (keyEffects.has(key))
|
|
1848
|
-
continue;
|
|
1849
|
-
ascend(() => {
|
|
1850
|
-
const stop = effect(function projectRecordKeyEffect({ reaction }) {
|
|
1851
|
-
if (!reaction)
|
|
1852
|
-
setActiveProjection({ source, key, target, depth, parent });
|
|
1853
|
-
const sourceKey = key;
|
|
1854
|
-
const previous = untracked(() => target[key]);
|
|
1855
|
-
const accessBase = {
|
|
1856
|
-
key: sourceKey,
|
|
1857
|
-
source: source,
|
|
1858
|
-
get: () => FoolProof.get(source, sourceKey, source),
|
|
1859
|
-
set: (value) => FoolProof.set(source, sourceKey, value, source),
|
|
1860
|
-
old: previous,
|
|
1861
|
-
};
|
|
1862
|
-
defineAccessValue(accessBase);
|
|
1863
|
-
const produced = apply(accessBase, target);
|
|
1864
|
-
target[sourceKey] = produced;
|
|
1865
|
-
});
|
|
1866
|
-
setEffectName(stop, `project[${depth}]:${String(key)}`);
|
|
1867
|
-
keyEffects.set(key, stop);
|
|
1868
|
-
});
|
|
1869
|
-
}
|
|
1870
|
-
for (const key of Array.from(keyEffects.keys()))
|
|
1871
|
-
if (!keys.has(key))
|
|
1872
|
-
disposeKey(key);
|
|
1873
|
-
});
|
|
1874
|
-
return makeCleanup(target, keyEffects, () => cleanupKeys(), {
|
|
1875
|
-
source: source,
|
|
1876
|
-
target,
|
|
1877
|
-
apply,
|
|
1878
|
-
depth,
|
|
1879
|
-
parent,
|
|
1880
|
-
});
|
|
1881
|
-
}
|
|
1882
|
-
function projectMap(source, apply) {
|
|
1883
|
-
source = reactive(source);
|
|
1884
|
-
const rawTarget = new Map();
|
|
1885
|
-
const target = reactive(rawTarget);
|
|
1886
|
-
const keyEffects = new Map();
|
|
1887
|
-
function disposeKey(key) {
|
|
1888
|
-
const stopEffect = keyEffects.get(key);
|
|
1889
|
-
if (stopEffect) {
|
|
1890
|
-
stopEffect();
|
|
1891
|
-
keyEffects.delete(key);
|
|
1892
|
-
target.delete(key);
|
|
1893
|
-
}
|
|
2407
|
+
function* makeReactiveIterator(iterator) {
|
|
2408
|
+
let result = iterator.next();
|
|
2409
|
+
while (!result.done) {
|
|
2410
|
+
yield reactive(result.value);
|
|
2411
|
+
result = iterator.next();
|
|
1894
2412
|
}
|
|
1895
|
-
const parent = getActiveProjection();
|
|
1896
|
-
const depth = parent ? parent.depth + 1 : 0;
|
|
1897
|
-
const cleanupKeys = effect(function projectMapEffect({ ascend }) {
|
|
1898
|
-
const keys = new Set();
|
|
1899
|
-
for (const key of source.keys())
|
|
1900
|
-
keys.add(key);
|
|
1901
|
-
for (const key of keys) {
|
|
1902
|
-
if (keyEffects.has(key))
|
|
1903
|
-
continue;
|
|
1904
|
-
ascend(() => {
|
|
1905
|
-
const stop = effect(function projectMapKeyEffect({ reaction }) {
|
|
1906
|
-
if (!reaction)
|
|
1907
|
-
setActiveProjection({ source, key, target, depth, parent });
|
|
1908
|
-
const previous = untracked(() => target.get(key));
|
|
1909
|
-
const accessBase = {
|
|
1910
|
-
key,
|
|
1911
|
-
source: source,
|
|
1912
|
-
get: () => source.get(key),
|
|
1913
|
-
set: (value) => {
|
|
1914
|
-
source.set(key, value);
|
|
1915
|
-
return true;
|
|
1916
|
-
},
|
|
1917
|
-
old: previous,
|
|
1918
|
-
};
|
|
1919
|
-
defineAccessValue(accessBase);
|
|
1920
|
-
const produced = apply(accessBase, target);
|
|
1921
|
-
target.set(key, produced);
|
|
1922
|
-
});
|
|
1923
|
-
setEffectName(stop, `project[${depth}]:${String(key)}`);
|
|
1924
|
-
keyEffects.set(key, stop);
|
|
1925
|
-
});
|
|
1926
|
-
}
|
|
1927
|
-
for (const key of Array.from(keyEffects.keys()))
|
|
1928
|
-
if (!keys.has(key))
|
|
1929
|
-
disposeKey(key);
|
|
1930
|
-
});
|
|
1931
|
-
return makeCleanup(target, keyEffects, () => cleanupKeys(), {
|
|
1932
|
-
source: source,
|
|
1933
|
-
target,
|
|
1934
|
-
apply,
|
|
1935
|
-
depth,
|
|
1936
|
-
parent,
|
|
1937
|
-
});
|
|
1938
|
-
}
|
|
1939
|
-
function projectCore(source, apply) {
|
|
1940
|
-
if (Array.isArray(source))
|
|
1941
|
-
return projectArray(source, apply);
|
|
1942
|
-
if (source instanceof Map)
|
|
1943
|
-
return projectMap(source, apply);
|
|
1944
|
-
if (source instanceof Register)
|
|
1945
|
-
return projectRegister(source, apply);
|
|
1946
|
-
if (source && (source.constructor === Object || source.constructor === undefined))
|
|
1947
|
-
return projectRecord(source, apply);
|
|
1948
|
-
throw new Error('Unsupported source type');
|
|
1949
|
-
}
|
|
1950
|
-
const project = Object.assign(projectCore, {
|
|
1951
|
-
array: projectArray,
|
|
1952
|
-
register: projectRegister,
|
|
1953
|
-
record: projectRecord,
|
|
1954
|
-
map: projectMap,
|
|
1955
|
-
});
|
|
1956
|
-
|
|
1957
|
-
/**
|
|
1958
|
-
* Organizes a source object's properties into a target object using a callback function.
|
|
1959
|
-
* This creates a reactive mapping between source properties and a target object,
|
|
1960
|
-
* automatically handling property additions, updates, and removals.
|
|
1961
|
-
*
|
|
1962
|
-
* @template Source - The type of the source object
|
|
1963
|
-
* @template Target - The type of the target object (defaults to Record<PropertyKey, any>)
|
|
1964
|
-
*
|
|
1965
|
-
* @param {Source} source - The source object to organize
|
|
1966
|
-
* @param {OrganizedCallback<Source, Target>} apply - Callback function that defines how each source property is mapped to the target
|
|
1967
|
-
* @param {Target} [baseTarget={}] - Optional base target object to use (will be made reactive if not already)
|
|
1968
|
-
*
|
|
1969
|
-
* @returns {OrganizedResult<Target>} The target object with cleanup capability
|
|
1970
|
-
*
|
|
1971
|
-
* @example
|
|
1972
|
-
* // Organize user permissions into role-based access
|
|
1973
|
-
* const user = reactive({ isAdmin: true, canEdit: false });
|
|
1974
|
-
* const permissions = organized(
|
|
1975
|
-
* user,
|
|
1976
|
-
* (access, target) => {
|
|
1977
|
-
* if (access.key === 'isAdmin') {
|
|
1978
|
-
* target.hasFullAccess = access.value;
|
|
1979
|
-
* }
|
|
1980
|
-
* target[`can${access.key.charAt(0).toUpperCase() + access.key.slice(1)}`] = access.value;
|
|
1981
|
-
* }
|
|
1982
|
-
* );
|
|
1983
|
-
*
|
|
1984
|
-
* @example
|
|
1985
|
-
* // Transform object structure with cleanup
|
|
1986
|
-
* const source = reactive({ firstName: 'John', lastName: 'Doe' });
|
|
1987
|
-
* const formatted = organized(
|
|
1988
|
-
* source,
|
|
1989
|
-
* (access, target) => {
|
|
1990
|
-
* if (access.key === 'firstName' || access.key === 'lastName') {
|
|
1991
|
-
* target.fullName = `${source.firstName} ${source.lastName}`.trim();
|
|
1992
|
-
* }
|
|
1993
|
-
* }
|
|
1994
|
-
* );
|
|
1995
|
-
*
|
|
1996
|
-
* @example
|
|
1997
|
-
* // Using with cleanup in a component
|
|
1998
|
-
* effect(() => {
|
|
1999
|
-
* const data = fetchData();
|
|
2000
|
-
* const organizedData = organized(data, (access, target) => {
|
|
2001
|
-
* // Transform data
|
|
2002
|
-
* });
|
|
2003
|
-
*
|
|
2004
|
-
* // The cleanup will be called automatically when the effect is disposed
|
|
2005
|
-
* return () => organizedData[cleanup]();
|
|
2006
|
-
* });
|
|
2007
|
-
*/
|
|
2008
|
-
function organized(source, apply, baseTarget = {}) {
|
|
2009
|
-
const observedSource = reactive(source);
|
|
2010
|
-
const target = reactive(baseTarget);
|
|
2011
|
-
const stop = attend(() => {
|
|
2012
|
-
const keys = [];
|
|
2013
|
-
for (const key in observedSource)
|
|
2014
|
-
keys.push(key);
|
|
2015
|
-
return keys;
|
|
2016
|
-
}, (key) => {
|
|
2017
|
-
const sourceKey = key;
|
|
2018
|
-
const accessBase = {
|
|
2019
|
-
key: sourceKey,
|
|
2020
|
-
get: () => FoolProof.get(observedSource, sourceKey, observedSource),
|
|
2021
|
-
set: (value) => FoolProof.set(observedSource, sourceKey, value, observedSource),
|
|
2022
|
-
};
|
|
2023
|
-
Object.defineProperty(accessBase, 'value', {
|
|
2024
|
-
get: accessBase.get,
|
|
2025
|
-
set: accessBase.set,
|
|
2026
|
-
configurable: true,
|
|
2027
|
-
enumerable: true,
|
|
2028
|
-
});
|
|
2029
|
-
return apply(accessBase, target);
|
|
2030
|
-
});
|
|
2031
|
-
return cleanedBy(target, stop);
|
|
2032
|
-
}
|
|
2033
|
-
/**
|
|
2034
|
-
* Organizes a property on a target object
|
|
2035
|
-
* Shortcut for defineProperty/delete with touched signal
|
|
2036
|
-
* @param target - The target object
|
|
2037
|
-
* @param property - The property to organize
|
|
2038
|
-
* @param access - The access object
|
|
2039
|
-
* @returns The property descriptor
|
|
2040
|
-
*/
|
|
2041
|
-
function organize(target, property, access) {
|
|
2042
|
-
Object.defineProperty(target, property, {
|
|
2043
|
-
get: access.get,
|
|
2044
|
-
set: access.set,
|
|
2045
|
-
configurable: true,
|
|
2046
|
-
enumerable: true,
|
|
2047
|
-
});
|
|
2048
|
-
touched1(target, { type: 'set', prop: property }, property);
|
|
2049
|
-
return () => delete target[property];
|
|
2050
2413
|
}
|
|
2051
|
-
|
|
2052
2414
|
/**
|
|
2053
|
-
*
|
|
2054
|
-
*
|
|
2055
|
-
* It tracks the keys of the descriptors record and defines/updates/removes
|
|
2056
|
-
* properties on the target object as they change.
|
|
2057
|
-
*
|
|
2058
|
-
* @param descriptors - A reactive record of property descriptors.
|
|
2059
|
-
* @param target - The object to define properties on (defaults to a new object).
|
|
2060
|
-
* @returns The target object with reactive property definitions.
|
|
2415
|
+
* Converts an iterator of key-value pairs to a generator that yields reactive key-value pairs
|
|
2061
2416
|
*/
|
|
2062
|
-
function
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
const
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
...desc,
|
|
2070
|
-
configurable: true,
|
|
2071
|
-
});
|
|
2072
|
-
touched1(target, { type: 'set', prop: key }, key);
|
|
2073
|
-
}
|
|
2074
|
-
return () => Reflect.deleteProperty(target, key);
|
|
2075
|
-
});
|
|
2076
|
-
return cleanedBy(target, stop);
|
|
2417
|
+
function* makeReactiveEntriesIterator(iterator) {
|
|
2418
|
+
let result = iterator.next();
|
|
2419
|
+
while (!result.done) {
|
|
2420
|
+
const [key, value] = result.value;
|
|
2421
|
+
yield [reactive(key), reactive(value)];
|
|
2422
|
+
result = iterator.next();
|
|
2423
|
+
}
|
|
2077
2424
|
}
|
|
2078
2425
|
|
|
2079
2426
|
function* index(i, { length = true } = {}) {
|
|
@@ -2081,14 +2428,6 @@ function* index(i, { length = true } = {}) {
|
|
|
2081
2428
|
yield 'length';
|
|
2082
2429
|
yield i;
|
|
2083
2430
|
}
|
|
2084
|
-
function* range(a, b, { length = false } = {}) {
|
|
2085
|
-
const start = Math.min(a, b);
|
|
2086
|
-
const end = Math.max(a, b);
|
|
2087
|
-
if (length)
|
|
2088
|
-
yield 'length';
|
|
2089
|
-
for (let i = start; i <= end; i++)
|
|
2090
|
-
yield i;
|
|
2091
|
-
}
|
|
2092
2431
|
class Indexer extends Array {
|
|
2093
2432
|
get(i) {
|
|
2094
2433
|
dependant(this, i);
|
|
@@ -2103,20 +2442,28 @@ class Indexer extends Array {
|
|
|
2103
2442
|
}
|
|
2104
2443
|
}
|
|
2105
2444
|
const indexLess = { get: FoolProof.get, set: FoolProof.set };
|
|
2445
|
+
// Fast numeric-string check: first char is a digit (0-9)
|
|
2446
|
+
function asIndex(prop) {
|
|
2447
|
+
const c = prop.charCodeAt(0);
|
|
2448
|
+
if (c < 48 || c > 57)
|
|
2449
|
+
return -1; // not 0-9
|
|
2450
|
+
const n = +prop; // coerce — faster than parseInt, handles "0", "12", etc.
|
|
2451
|
+
return n === (n | 0) && n >= 0 ? n : -1;
|
|
2452
|
+
}
|
|
2106
2453
|
Object.assign(FoolProof, {
|
|
2107
2454
|
get(obj, prop, receiver) {
|
|
2108
|
-
if (obj
|
|
2109
|
-
const
|
|
2110
|
-
if (
|
|
2111
|
-
return Indexer.prototype.get.call(obj,
|
|
2455
|
+
if (Array.isArray(obj) && typeof prop === 'string') {
|
|
2456
|
+
const i = asIndex(prop);
|
|
2457
|
+
if (i >= 0)
|
|
2458
|
+
return Indexer.prototype.get.call(obj, i);
|
|
2112
2459
|
}
|
|
2113
2460
|
return indexLess.get(obj, prop, receiver);
|
|
2114
2461
|
},
|
|
2115
2462
|
set(obj, prop, value, receiver) {
|
|
2116
|
-
if (obj
|
|
2117
|
-
const
|
|
2118
|
-
if (
|
|
2119
|
-
return Indexer.prototype.set.call(obj,
|
|
2463
|
+
if (Array.isArray(obj) && typeof prop === 'string') {
|
|
2464
|
+
const i = asIndex(prop);
|
|
2465
|
+
if (i >= 0)
|
|
2466
|
+
return Indexer.prototype.set.call(obj, i, value);
|
|
2120
2467
|
}
|
|
2121
2468
|
return indexLess.set(obj, prop, value, receiver);
|
|
2122
2469
|
},
|
|
@@ -2125,374 +2472,186 @@ class ReactiveArray extends Array {
|
|
|
2125
2472
|
toJSON() {
|
|
2126
2473
|
return this;
|
|
2127
2474
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
dependant(this, 'length');
|
|
2149
|
-
if (actualIndex < 0 || actualIndex >= this.length)
|
|
2150
|
-
return undefined;
|
|
2151
|
-
return reactive(this[actualIndex]);
|
|
2152
|
-
}
|
|
2153
|
-
// Immutable versions of mutator methods
|
|
2154
|
-
toReversed() {
|
|
2155
|
-
dependant(this);
|
|
2156
|
-
return reactive(this.toReversed());
|
|
2157
|
-
}
|
|
2158
|
-
toSorted(compareFn) {
|
|
2159
|
-
dependant(this);
|
|
2160
|
-
return reactive(this.toSorted(compareFn));
|
|
2161
|
-
}
|
|
2162
|
-
toSpliced(start, deleteCount, ...items) {
|
|
2163
|
-
dependant(this);
|
|
2164
|
-
return deleteCount === undefined
|
|
2165
|
-
? this.toSpliced(start)
|
|
2166
|
-
: this.toSpliced(start, deleteCount, ...items);
|
|
2167
|
-
}
|
|
2168
|
-
with(index, value) {
|
|
2169
|
-
dependant(this);
|
|
2170
|
-
return reactive(this.with(index, value));
|
|
2171
|
-
}
|
|
2172
|
-
// Iterator methods with reactivity tracking
|
|
2173
|
-
entries() {
|
|
2174
|
-
dependant(this);
|
|
2175
|
-
return makeReactiveEntriesIterator(this.entries());
|
|
2176
|
-
}
|
|
2177
|
-
keys() {
|
|
2178
|
-
dependant(this, 'length');
|
|
2179
|
-
return this.keys();
|
|
2180
|
-
}
|
|
2181
|
-
values() {
|
|
2182
|
-
dependant(this);
|
|
2183
|
-
return makeReactiveIterator(this.values());
|
|
2184
|
-
}
|
|
2185
|
-
[Symbol.iterator]() {
|
|
2186
|
-
dependant(this);
|
|
2187
|
-
const nativeIterator = this[Symbol.iterator]();
|
|
2188
|
-
return {
|
|
2189
|
-
next() {
|
|
2190
|
-
const result = nativeIterator.next();
|
|
2191
|
-
if (result.done) {
|
|
2192
|
-
return result;
|
|
2193
|
-
}
|
|
2194
|
-
return { value: reactive(result.value), done: false };
|
|
2195
|
-
},
|
|
2196
|
-
[Symbol.iterator]() {
|
|
2197
|
-
return this;
|
|
2198
|
-
},
|
|
2199
|
-
[Symbol.dispose]() { },
|
|
2200
|
-
};
|
|
2201
|
-
}
|
|
2202
|
-
indexOf(searchElement, fromIndex) {
|
|
2203
|
-
const length = this.length;
|
|
2204
|
-
let i = fromIndex === undefined ? 0 : fromIndex;
|
|
2205
|
-
if (i < 0)
|
|
2206
|
-
i = Math.max(length + i, 0);
|
|
2207
|
-
const unwrappedSearch = unwrap(searchElement);
|
|
2208
|
-
for (; i < length; i++) {
|
|
2209
|
-
dependant(this, i);
|
|
2210
|
-
const item = this[i];
|
|
2211
|
-
if (item === searchElement || item === unwrappedSearch || unwrap(item) === unwrappedSearch) {
|
|
2212
|
-
return i;
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* This is a wrapper class for Array that adds reactive behavior.
|
|
2478
|
+
* It extends Array and overrides methods to add reactive behavior, while making sure that the internal representation is not reactive.
|
|
2479
|
+
*/
|
|
2480
|
+
let ReactiveArrayWrapper = (() => {
|
|
2481
|
+
var _a;
|
|
2482
|
+
let _classSuper = Array;
|
|
2483
|
+
let _instanceExtraInitializers = [];
|
|
2484
|
+
let _fill_decorators;
|
|
2485
|
+
let _pop_decorators;
|
|
2486
|
+
let _push_decorators;
|
|
2487
|
+
let _reverse_decorators;
|
|
2488
|
+
let _shift_decorators;
|
|
2489
|
+
let _sort_decorators;
|
|
2490
|
+
let _splice_decorators;
|
|
2491
|
+
let _unshift_decorators;
|
|
2492
|
+
return _a = class ReactiveArrayWrapper extends _classSuper {
|
|
2493
|
+
at(index) {
|
|
2494
|
+
return reactive(super.at(index));
|
|
2213
2495
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2496
|
+
concat(...items) {
|
|
2497
|
+
return reactive(super.concat(...items.map(unwrap)));
|
|
2498
|
+
}
|
|
2499
|
+
entries() {
|
|
2500
|
+
dependant(this, keysOf);
|
|
2501
|
+
return makeReactiveEntriesIterator(super.entries());
|
|
2502
|
+
}
|
|
2503
|
+
every(predicate, thisArg) {
|
|
2504
|
+
return super.every((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
|
|
2505
|
+
}
|
|
2506
|
+
fill(value, start, end) {
|
|
2507
|
+
return super.fill(unwrap(value), start, end);
|
|
2508
|
+
}
|
|
2509
|
+
filter(predicate, thisArg) {
|
|
2510
|
+
return reactive(super.filter((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg));
|
|
2511
|
+
}
|
|
2512
|
+
find(predicate, thisArg) {
|
|
2513
|
+
return reactive(super.find((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg));
|
|
2514
|
+
}
|
|
2515
|
+
findIndex(predicate, thisArg) {
|
|
2516
|
+
return super.findIndex((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
|
|
2517
|
+
}
|
|
2518
|
+
findLast(predicate, thisArg) {
|
|
2519
|
+
return reactive(super.findLast((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg));
|
|
2520
|
+
}
|
|
2521
|
+
findLastIndex(predicate, thisArg) {
|
|
2522
|
+
return super.findLastIndex((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
|
|
2523
|
+
}
|
|
2524
|
+
flat(depth) {
|
|
2525
|
+
return reactive(super.flat(depth));
|
|
2526
|
+
}
|
|
2527
|
+
flatMap(callbackfn, thisArg) {
|
|
2528
|
+
return reactive(super.flatMap((v, i, a) => unwrap(callbackfn.call(thisArg, reactive(v), i, a)), thisArg));
|
|
2529
|
+
}
|
|
2530
|
+
forEach(callbackfn, thisArg) {
|
|
2531
|
+
super.forEach((v, i, a) => {
|
|
2532
|
+
callbackfn.call(thisArg, reactive(v), i, a);
|
|
2533
|
+
}, thisArg);
|
|
2534
|
+
}
|
|
2535
|
+
includes(searchElement, fromIndex) {
|
|
2536
|
+
return arguments.length > 1
|
|
2537
|
+
? super.includes(unwrap(searchElement), fromIndex)
|
|
2538
|
+
: super.includes(unwrap(searchElement));
|
|
2539
|
+
}
|
|
2540
|
+
indexOf(searchElement, fromIndex) {
|
|
2541
|
+
return arguments.length > 1
|
|
2542
|
+
? super.indexOf(unwrap(searchElement), fromIndex)
|
|
2543
|
+
: super.indexOf(unwrap(searchElement));
|
|
2544
|
+
}
|
|
2545
|
+
join(separator) {
|
|
2546
|
+
return super.join(separator);
|
|
2547
|
+
}
|
|
2548
|
+
keys() {
|
|
2549
|
+
dependant(this, 'length');
|
|
2550
|
+
return super.keys();
|
|
2551
|
+
}
|
|
2552
|
+
lastIndexOf(searchElement, fromIndex) {
|
|
2553
|
+
return arguments.length > 1
|
|
2554
|
+
? super.lastIndexOf(unwrap(searchElement), fromIndex)
|
|
2555
|
+
: super.lastIndexOf(unwrap(searchElement));
|
|
2556
|
+
}
|
|
2557
|
+
map(callbackfn, thisArg) {
|
|
2558
|
+
return reactive(super.map((v, i, a) => unwrap(callbackfn.call(thisArg, reactive(v), i, a)), thisArg));
|
|
2559
|
+
}
|
|
2560
|
+
pop() {
|
|
2561
|
+
return reactive(super.pop());
|
|
2562
|
+
}
|
|
2563
|
+
push(...items) {
|
|
2564
|
+
return super.push(...items.map(unwrap));
|
|
2565
|
+
}
|
|
2566
|
+
reduce(callbackfn, initialValue) {
|
|
2567
|
+
return reactive(arguments.length > 1
|
|
2568
|
+
? super.reduce((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a)), initialValue)
|
|
2569
|
+
: super.reduce((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a))));
|
|
2570
|
+
}
|
|
2571
|
+
reduceRight(callbackfn, initialValue) {
|
|
2572
|
+
return reactive(arguments.length > 1
|
|
2573
|
+
? super.reduceRight((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a)), initialValue)
|
|
2574
|
+
: super.reduceRight((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a))));
|
|
2575
|
+
}
|
|
2576
|
+
reverse() {
|
|
2577
|
+
return reactive(super.reverse());
|
|
2578
|
+
}
|
|
2579
|
+
shift() {
|
|
2580
|
+
return reactive(super.shift());
|
|
2581
|
+
}
|
|
2582
|
+
slice(start, end) {
|
|
2583
|
+
return reactive(super.slice(start, end));
|
|
2584
|
+
}
|
|
2585
|
+
some(predicate, thisArg) {
|
|
2586
|
+
return super.some((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
|
|
2587
|
+
}
|
|
2588
|
+
sort(compareFn) {
|
|
2589
|
+
const wrappedCompare = compareFn
|
|
2590
|
+
? (a, b) => compareFn(reactive(a), reactive(b))
|
|
2591
|
+
: undefined;
|
|
2592
|
+
return super.sort(wrappedCompare);
|
|
2255
2593
|
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
}
|
|
2265
|
-
findIndex(predicateOrElement, thisArg) {
|
|
2266
|
-
if (typeof predicateOrElement === 'function') {
|
|
2267
|
-
const predicate = predicateOrElement;
|
|
2268
|
-
const length = this.length;
|
|
2269
|
-
for (let i = 0; i < length; i++) {
|
|
2270
|
-
dependant(this, i);
|
|
2271
|
-
const val = reactive(this[i]);
|
|
2272
|
-
if (predicate.call(thisArg, val, i, this)) {
|
|
2273
|
-
return i;
|
|
2274
|
-
}
|
|
2594
|
+
splice(start, deleteCount, ...items) {
|
|
2595
|
+
if (arguments.length > 2)
|
|
2596
|
+
return reactive(super.splice(start, deleteCount, ...items.map(unwrap)));
|
|
2597
|
+
if (arguments.length === 2)
|
|
2598
|
+
return reactive(super.splice(start, deleteCount));
|
|
2599
|
+
if (arguments.length === 1)
|
|
2600
|
+
return reactive(super.splice(start));
|
|
2601
|
+
return reactive([]);
|
|
2275
2602
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
}
|
|
2279
|
-
const fromIndex = typeof thisArg === 'number' ? thisArg : undefined;
|
|
2280
|
-
return this.indexOf(predicateOrElement, fromIndex);
|
|
2281
|
-
}
|
|
2282
|
-
flat(depth) {
|
|
2283
|
-
dependant(this);
|
|
2284
|
-
return reactive(depth === undefined ? this.flat() : this.flat(depth));
|
|
2285
|
-
}
|
|
2286
|
-
flatMap(callbackfn, thisArg) {
|
|
2287
|
-
dependant(this);
|
|
2288
|
-
return reactive(this.flatMap((item, index, array) => callbackfn.call(thisArg, reactive(item), index, array), thisArg));
|
|
2289
|
-
}
|
|
2290
|
-
filter(callbackfn, thisArg) {
|
|
2291
|
-
dependant(this);
|
|
2292
|
-
return reactive(this.filter((item, index, array) => callbackfn(reactive(item), index, array), thisArg));
|
|
2293
|
-
}
|
|
2294
|
-
map(callbackfn, thisArg) {
|
|
2295
|
-
dependant(this);
|
|
2296
|
-
return reactive(this.map((item, index, array) => callbackfn(reactive(item), index, array), thisArg));
|
|
2297
|
-
}
|
|
2298
|
-
reduce(callbackfn, initialValue) {
|
|
2299
|
-
dependant(this);
|
|
2300
|
-
const result = initialValue === undefined
|
|
2301
|
-
? this.reduce(callbackfn)
|
|
2302
|
-
: this.reduce(callbackfn, initialValue);
|
|
2303
|
-
return reactive(result);
|
|
2304
|
-
}
|
|
2305
|
-
reduceRight(callbackfn, initialValue) {
|
|
2306
|
-
dependant(this);
|
|
2307
|
-
const result = initialValue !== undefined
|
|
2308
|
-
? this.reduceRight(callbackfn, initialValue)
|
|
2309
|
-
: this.reduceRight(callbackfn);
|
|
2310
|
-
return reactive(result);
|
|
2311
|
-
}
|
|
2312
|
-
slice(start, end) {
|
|
2313
|
-
for (const i of range(start || 0, end || this.length - 1))
|
|
2314
|
-
dependant(this, i);
|
|
2315
|
-
return start === undefined
|
|
2316
|
-
? this.slice()
|
|
2317
|
-
: end === undefined
|
|
2318
|
-
? this.slice(start)
|
|
2319
|
-
: this.slice(start, end);
|
|
2320
|
-
}
|
|
2321
|
-
concat(...items) {
|
|
2322
|
-
dependant(this);
|
|
2323
|
-
return reactive(this.concat(...items));
|
|
2324
|
-
}
|
|
2325
|
-
join(separator) {
|
|
2326
|
-
dependant(this);
|
|
2327
|
-
return this.join(separator);
|
|
2328
|
-
}
|
|
2329
|
-
forEach(callbackfn, thisArg) {
|
|
2330
|
-
dependant(this);
|
|
2331
|
-
this.forEach((value, index, array) => {
|
|
2332
|
-
callbackfn.call(thisArg, reactive(value), index, array);
|
|
2333
|
-
});
|
|
2334
|
-
}
|
|
2335
|
-
every(callbackfn, thisArg) {
|
|
2336
|
-
const length = this.length;
|
|
2337
|
-
for (let i = 0; i < length; i++) {
|
|
2338
|
-
dependant(this, i);
|
|
2339
|
-
if (!callbackfn.call(thisArg, reactive(this[i]), i, this)) {
|
|
2340
|
-
return false;
|
|
2603
|
+
unshift(...items) {
|
|
2604
|
+
return super.unshift(...items.map(unwrap));
|
|
2341
2605
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
}
|
|
2346
|
-
some(callbackfn, thisArg) {
|
|
2347
|
-
const length = this.length;
|
|
2348
|
-
for (let i = 0; i < length; i++) {
|
|
2349
|
-
dependant(this, i);
|
|
2350
|
-
if (callbackfn.call(thisArg, reactive(this[i]), i, this)) {
|
|
2351
|
-
return true;
|
|
2606
|
+
values() {
|
|
2607
|
+
dependant(this, keysOf);
|
|
2608
|
+
return makeReactiveIterator(super.values());
|
|
2352
2609
|
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
}
|
|
2357
|
-
// Side-effectful
|
|
2358
|
-
push(...items) {
|
|
2359
|
-
const oldLength = this.length;
|
|
2360
|
-
try {
|
|
2361
|
-
return this.push(...items);
|
|
2362
|
-
}
|
|
2363
|
-
finally {
|
|
2364
|
-
touched(this, { type: 'bunch', method: 'push' }, range(oldLength, oldLength + items.length - 1, { length: true }));
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
pop() {
|
|
2368
|
-
if (this.length === 0)
|
|
2369
|
-
return undefined;
|
|
2370
|
-
try {
|
|
2371
|
-
return reactive(this.pop());
|
|
2372
|
-
}
|
|
2373
|
-
finally {
|
|
2374
|
-
touched(this, { type: 'bunch', method: 'pop' }, index(this.length));
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
shift() {
|
|
2378
|
-
if (this.length === 0)
|
|
2379
|
-
return undefined;
|
|
2380
|
-
try {
|
|
2381
|
-
return reactive(this.shift());
|
|
2382
|
-
}
|
|
2383
|
-
finally {
|
|
2384
|
-
touched(this, { type: 'bunch', method: 'shift' }, range(0, this.length + 1, { length: true }));
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
unshift(...items) {
|
|
2388
|
-
try {
|
|
2389
|
-
return this.unshift(...items);
|
|
2390
|
-
}
|
|
2391
|
-
finally {
|
|
2392
|
-
touched(this, { type: 'bunch', method: 'unshift' }, range(0, this.length - items.length, { length: true }));
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
splice(start, deleteCount, ...items) {
|
|
2396
|
-
const oldLength = this.length;
|
|
2397
|
-
// Normalize start index
|
|
2398
|
-
let actualStart = start;
|
|
2399
|
-
if (actualStart < 0)
|
|
2400
|
-
actualStart = Math.max(oldLength + actualStart, 0);
|
|
2401
|
-
else
|
|
2402
|
-
actualStart = Math.min(actualStart, oldLength);
|
|
2403
|
-
// Normalize deleteCount
|
|
2404
|
-
let actualDeleteCount = deleteCount;
|
|
2405
|
-
if (actualDeleteCount === undefined) {
|
|
2406
|
-
actualDeleteCount = oldLength - actualStart;
|
|
2407
|
-
}
|
|
2408
|
-
else {
|
|
2409
|
-
actualDeleteCount = Math.max(0, Math.min(actualDeleteCount, oldLength - actualStart));
|
|
2410
|
-
}
|
|
2411
|
-
try {
|
|
2412
|
-
if (deleteCount === undefined)
|
|
2413
|
-
return reactive(this.splice(start));
|
|
2414
|
-
return reactive(this.splice(start, deleteCount, ...items));
|
|
2415
|
-
}
|
|
2416
|
-
finally {
|
|
2417
|
-
touched(this, { type: 'bunch', method: 'splice' }, actualDeleteCount === items.length
|
|
2418
|
-
? range(actualStart, actualStart + actualDeleteCount - 1)
|
|
2419
|
-
: range(actualStart, oldLength + Math.max(items.length - actualDeleteCount, 0), {
|
|
2420
|
-
length: true,
|
|
2421
|
-
}));
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
reverse() {
|
|
2425
|
-
try {
|
|
2426
|
-
return this.reverse();
|
|
2427
|
-
}
|
|
2428
|
-
finally {
|
|
2429
|
-
touched(this, { type: 'bunch', method: 'reverse' }, range(0, this.length - 1));
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
sort(compareFn) {
|
|
2433
|
-
compareFn = compareFn || ((a, b) => a.toString().localeCompare(b.toString()));
|
|
2434
|
-
try {
|
|
2435
|
-
return this.sort((a, b) => compareFn(reactive(a), reactive(b)));
|
|
2436
|
-
}
|
|
2437
|
-
finally {
|
|
2438
|
-
touched(this, { type: 'bunch', method: 'sort' }, range(0, this.length - 1));
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
fill(value, start, end) {
|
|
2442
|
-
const len = this.length;
|
|
2443
|
-
let k = start === undefined ? 0 : start;
|
|
2444
|
-
if (k < 0)
|
|
2445
|
-
k = Math.max(len + k, 0);
|
|
2446
|
-
else
|
|
2447
|
-
k = Math.min(k, len);
|
|
2448
|
-
let final = end === undefined ? len : end;
|
|
2449
|
-
if (final < 0)
|
|
2450
|
-
final = Math.max(len + final, 0);
|
|
2451
|
-
else
|
|
2452
|
-
final = Math.min(final, len);
|
|
2453
|
-
try {
|
|
2454
|
-
if (start === undefined)
|
|
2455
|
-
return this.fill(value);
|
|
2456
|
-
if (end === undefined)
|
|
2457
|
-
return this.fill(value, start);
|
|
2458
|
-
return this.fill(value, start, end);
|
|
2459
|
-
}
|
|
2460
|
-
finally {
|
|
2461
|
-
if (final > k) {
|
|
2462
|
-
touched(this, { type: 'bunch', method: 'fill' }, range(k, final - 1));
|
|
2610
|
+
[(_fill_decorators = [atomic], _pop_decorators = [atomic], _push_decorators = [atomic], _reverse_decorators = [atomic], _shift_decorators = [atomic], _sort_decorators = [atomic], _splice_decorators = [atomic], _unshift_decorators = [atomic], Symbol.iterator)]() {
|
|
2611
|
+
dependant(this, keysOf);
|
|
2612
|
+
return makeReactiveIterator(super[Symbol.iterator]());
|
|
2463
2613
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
const count = Math.min(final - from, len - to);
|
|
2490
|
-
if (count > 0) {
|
|
2491
|
-
touched(this, { type: 'bunch', method: 'copyWithin' }, range(to, to + count - 1));
|
|
2614
|
+
toReversed() {
|
|
2615
|
+
return reactive(super.toReversed());
|
|
2616
|
+
}
|
|
2617
|
+
toSorted(compareFn) {
|
|
2618
|
+
const wrappedCompare = compareFn
|
|
2619
|
+
? (a, b) => compareFn(reactive(a), reactive(b))
|
|
2620
|
+
: undefined;
|
|
2621
|
+
return reactive(super.toSorted(wrappedCompare));
|
|
2622
|
+
}
|
|
2623
|
+
// biome-ignore lint/complexity/noArguments: Needed to distinguish fn() from fn(undefined)
|
|
2624
|
+
toSpliced(start, deleteCount, ...items) {
|
|
2625
|
+
if (arguments.length > 2)
|
|
2626
|
+
return reactive(super.toSpliced(start, deleteCount, ...items.map(unwrap)));
|
|
2627
|
+
if (arguments.length === 2)
|
|
2628
|
+
return reactive(super.toSpliced(start, deleteCount));
|
|
2629
|
+
if (arguments.length === 1)
|
|
2630
|
+
return reactive(super.toSpliced(start));
|
|
2631
|
+
return reactive([...this]);
|
|
2632
|
+
}
|
|
2633
|
+
with(index, value) {
|
|
2634
|
+
return reactive(super.with(index, unwrap(value)));
|
|
2635
|
+
}
|
|
2636
|
+
constructor() {
|
|
2637
|
+
super(...arguments);
|
|
2638
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
2492
2639
|
}
|
|
2493
|
-
}
|
|
2494
|
-
|
|
2495
|
-
|
|
2640
|
+
},
|
|
2641
|
+
(() => {
|
|
2642
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
2643
|
+
__esDecorate(_a, null, _fill_decorators, { kind: "method", name: "fill", static: false, private: false, access: { has: obj => "fill" in obj, get: obj => obj.fill }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2644
|
+
__esDecorate(_a, null, _pop_decorators, { kind: "method", name: "pop", static: false, private: false, access: { has: obj => "pop" in obj, get: obj => obj.pop }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2645
|
+
__esDecorate(_a, null, _push_decorators, { kind: "method", name: "push", static: false, private: false, access: { has: obj => "push" in obj, get: obj => obj.push }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2646
|
+
__esDecorate(_a, null, _reverse_decorators, { kind: "method", name: "reverse", static: false, private: false, access: { has: obj => "reverse" in obj, get: obj => obj.reverse }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2647
|
+
__esDecorate(_a, null, _shift_decorators, { kind: "method", name: "shift", static: false, private: false, access: { has: obj => "shift" in obj, get: obj => obj.shift }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2648
|
+
__esDecorate(_a, null, _sort_decorators, { kind: "method", name: "sort", static: false, private: false, access: { has: obj => "sort" in obj, get: obj => obj.sort }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2649
|
+
__esDecorate(_a, null, _splice_decorators, { kind: "method", name: "splice", static: false, private: false, access: { has: obj => "splice" in obj, get: obj => obj.splice }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2650
|
+
__esDecorate(_a, null, _unshift_decorators, { kind: "method", name: "unshift", static: false, private: false, access: { has: obj => "unshift" in obj, get: obj => obj.unshift }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
2651
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
2652
|
+
})(),
|
|
2653
|
+
_a;
|
|
2654
|
+
})();
|
|
2496
2655
|
|
|
2497
2656
|
/**
|
|
2498
2657
|
* Reactive wrapper around JavaScript's WeakMap class
|
|
@@ -2555,7 +2714,7 @@ class ReactiveMap extends Map {
|
|
|
2555
2714
|
this.forEach(callbackfn, thisArg);
|
|
2556
2715
|
}
|
|
2557
2716
|
keys() {
|
|
2558
|
-
dependant(contentRef(this));
|
|
2717
|
+
dependant(contentRef(this), keysOf);
|
|
2559
2718
|
return this.keys();
|
|
2560
2719
|
}
|
|
2561
2720
|
values() {
|
|
@@ -2712,11 +2871,14 @@ class ReactiveSet extends Set {
|
|
|
2712
2871
|
}
|
|
2713
2872
|
|
|
2714
2873
|
// Register native collection types to use specialized reactive wrappers
|
|
2874
|
+
console.time('mutts-reactive-init');
|
|
2715
2875
|
metaProtos.set(Array, ReactiveArray.prototype);
|
|
2716
2876
|
metaProtos.set(Set, ReactiveSet.prototype);
|
|
2717
2877
|
metaProtos.set(WeakSet, ReactiveWeakSet.prototype);
|
|
2718
2878
|
metaProtos.set(Map, ReactiveMap.prototype);
|
|
2719
2879
|
metaProtos.set(WeakMap, ReactiveWeakMap.prototype);
|
|
2880
|
+
wrapProtos.set(Array, ReactiveArrayWrapper.prototype);
|
|
2881
|
+
console.timeEnd('mutts-reactive-init');
|
|
2720
2882
|
/**
|
|
2721
2883
|
* Object containing internal reactive system state for debugging and profiling
|
|
2722
2884
|
*/
|
|
@@ -2959,7 +3121,7 @@ function throttle(delay) {
|
|
|
2959
3121
|
});
|
|
2960
3122
|
}
|
|
2961
3123
|
|
|
2962
|
-
var version$1 = "1.0.
|
|
3124
|
+
var version$1 = "1.0.10";
|
|
2963
3125
|
var pkg = {
|
|
2964
3126
|
version: version$1};
|
|
2965
3127
|
|
|
@@ -2976,6 +3138,7 @@ const globalScope = (typeof globalThis !== 'undefined'
|
|
|
2976
3138
|
if (globalScope) {
|
|
2977
3139
|
// Detect the source of this instance safely across different environments
|
|
2978
3140
|
let source = 'mutts/index';
|
|
3141
|
+
// biome-ignore lint/security/noGlobalEval: Intentional eval for cross-env import.meta detection
|
|
2979
3142
|
const viteEval = eval;
|
|
2980
3143
|
try {
|
|
2981
3144
|
if (typeof __filename !== 'undefined')
|
|
@@ -3007,5 +3170,5 @@ if (globalScope) {
|
|
|
3007
3170
|
globalScope[GLOBAL_MUTTS_KEY] = currentSourceInfo;
|
|
3008
3171
|
}
|
|
3009
3172
|
|
|
3010
|
-
export { ArrayReadForward as A, setAt as B, throttle as C, Destroyable as D, Eventful as E, unreactive as F, watch as G, Indexable as I, Register as R, DestructionError as a, allocated as b, allocatedValues as c,
|
|
3011
|
-
//# sourceMappingURL=index-
|
|
3173
|
+
export { ArrayReadForward as A, setAt as B, throttle as C, Destroyable as D, Eventful as E, unreactive as F, watch as G, when as H, Indexable as I, Register as R, DestructionError as a, allocated as b, allocatedValues as c, arrayDiff as d, attend as e, cache as f, cached as g, callOnGC as h, chainPromise as i, debounce as j, deepWatch as k, deprecated as l, descriptor as m, destructor as n, forwardArray as o, getAt as p, isCached as q, lift as r, memoize as s, morph as t, organize as u, organized as v, profileInfo as w, register as x, resource as y, scan as z };
|
|
3174
|
+
//# sourceMappingURL=index-CaaQQlPJ.esm.js.map
|