@ng-org/alien-deepsignals 0.1.2-alpha.5 → 0.1.2-alpha.7
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 +55 -374
- package/dist/core.d.ts +66 -60
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +70 -82
- package/dist/deepSignal.d.ts +19 -5
- package/dist/deepSignal.d.ts.map +1 -1
- package/dist/deepSignal.js +214 -141
- package/dist/effect.d.ts.map +1 -1
- package/dist/effect.js +8 -1
- package/dist/hooks/react/useDeepSignal.d.ts +1 -1
- package/dist/hooks/react/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/react/useDeepSignal.js +5 -2
- package/dist/hooks/svelte/index.d.ts +0 -1
- package/dist/hooks/svelte/index.d.ts.map +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +3 -15
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.js +29 -63
- package/dist/hooks/svelte4/index.d.ts +4 -0
- package/dist/hooks/svelte4/index.d.ts.map +1 -0
- package/dist/hooks/svelte4/index.js +8 -0
- package/dist/hooks/svelte4/useDeepSignal.svelte.d.ts +27 -0
- package/dist/hooks/svelte4/useDeepSignal.svelte.d.ts.map +1 -0
- package/dist/hooks/svelte4/useDeepSignal.svelte.js +91 -0
- package/dist/hooks/vue/useDeepSignal.d.ts +2 -2
- package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/vue/useDeepSignal.js +22 -74
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts.map +1 -1
- package/dist/test/frontend/astro-app/src/components/{ReactPanel.js → ReactPanel.jsx} +56 -45
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.js +13 -3
- package/dist/test/frontend/utils/mockData.d.ts.map +1 -1
- package/dist/test/frontend/utils/mockData.js +13 -4
- package/dist/test/lib/index.test.js +16 -3
- package/dist/test/lib/misc.test.js +104 -1
- package/dist/test/lib/watch.test.js +2 -11
- package/dist/types.d.ts +94 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -0
- package/dist/watch.d.ts +37 -0
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +35 -0
- package/package.json +8 -5
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts +0 -4
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts.map +0 -1
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.js +0 -225
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts +0 -4
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts.map +0 -1
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.js +0 -150
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts +0 -4
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts.map +0 -1
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.js +0 -184
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts +0 -2
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts.map +0 -1
- package/dist/test/frontend/playwright/perfSuite.spec.js +0 -128
- package/dist/test/frontend/utils/perfScenarios.d.ts +0 -15
- package/dist/test/frontend/utils/perfScenarios.d.ts.map +0 -1
- package/dist/test/frontend/utils/perfScenarios.js +0 -287
- package/dist/test/lib/core.test.d.ts +0 -2
- package/dist/test/lib/core.test.d.ts.map +0 -1
- package/dist/test/lib/core.test.js +0 -53
package/dist/deepSignal.js
CHANGED
|
@@ -17,19 +17,23 @@ exports.getDeepSignalVersion = getDeepSignalVersion;
|
|
|
17
17
|
exports.shallow = shallow;
|
|
18
18
|
exports.setSetEntrySyntheticId = setSetEntrySyntheticId;
|
|
19
19
|
exports.addWithId = addWithId;
|
|
20
|
+
exports.getRaw = getRaw;
|
|
20
21
|
const core_1 = require("./core");
|
|
21
22
|
const iteratorHelpers_1 = require("./iteratorHelpers");
|
|
22
23
|
/** The current proxy object for the raw object (others might exist but are not the current / clean ones). */
|
|
23
24
|
const rawToProxy = new WeakMap();
|
|
24
25
|
const rawToMeta = new WeakMap();
|
|
25
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Mapping of raw objects to child signals.
|
|
28
|
+
* We store them globally because the same object can be attached in different locations.
|
|
29
|
+
*/
|
|
26
30
|
const propertiesToSignals = new WeakMap();
|
|
27
31
|
const iterableSignals = new WeakMap();
|
|
28
32
|
const ignored = new WeakSet();
|
|
33
|
+
// TODO: We can just add this to the root directly
|
|
29
34
|
const rootStates = new Map();
|
|
30
35
|
const pendingRoots = new Set();
|
|
31
36
|
const supported = new Set([Object, Array, Set]);
|
|
32
|
-
const descriptor = Object.getOwnPropertyDescriptor;
|
|
33
37
|
let blankNodeCounter = 0;
|
|
34
38
|
let tmpIdCounter = 0;
|
|
35
39
|
const wellKnownSymbols = new Set([
|
|
@@ -62,51 +66,89 @@ function shouldProxy(value) {
|
|
|
62
66
|
!ignored.has(value));
|
|
63
67
|
}
|
|
64
68
|
/**
|
|
65
|
-
* Get or create the map in `proxySignals` for key
|
|
69
|
+
* Get or create the map in `proxySignals` for raw object to `Map<property key, signal>`
|
|
66
70
|
*/
|
|
67
|
-
function
|
|
71
|
+
function getOrCreateSignalMap(rawObj) {
|
|
68
72
|
if (!propertiesToSignals.has(rawObj))
|
|
69
73
|
propertiesToSignals.set(rawObj, new Map());
|
|
70
74
|
return propertiesToSignals.get(rawObj);
|
|
71
75
|
}
|
|
72
76
|
/**
|
|
73
77
|
* Write a new value into a cached signal, creating it if needed.
|
|
74
|
-
* Does nothing computed signals.
|
|
78
|
+
* Does nothing on computed signals.
|
|
75
79
|
*/
|
|
76
|
-
function setSignalValue(
|
|
80
|
+
function setSignalValue(meta, key, value) {
|
|
81
|
+
const signals = propertiesToSignals.get(meta.raw);
|
|
77
82
|
if (!signals.has(key)) {
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
// Add new signal.
|
|
84
|
+
signals.set(key, {
|
|
85
|
+
alienSignal: (0, core_1.alienSignal)(value),
|
|
86
|
+
// Do not add external subscribers yet, they will be created on first read.
|
|
87
|
+
externalSubscribers: new Map(),
|
|
88
|
+
});
|
|
80
89
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
else {
|
|
91
|
+
// Update existing signal.
|
|
92
|
+
const existingSignal = signals.get(key);
|
|
93
|
+
existingSignal.alienSignal(value);
|
|
94
|
+
existingSignal.externalSubscribers
|
|
95
|
+
.values()
|
|
96
|
+
.forEach((subscriber) => subscriber.onSet(value));
|
|
84
97
|
}
|
|
85
98
|
}
|
|
86
|
-
/**
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
/** Gets the current value of a signal and notifies external subscribers about read. */
|
|
100
|
+
function getValFromSignalRecord(meta, record) {
|
|
101
|
+
const val = record.alienSignal();
|
|
102
|
+
// Notify external subscribers (usually frontend frameworks) about property access.
|
|
103
|
+
meta.options.subscriberFactories?.forEach((createSubscriber) => {
|
|
104
|
+
let subscriber = record.externalSubscribers.get(createSubscriber);
|
|
105
|
+
if (!subscriber) {
|
|
106
|
+
// Create new subscriber
|
|
107
|
+
subscriber = createSubscriber();
|
|
108
|
+
record.externalSubscribers.set(createSubscriber, subscriber);
|
|
109
|
+
}
|
|
110
|
+
subscriber.onGet();
|
|
111
|
+
});
|
|
112
|
+
return val;
|
|
113
|
+
}
|
|
114
|
+
/** Sets the current value of a signal and notifies external subscribers about update. */
|
|
115
|
+
function setValToSignalRecord(record, value) {
|
|
116
|
+
record.alienSignal(value);
|
|
117
|
+
// Notify external subscribers (usually frontend frameworks) about property update.
|
|
118
|
+
record.externalSubscribers.forEach((subscriber) => subscriber.onSet(value));
|
|
119
|
+
}
|
|
120
|
+
/** Track mutations that affect object iteration order/length with a counter signal. */
|
|
121
|
+
function ensureIterableSignal(meta, target) {
|
|
122
|
+
// Create new one if none exists.
|
|
123
|
+
if (!iterableSignals.has(target)) {
|
|
124
|
+
iterableSignals.set(target, {
|
|
125
|
+
alienSignal: (0, core_1.alienSignal)(0),
|
|
126
|
+
externalSubscribers: new Map(),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const signal = iterableSignals.get(target);
|
|
130
|
+
getValFromSignalRecord(meta, signal);
|
|
91
131
|
}
|
|
92
|
-
/** Notify all iteration-based subscribers that the container changed shape. */
|
|
93
|
-
function touchIterable(target) {
|
|
132
|
+
/** Notify all iteration-based subscribers that the container changed shape, by increasing the counter signal. */
|
|
133
|
+
function touchIterable(meta, target) {
|
|
94
134
|
if (!iterableSignals.has(target))
|
|
95
135
|
return;
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
function hasGetter(target, key) {
|
|
101
|
-
return typeof descriptor(target, key)?.get === "function";
|
|
136
|
+
const signalRecord = iterableSignals.get(target);
|
|
137
|
+
const oldCountVal = getValFromSignalRecord(meta, signalRecord);
|
|
138
|
+
const newVal = oldCountVal + 1;
|
|
139
|
+
setValToSignalRecord(signalRecord, newVal);
|
|
102
140
|
}
|
|
103
141
|
/**
|
|
104
|
-
*
|
|
105
|
-
* TODO: Why is this necessary?
|
|
142
|
+
* Create computed signals for getter functions.
|
|
106
143
|
*/
|
|
107
|
-
function
|
|
108
|
-
if (!signals.has(key) &&
|
|
109
|
-
|
|
144
|
+
function ensureProxiedGetter(signals, target, key, receiver) {
|
|
145
|
+
if (!signals.has(key) &&
|
|
146
|
+
typeof Object.getOwnPropertyDescriptor(target, key)?.get === "function" // If we have a getter?
|
|
147
|
+
) {
|
|
148
|
+
signals.set(key, {
|
|
149
|
+
alienSignal: (0, core_1.computed)(() => Reflect.get(target, key, receiver)),
|
|
150
|
+
externalSubscribers: new Map(),
|
|
151
|
+
});
|
|
110
152
|
}
|
|
111
153
|
}
|
|
112
154
|
/** Escape JSON-pointer-like path segments so patches remain unambiguous. */
|
|
@@ -121,18 +163,23 @@ function isReactiveSymbol(key) {
|
|
|
121
163
|
return typeof key === "symbol" && !wellKnownSymbols.has(key);
|
|
122
164
|
}
|
|
123
165
|
/**
|
|
124
|
-
* Replaces the old
|
|
166
|
+
* Replaces the old rawToProxy object with a proxy object.
|
|
125
167
|
* Used so that we can indicate modifications along the path of a change by equality checks.
|
|
168
|
+
* All values remain the same.
|
|
169
|
+
*
|
|
170
|
+
* Does nothing if the deep signal's options `replaceProxiesInBranchOnChange` is false.
|
|
126
171
|
*/
|
|
127
|
-
function
|
|
128
|
-
if (!meta.parent ||
|
|
172
|
+
function replaceProxyMaybe(meta) {
|
|
173
|
+
if (!meta.parent ||
|
|
174
|
+
!meta.key ||
|
|
175
|
+
!meta.options.replaceProxiesInBranchOnChange)
|
|
129
176
|
return;
|
|
130
177
|
// Create a new proxy for this raw object -- frontend libs like react need this to recognize changes along this path.
|
|
131
178
|
const handlers = meta.raw instanceof Set ? setHandlers : objectHandlers;
|
|
132
179
|
const proxy = new Proxy(meta.raw, handlers);
|
|
133
180
|
rawToProxy.set(meta.raw, proxy);
|
|
134
|
-
const signal =
|
|
135
|
-
signal?.(proxy);
|
|
181
|
+
const signal = getOrCreateSignalMap(meta.parent.raw).get(meta.key);
|
|
182
|
+
signal?.alienSignal(proxy);
|
|
136
183
|
}
|
|
137
184
|
/**
|
|
138
185
|
* Walk the metadata chain to build the patch path for a property access.
|
|
@@ -160,7 +207,7 @@ function buildPath(meta, key, skipEscape = false) {
|
|
|
160
207
|
let cursor = meta;
|
|
161
208
|
while (cursor && cursor.parent && cursor.key !== undefined) {
|
|
162
209
|
push(cursor.key, !!cursor.isSyntheticId);
|
|
163
|
-
|
|
210
|
+
replaceProxyMaybe(cursor);
|
|
164
211
|
cursor = cursor.parent;
|
|
165
212
|
}
|
|
166
213
|
return path;
|
|
@@ -169,7 +216,7 @@ function buildPath(meta, key, skipEscape = false) {
|
|
|
169
216
|
function resolveContainerPath(meta) {
|
|
170
217
|
if (!meta || !meta.parent || meta.key === undefined)
|
|
171
218
|
return [];
|
|
172
|
-
|
|
219
|
+
replaceProxyMaybe(meta);
|
|
173
220
|
return buildPath(meta.parent, meta.key, !!meta.isSyntheticId);
|
|
174
221
|
}
|
|
175
222
|
/**
|
|
@@ -293,16 +340,17 @@ function initializeObjectTreeIfNoListeners(meta, basePath, value, inSet) {
|
|
|
293
340
|
initializeObjectTree(meta, value, basePath ?? [], inSet);
|
|
294
341
|
}
|
|
295
342
|
/**
|
|
296
|
-
* Return (or create) a proxy for a
|
|
343
|
+
* Return (or create) a proxy for a property value.
|
|
297
344
|
* Ensures the linkage between parent and child in metadata.
|
|
298
345
|
* Does not proxy and returns `value` if @see shouldProxy returns false.
|
|
299
|
-
*
|
|
346
|
+
* Assumes parent has proxy.
|
|
300
347
|
*/
|
|
301
348
|
function ensureChildProxy(rawChild, parent, key, isSyntheticId = false) {
|
|
302
349
|
if (!shouldProxy(rawChild))
|
|
303
350
|
return rawChild;
|
|
304
351
|
const parentRaw = parent[RAW_KEY] || parent;
|
|
305
|
-
const parentMeta = rawToMeta
|
|
352
|
+
const parentMeta = rawToMeta.get(parentRaw);
|
|
353
|
+
// Child is already proxied, ensure the linkage from parent to child.
|
|
306
354
|
if (rawToProxy.has(rawChild)) {
|
|
307
355
|
const proxied = rawToProxy.get(rawChild);
|
|
308
356
|
const proxiedMeta = rawToMeta.get(rawChild);
|
|
@@ -313,8 +361,6 @@ function ensureChildProxy(rawChild, parent, key, isSyntheticId = false) {
|
|
|
313
361
|
}
|
|
314
362
|
return proxied;
|
|
315
363
|
}
|
|
316
|
-
if (!parentMeta)
|
|
317
|
-
return rawChild;
|
|
318
364
|
// Create proxy if none exists yet.
|
|
319
365
|
const proxy = createProxy(rawChild, parentMeta.root, parentMeta.options, parentMeta, key, isSyntheticId);
|
|
320
366
|
return proxy;
|
|
@@ -336,8 +382,8 @@ function ensureSetInfo(meta) {
|
|
|
336
382
|
* Assign (or reuse) a synthetic identifier for a Set entry, respecting user options:
|
|
337
383
|
* - Use user-provided propGenerator for synthetic ids and add add returned extra properties
|
|
338
384
|
* - Check if the object has a property of `syntheticIdPropertyName` (default `@id`)
|
|
339
|
-
* - Use a blank node
|
|
340
|
-
* - Add object and
|
|
385
|
+
* - Use a blank node ID as a fallback.
|
|
386
|
+
* - Add object and ID to `idForObject` and `objectForId` maps.
|
|
341
387
|
*/
|
|
342
388
|
function assignSyntheticId(meta, entry, path, inSet) {
|
|
343
389
|
const rawEntry = entry?.[RAW_KEY] ?? entry;
|
|
@@ -410,7 +456,7 @@ function createProxy(target, root, options, parentMeta, key, isSyntheticId) {
|
|
|
410
456
|
root,
|
|
411
457
|
options,
|
|
412
458
|
};
|
|
413
|
-
|
|
459
|
+
getOrCreateSignalMap(target);
|
|
414
460
|
rawToMeta.set(target, meta);
|
|
415
461
|
rawToProxy.set(target, proxy);
|
|
416
462
|
return proxy;
|
|
@@ -507,25 +553,22 @@ function emitPatchesForNew(value, meta, basePath, inSet = false) {
|
|
|
507
553
|
* and dependent effects are notified. Recreates/proxies values for indices < length
|
|
508
554
|
* and clears signals for indices >= length.
|
|
509
555
|
*/
|
|
510
|
-
function refreshNumericIndexSignals(target, receiver) {
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
return;
|
|
514
|
-
for (const k of Array.from(sigs.keys())) {
|
|
556
|
+
function refreshNumericIndexSignals(meta, target, receiver) {
|
|
557
|
+
const signals = getOrCreateSignalMap(meta.raw);
|
|
558
|
+
for (const k of Array.from(signals.keys())) {
|
|
515
559
|
if (typeof k === "string" && /^\d+$/.test(k)) {
|
|
516
560
|
const idx = Number(k);
|
|
517
561
|
if (idx >= target.length) {
|
|
518
|
-
const existing =
|
|
519
|
-
if (existing
|
|
520
|
-
|
|
521
|
-
existing.set(undefined);
|
|
562
|
+
const existing = signals.get(k);
|
|
563
|
+
if (existing) {
|
|
564
|
+
setSignalValue(meta, k, undefined);
|
|
522
565
|
}
|
|
523
|
-
|
|
566
|
+
signals.delete(k);
|
|
524
567
|
}
|
|
525
568
|
else {
|
|
526
569
|
const val = target[idx];
|
|
527
570
|
const proxied = ensureChildProxy(val, receiver, idx);
|
|
528
|
-
setSignalValue(
|
|
571
|
+
setSignalValue(meta, k, proxied);
|
|
529
572
|
}
|
|
530
573
|
}
|
|
531
574
|
}
|
|
@@ -559,7 +602,7 @@ const getArrayMutationProxy = (target, key, receiver) => {
|
|
|
559
602
|
// Update length of proxy explicitly.
|
|
560
603
|
receiver.length = target.length;
|
|
561
604
|
// Refresh numeric index signals so shifted indices don't return stale values.
|
|
562
|
-
refreshNumericIndexSignals(target, receiver);
|
|
605
|
+
refreshNumericIndexSignals(meta, target, receiver);
|
|
563
606
|
};
|
|
564
607
|
}
|
|
565
608
|
else if (key === "splice") {
|
|
@@ -591,7 +634,7 @@ const getArrayMutationProxy = (target, key, receiver) => {
|
|
|
591
634
|
ensureChildProxy(items[i], target, start + i);
|
|
592
635
|
}
|
|
593
636
|
// Refresh numeric index signals so shifted indices don't return stale values.
|
|
594
|
-
refreshNumericIndexSignals(target, receiver);
|
|
637
|
+
refreshNumericIndexSignals(meta, target, receiver);
|
|
595
638
|
// Update length of proxy explicitly.
|
|
596
639
|
receiver.length = target.length;
|
|
597
640
|
return deletedItems;
|
|
@@ -619,7 +662,7 @@ const getArrayMutationProxy = (target, key, receiver) => {
|
|
|
619
662
|
// Update length of proxy explicitly.
|
|
620
663
|
receiver.length = target.length;
|
|
621
664
|
// Refresh numeric index signals so shifted indices don't return stale values.
|
|
622
|
-
refreshNumericIndexSignals(target, receiver);
|
|
665
|
+
refreshNumericIndexSignals(meta, target, receiver);
|
|
623
666
|
return deletedItems;
|
|
624
667
|
};
|
|
625
668
|
}
|
|
@@ -632,13 +675,14 @@ const objectHandlers = {
|
|
|
632
675
|
return target;
|
|
633
676
|
if (key === META_KEY)
|
|
634
677
|
return rawToMeta.get(target);
|
|
635
|
-
|
|
678
|
+
const meta = rawToMeta.get(target);
|
|
636
679
|
if (typeof key === "symbol") {
|
|
637
680
|
if (key === Symbol.iterator) {
|
|
638
|
-
|
|
639
|
-
|
|
681
|
+
// Ensure that signal exists tracking iteration-based dependencies.
|
|
682
|
+
ensureIterableSignal(meta, target);
|
|
640
683
|
}
|
|
641
684
|
if (!isReactiveSymbol(key))
|
|
685
|
+
// No reactivity for well-known symbols
|
|
642
686
|
return Reflect.get(target, key, receiver);
|
|
643
687
|
}
|
|
644
688
|
// Array helper handling. We need that because otherwise, every array position change would go as a separate operation through the proxy.
|
|
@@ -650,20 +694,20 @@ const objectHandlers = {
|
|
|
650
694
|
}
|
|
651
695
|
}
|
|
652
696
|
// Get object map from key to signal.
|
|
653
|
-
const signals =
|
|
654
|
-
//
|
|
655
|
-
|
|
697
|
+
const signals = getOrCreateSignalMap(meta.raw);
|
|
698
|
+
// If the property value is a getter, create a computed signal for it.
|
|
699
|
+
ensureProxiedGetter(signals, target, key, receiver);
|
|
656
700
|
// Add signal if it does not exist already and did not have a getter.
|
|
657
701
|
if (!signals.has(key)) {
|
|
658
702
|
let rawChild = Reflect.get(target, key, receiver);
|
|
659
703
|
if (typeof rawChild === "function")
|
|
660
704
|
return rawChild.bind(receiver ?? target);
|
|
661
705
|
const childProxyOrRaw = ensureChildProxy(rawChild, receiver, key);
|
|
662
|
-
|
|
706
|
+
setSignalValue(meta, key, childProxyOrRaw);
|
|
663
707
|
}
|
|
664
|
-
// Call and return signal
|
|
708
|
+
// Call and return signal.
|
|
665
709
|
const sig = signals.get(key);
|
|
666
|
-
return sig
|
|
710
|
+
return getValFromSignalRecord(meta, sig);
|
|
667
711
|
},
|
|
668
712
|
set(target, key, value, receiver) {
|
|
669
713
|
// Skip reactivity for symbols.
|
|
@@ -674,9 +718,6 @@ const objectHandlers = {
|
|
|
674
718
|
throw new Error(`Cannot modify readonly property '${String(key)}'`);
|
|
675
719
|
}
|
|
676
720
|
const path = meta ? buildPath(meta, key) : undefined;
|
|
677
|
-
const desc = descriptor(target, key);
|
|
678
|
-
const hasAccessor = !!desc &&
|
|
679
|
-
(typeof desc.get === "function" || typeof desc.set === "function");
|
|
680
721
|
const proxied = ensureChildProxy(value, target, key);
|
|
681
722
|
const rawValue = value?.[RAW_KEY] ?? value;
|
|
682
723
|
const hadKey = Object.prototype.hasOwnProperty.call(target, key);
|
|
@@ -690,12 +731,10 @@ const objectHandlers = {
|
|
|
690
731
|
}
|
|
691
732
|
// === Set value on actual target ===
|
|
692
733
|
const result = Reflect.set(target, key, rawValue, receiver);
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
setSignalValue(signals, key, proxied);
|
|
696
|
-
}
|
|
734
|
+
// Set signal value.
|
|
735
|
+
setSignalValue(meta, key, proxied);
|
|
697
736
|
if (!hadKey)
|
|
698
|
-
touchIterable(target);
|
|
737
|
+
touchIterable(meta, target);
|
|
699
738
|
if (meta && path && typeof rawValue === "object") {
|
|
700
739
|
initializeObjectTreeIfNoListeners(meta, path, rawValue, false);
|
|
701
740
|
}
|
|
@@ -738,18 +777,15 @@ const objectHandlers = {
|
|
|
738
777
|
const hadKey = Object.prototype.hasOwnProperty.call(target, key);
|
|
739
778
|
const result = Reflect.deleteProperty(target, key);
|
|
740
779
|
if (hadKey) {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
existing.set(undefined);
|
|
748
|
-
}
|
|
749
|
-
signals.delete(key);
|
|
750
|
-
}
|
|
780
|
+
// Trigger signal
|
|
781
|
+
const signals = getOrCreateSignalMap(meta.raw);
|
|
782
|
+
const existing = signals.get(key);
|
|
783
|
+
if (existing)
|
|
784
|
+
setValToSignalRecord(existing, undefined);
|
|
785
|
+
signals.delete(key);
|
|
751
786
|
// Notify listeners
|
|
752
|
-
touchIterable(target);
|
|
787
|
+
touchIterable(meta, target);
|
|
788
|
+
// Schedule remove patch.
|
|
753
789
|
schedulePatch(meta, () => ({
|
|
754
790
|
path: buildPath(meta, key),
|
|
755
791
|
op: "remove",
|
|
@@ -758,30 +794,22 @@ const objectHandlers = {
|
|
|
758
794
|
return result;
|
|
759
795
|
},
|
|
760
796
|
ownKeys(target) {
|
|
761
|
-
const
|
|
762
|
-
sig();
|
|
797
|
+
const meta = rawToMeta.get(target);
|
|
798
|
+
const sig = ensureIterableSignal(meta, target);
|
|
763
799
|
return Reflect.ownKeys(target);
|
|
764
800
|
},
|
|
765
801
|
};
|
|
766
|
-
/**
|
|
767
|
-
* Guarantee Set iteration always surfaces proxies, even when raw values were stored.
|
|
768
|
-
*/
|
|
769
|
-
function ensureEntryProxy(raw, entry, syntheticKey, meta) {
|
|
770
|
-
return shouldProxy(entry)
|
|
771
|
-
? ensureChildProxy(entry, raw, syntheticKey, true)
|
|
772
|
-
: entry;
|
|
773
|
-
}
|
|
774
802
|
/** Wrap the underlying Set iterator so each value is proxied before leaving the trap. */
|
|
775
803
|
function createSetIterator(target, receiver, mapValue) {
|
|
804
|
+
const meta = rawToMeta.get(target);
|
|
776
805
|
const iterator = target.values();
|
|
777
|
-
|
|
778
|
-
iterableSignal();
|
|
806
|
+
ensureIterableSignal(meta, target);
|
|
779
807
|
return (0, iteratorHelpers_1.createIteratorWithHelpers)(() => {
|
|
780
808
|
const next = iterator.next();
|
|
781
809
|
if (next.done)
|
|
782
810
|
return next;
|
|
783
811
|
const meta = rawToMeta.get(target);
|
|
784
|
-
const proxied =
|
|
812
|
+
const proxied = ensureChildProxy(next.value, target, assignSyntheticId(meta, next.value, [], true), true);
|
|
785
813
|
return {
|
|
786
814
|
value: mapValue(proxied),
|
|
787
815
|
done: false,
|
|
@@ -797,36 +825,33 @@ const setHandlers = {
|
|
|
797
825
|
if (key === META_KEY)
|
|
798
826
|
return meta;
|
|
799
827
|
if (key === "size") {
|
|
800
|
-
|
|
801
|
-
sig();
|
|
828
|
+
ensureIterableSignal(meta, target);
|
|
802
829
|
return target.size;
|
|
803
830
|
}
|
|
804
831
|
if (key === "first") {
|
|
805
832
|
return function first() {
|
|
806
|
-
const iterableSig = ensureIterableSignal(target);
|
|
807
|
-
iterableSig();
|
|
808
833
|
const iterator = target.values().next();
|
|
809
834
|
if (iterator.done)
|
|
810
835
|
return undefined;
|
|
811
|
-
|
|
836
|
+
const proxy = ensureChildProxy(iterator.value, target, assignSyntheticId(meta, iterator.value, [], true), true);
|
|
837
|
+
ensureIterableSignal(meta, target);
|
|
838
|
+
return proxy;
|
|
812
839
|
};
|
|
813
840
|
}
|
|
814
841
|
if (key === "getById") {
|
|
815
842
|
return function getById(id) {
|
|
816
|
-
const iterableSig = ensureIterableSignal(target);
|
|
817
|
-
iterableSig();
|
|
818
843
|
if (!meta?.setInfo)
|
|
819
844
|
return undefined;
|
|
820
845
|
const entry = meta.setInfo.objectForId.get(String(id));
|
|
821
846
|
if (!entry)
|
|
822
847
|
return undefined;
|
|
823
|
-
|
|
848
|
+
const proxy = ensureChildProxy(entry, target, String(id));
|
|
849
|
+
ensureIterableSignal(meta, target);
|
|
850
|
+
return proxy;
|
|
824
851
|
};
|
|
825
852
|
}
|
|
826
853
|
if (key === "getBy") {
|
|
827
854
|
return function getBy(graphIri, subjectIri) {
|
|
828
|
-
const iterableSig = ensureIterableSignal(target);
|
|
829
|
-
iterableSig();
|
|
830
855
|
return this.getById(`${graphIri}|${subjectIri}`);
|
|
831
856
|
};
|
|
832
857
|
}
|
|
@@ -834,26 +859,30 @@ const setHandlers = {
|
|
|
834
859
|
return function add(value) {
|
|
835
860
|
const containerPath = resolveContainerPath(meta);
|
|
836
861
|
const rawValue = value[RAW_KEY] ?? value;
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
862
|
+
if (target.has(rawValue)) {
|
|
863
|
+
// Nothing to do.
|
|
864
|
+
return receiver;
|
|
865
|
+
}
|
|
866
|
+
target.add(rawValue);
|
|
867
|
+
if (rawValue && typeof rawValue === "object") {
|
|
868
|
+
// Case: Object in set
|
|
869
|
+
const synthetic = assignSyntheticId(meta, rawValue, containerPath, true);
|
|
870
|
+
initializeObjectTreeIfNoListeners(meta, [...containerPath, synthetic], rawValue, true);
|
|
871
|
+
ensureChildProxy(rawValue, target, synthetic, true);
|
|
872
|
+
touchIterable(meta, target);
|
|
873
|
+
schedulePatch(meta, () => emitPatchesForNew(rawValue, meta, [...containerPath, synthetic], true));
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
// Case: Literal in set
|
|
877
|
+
touchIterable(meta, target);
|
|
878
|
+
const literal = snapshotLiteral(rawValue);
|
|
879
|
+
if (literal !== undefined) {
|
|
880
|
+
schedulePatch(meta, () => ({
|
|
881
|
+
path: containerPath,
|
|
882
|
+
op: "add",
|
|
883
|
+
type: "set",
|
|
884
|
+
value: [literal],
|
|
885
|
+
}));
|
|
857
886
|
}
|
|
858
887
|
}
|
|
859
888
|
return receiver;
|
|
@@ -861,14 +890,16 @@ const setHandlers = {
|
|
|
861
890
|
}
|
|
862
891
|
if (key === "delete") {
|
|
863
892
|
return function deleteEntry(value) {
|
|
864
|
-
const containerPath = resolveContainerPath(meta);
|
|
865
893
|
const rawValue = value?.[RAW_KEY] ?? value;
|
|
866
894
|
const synthetic = rawValue && typeof rawValue === "object"
|
|
867
895
|
? ensureSetInfo(meta).idForObject.get(rawValue)
|
|
868
896
|
: rawValue;
|
|
869
897
|
const existed = target.delete(rawValue);
|
|
898
|
+
if (existed) {
|
|
899
|
+
touchIterable(meta, target);
|
|
900
|
+
}
|
|
870
901
|
if (existed && synthetic !== undefined) {
|
|
871
|
-
|
|
902
|
+
const containerPath = resolveContainerPath(meta);
|
|
872
903
|
if (rawValue && typeof rawValue === "object") {
|
|
873
904
|
schedulePatch(meta, () => ({
|
|
874
905
|
path: [...containerPath, synthetic],
|
|
@@ -893,13 +924,16 @@ const setHandlers = {
|
|
|
893
924
|
}
|
|
894
925
|
if (key === "clear") {
|
|
895
926
|
return function clear() {
|
|
927
|
+
// Nothing to do.
|
|
928
|
+
if (target.size === 0)
|
|
929
|
+
return;
|
|
896
930
|
const containerPath = resolveContainerPath(meta);
|
|
897
931
|
if (meta.setInfo) {
|
|
898
932
|
meta.setInfo.objectForId.clear();
|
|
899
933
|
meta.setInfo.idForObject = new WeakMap();
|
|
900
934
|
}
|
|
901
935
|
target.clear();
|
|
902
|
-
touchIterable(target);
|
|
936
|
+
touchIterable(meta, target);
|
|
903
937
|
schedulePatch(meta, () => ({
|
|
904
938
|
path: containerPath,
|
|
905
939
|
op: "add",
|
|
@@ -938,13 +972,12 @@ const setHandlers = {
|
|
|
938
972
|
}
|
|
939
973
|
if (key === "forEach") {
|
|
940
974
|
return function forEach(callback, thisArg) {
|
|
941
|
-
|
|
942
|
-
iterableSig();
|
|
975
|
+
ensureIterableSignal(meta, target);
|
|
943
976
|
let index = 0;
|
|
944
977
|
const expectsIteratorSignature = callback.length <= 2;
|
|
945
978
|
const iteratorCallback = callback;
|
|
946
979
|
target.forEach((entry) => {
|
|
947
|
-
const proxied =
|
|
980
|
+
const proxied = ensureChildProxy(entry, target, assignSyntheticId(meta, entry, [], true), true);
|
|
948
981
|
if (expectsIteratorSignature) {
|
|
949
982
|
iteratorCallback.call(thisArg, proxied, index++);
|
|
950
983
|
}
|
|
@@ -956,10 +989,23 @@ const setHandlers = {
|
|
|
956
989
|
}
|
|
957
990
|
if (key === "has") {
|
|
958
991
|
return function has(value) {
|
|
992
|
+
ensureIterableSignal(meta, target);
|
|
959
993
|
return target.has(value?.[RAW_KEY] ?? value);
|
|
960
994
|
};
|
|
961
995
|
}
|
|
962
|
-
|
|
996
|
+
// All other cases:
|
|
997
|
+
const res = target[key];
|
|
998
|
+
if (typeof res === "function") {
|
|
999
|
+
// For all other functions on sets, return a wrapped function
|
|
1000
|
+
// that calls ensureIterableSignal() before.
|
|
1001
|
+
return (...props) => {
|
|
1002
|
+
ensureIterableSignal(meta, target);
|
|
1003
|
+
return res.bind(target)(...props);
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
return res;
|
|
1008
|
+
}
|
|
963
1009
|
},
|
|
964
1010
|
};
|
|
965
1011
|
/** Runtime guard that checks whether a value is a deepSignal proxy. */
|
|
@@ -967,13 +1013,27 @@ function isDeepSignal(value) {
|
|
|
967
1013
|
return !!value?.[RAW_KEY];
|
|
968
1014
|
}
|
|
969
1015
|
/**
|
|
970
|
-
*
|
|
971
|
-
*
|
|
972
|
-
*
|
|
1016
|
+
* MAIN ENTRY POINT to create a deep reactive proxy for objects, arrays or Sets.
|
|
1017
|
+
*
|
|
1018
|
+
* If input is a deepSignal already and options are provided,
|
|
1019
|
+
* the added subscriberFactories are joined with the existing ones
|
|
1020
|
+
* and `replaceProxiesInBranchOnChange` is or-ed with the current value.
|
|
1021
|
+
*
|
|
1022
|
+
*
|
|
1023
|
+
* @throws if provided with unsupported input types.
|
|
973
1024
|
*/
|
|
974
1025
|
function deepSignal(input, options) {
|
|
975
|
-
|
|
1026
|
+
// Is the input already a signal?
|
|
1027
|
+
if (isDeepSignal(input)) {
|
|
1028
|
+
// Add possibly new external subscribers to existing ones.
|
|
1029
|
+
const meta = rawToMeta.get(input[RAW_KEY]);
|
|
1030
|
+
meta.options.subscriberFactories =
|
|
1031
|
+
meta.options.subscriberFactories.union(options?.subscriberFactories ?? new Set());
|
|
1032
|
+
meta.options.replaceProxiesInBranchOnChange =
|
|
1033
|
+
meta?.options.replaceProxiesInBranchOnChange ||
|
|
1034
|
+
options?.replaceProxiesInBranchOnChange;
|
|
976
1035
|
return input;
|
|
1036
|
+
}
|
|
977
1037
|
if (!shouldProxy(input))
|
|
978
1038
|
throw new Error("deepSignal() expects an object, array, or Set");
|
|
979
1039
|
if (rawToProxy.has(input))
|
|
@@ -982,6 +1042,8 @@ function deepSignal(input, options) {
|
|
|
982
1042
|
const rootState = {
|
|
983
1043
|
options: {
|
|
984
1044
|
syntheticIdPropertyName: DEFAULT_SYNTHETIC_ID_PROPERTY_NAME,
|
|
1045
|
+
replaceProxiesInBranchOnChange: false,
|
|
1046
|
+
subscriberFactories: new Set(),
|
|
985
1047
|
...options,
|
|
986
1048
|
},
|
|
987
1049
|
version: 0,
|
|
@@ -1030,7 +1092,14 @@ function getDeepSignalVersion(root) {
|
|
|
1030
1092
|
return undefined;
|
|
1031
1093
|
return rootStates.get(rootId)?.version;
|
|
1032
1094
|
}
|
|
1033
|
-
/** Mark an object so deepSignal skips proxying it (shallow boundary).
|
|
1095
|
+
/** Mark an object so deepSignal skips proxying it (shallow boundary).
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* ```ts
|
|
1099
|
+
* import { shallow } from "alien-deepsignals";
|
|
1100
|
+
* state.config = shallow({ huge: { blob: true } });
|
|
1101
|
+
* ```
|
|
1102
|
+
*/
|
|
1034
1103
|
function shallow(obj) {
|
|
1035
1104
|
ignored.add(obj);
|
|
1036
1105
|
return obj;
|
|
@@ -1058,3 +1127,7 @@ function addWithId(set, entry, id) {
|
|
|
1058
1127
|
}
|
|
1059
1128
|
return entry;
|
|
1060
1129
|
}
|
|
1130
|
+
/** Get the original, raw value of a deep signal. */
|
|
1131
|
+
function getRaw(value) {
|
|
1132
|
+
return value?.[RAW_KEY] ?? value;
|
|
1133
|
+
}
|
package/dist/effect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../src/effect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../src/effect.ts"],"names":[],"mappings":"AAYA,KAAK,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAErD,sEAAsE;AACtE,wBAAgB,MAAM,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,eAAe,KAAK,IAAI,cA2BtE"}
|