@ng-org/alien-deepsignals 0.1.2-alpha.2 → 0.1.2-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deepSignal.d.ts.map +1 -1
- package/dist/deepSignal.js +244 -100
- package/dist/hooks/react/useDeepSignal.d.ts +4 -4
- package/dist/hooks/react/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/react/useDeepSignal.js +22 -15
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +3 -7
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.js +34 -18
- package/dist/hooks/vue/useDeepSignal.d.ts +4 -3
- package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/vue/useDeepSignal.js +52 -24
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.js +225 -0
- package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/ReactPanel.js +227 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.js +150 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.js +184 -0
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts +2 -0
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts.map +1 -0
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.js +171 -0
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts +2 -0
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts.map +1 -0
- package/dist/test/frontend/playwright/perfSuite.spec.js +128 -0
- package/dist/test/frontend/utils/mockData.d.ts +53 -0
- package/dist/test/frontend/utils/mockData.d.ts.map +1 -0
- package/dist/test/frontend/utils/mockData.js +78 -0
- package/dist/test/frontend/utils/paths.d.ts +4 -0
- package/dist/test/frontend/utils/paths.d.ts.map +1 -0
- package/dist/test/frontend/utils/paths.js +28 -0
- package/dist/test/frontend/utils/perfScenarios.d.ts +15 -0
- package/dist/test/frontend/utils/perfScenarios.d.ts.map +1 -0
- package/dist/test/frontend/utils/perfScenarios.js +287 -0
- package/dist/test/frontend/utils/renderMetrics.d.ts +13 -0
- package/dist/test/frontend/utils/renderMetrics.d.ts.map +1 -0
- package/dist/test/frontend/utils/renderMetrics.js +45 -0
- package/dist/test/frontend/utils/state.d.ts +57 -0
- package/dist/test/frontend/utils/state.d.ts.map +1 -0
- package/dist/test/frontend/utils/state.js +79 -0
- package/dist/test/lib/core.test.d.ts +2 -0
- package/dist/test/lib/core.test.d.ts.map +1 -0
- package/dist/test/lib/core.test.js +53 -0
- package/dist/test/lib/deepSignalOptions.test.d.ts +2 -0
- package/dist/test/lib/deepSignalOptions.test.d.ts.map +1 -0
- package/dist/test/lib/deepSignalOptions.test.js +230 -0
- package/dist/test/lib/index.test.d.ts +2 -0
- package/dist/test/lib/index.test.d.ts.map +1 -0
- package/dist/test/lib/index.test.js +807 -0
- package/dist/test/lib/misc.test.d.ts +2 -0
- package/dist/test/lib/misc.test.d.ts.map +1 -0
- package/dist/test/lib/misc.test.js +140 -0
- package/dist/test/lib/watch.test.d.ts +2 -0
- package/dist/test/lib/watch.test.d.ts.map +1 -0
- package/dist/test/lib/watch.test.js +1280 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +36 -19
- package/src/index.ts +5 -0
package/dist/deepSignal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepSignal.d.ts","sourceRoot":"","sources":["../src/deepSignal.ts"],"names":[],"mappings":"AAWA,OAAO,EAGH,sBAAsB,EACtB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EAMpB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"deepSignal.d.ts","sourceRoot":"","sources":["../src/deepSignal.ts"],"names":[],"mappings":"AAWA,OAAO,EAGH,sBAAsB,EACtB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EAMpB,MAAM,SAAS,CAAC;AA+qCjB,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACvC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,iBAAiB,GAC5B,UAAU,CAAC,CAAC,CAAC,CAwBf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAClC,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,EAAE,EAAE,mBAAmB,GAAG,sBAAsB,EAChD,gBAAgB,GAAE,OAAe,GAClC,MAAM,IAAI,CAoBZ;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAElE;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAChC,IAAI,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM,GAAG,SAAS,CAIpB;AAED,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAGnD;AAED,mFAAmF;AACnF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,QAItE;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,CAa1E"}
|
package/dist/deepSignal.js
CHANGED
|
@@ -19,9 +19,11 @@ exports.setSetEntrySyntheticId = setSetEntrySyntheticId;
|
|
|
19
19
|
exports.addWithId = addWithId;
|
|
20
20
|
const core_1 = require("./core");
|
|
21
21
|
const iteratorHelpers_1 = require("./iteratorHelpers");
|
|
22
|
+
/** The current proxy object for the raw object (others might exist but are not the current / clean ones). */
|
|
22
23
|
const rawToProxy = new WeakMap();
|
|
23
|
-
const
|
|
24
|
-
|
|
24
|
+
const rawToMeta = new WeakMap();
|
|
25
|
+
// TODO: We can move them to the meta objects.
|
|
26
|
+
const propertiesToSignals = new WeakMap();
|
|
25
27
|
const iterableSignals = new WeakMap();
|
|
26
28
|
const ignored = new WeakSet();
|
|
27
29
|
const rootStates = new Map();
|
|
@@ -29,6 +31,7 @@ const pendingRoots = new Set();
|
|
|
29
31
|
const supported = new Set([Object, Array, Set]);
|
|
30
32
|
const descriptor = Object.getOwnPropertyDescriptor;
|
|
31
33
|
let blankNodeCounter = 0;
|
|
34
|
+
let tmpIdCounter = 0;
|
|
32
35
|
const wellKnownSymbols = new Set([
|
|
33
36
|
Symbol.asyncDispose,
|
|
34
37
|
Symbol.asyncIterator,
|
|
@@ -51,37 +54,20 @@ const forcedSyntheticIds = new WeakMap();
|
|
|
51
54
|
const META_KEY = "__meta__";
|
|
52
55
|
const RAW_KEY = "__raw__";
|
|
53
56
|
const DEFAULT_SYNTHETIC_ID_PROPERTY_NAME = "@id";
|
|
54
|
-
/**
|
|
55
|
-
* Lookup the metadata record backing a proxy (if the value is proxied).
|
|
56
|
-
* Returns undefined for non-object inputs or values that never went through deepSignal().
|
|
57
|
-
*/
|
|
58
|
-
function getMeta(target) {
|
|
59
|
-
if (!target || typeof target !== "object")
|
|
60
|
-
return undefined;
|
|
61
|
-
return proxyToMeta.get(target);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Resolve the raw underlying object for a possibly proxied value.
|
|
65
|
-
* Falls back to the value itself when no proxy metadata is attached.
|
|
66
|
-
*/
|
|
67
|
-
function getRaw(value) {
|
|
68
|
-
const meta = getMeta(value);
|
|
69
|
-
return meta?.raw ?? value;
|
|
70
|
-
}
|
|
71
57
|
/** Returns `true` if `value` is an object, array or set and is not in `ignored`. */
|
|
72
58
|
function shouldProxy(value) {
|
|
73
59
|
return (!!value &&
|
|
74
60
|
typeof value === "object" &&
|
|
75
|
-
supported.has(value.constructor) &&
|
|
61
|
+
(supported.has(value.constructor) || value instanceof Set) &&
|
|
76
62
|
!ignored.has(value));
|
|
77
63
|
}
|
|
78
64
|
/**
|
|
79
65
|
* Get or create the map in `proxySignals` for key `proxy`.
|
|
80
66
|
*/
|
|
81
|
-
function ensureSignalMap(
|
|
82
|
-
if (!
|
|
83
|
-
|
|
84
|
-
return
|
|
67
|
+
function ensureSignalMap(rawObj) {
|
|
68
|
+
if (!propertiesToSignals.has(rawObj))
|
|
69
|
+
propertiesToSignals.set(rawObj, new Map());
|
|
70
|
+
return propertiesToSignals.get(rawObj);
|
|
85
71
|
}
|
|
86
72
|
/**
|
|
87
73
|
* Write a new value into a cached signal, creating it if needed.
|
|
@@ -134,6 +120,20 @@ function escapePathSegment(segment) {
|
|
|
134
120
|
function isReactiveSymbol(key) {
|
|
135
121
|
return typeof key === "symbol" && !wellKnownSymbols.has(key);
|
|
136
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Replaces the old proxy to a raw object with a new one.
|
|
125
|
+
* Used so that we can indicate modifications along the path of a change by equality checks.
|
|
126
|
+
*/
|
|
127
|
+
function replaceProxy(meta) {
|
|
128
|
+
if (!meta.parent || !meta.key)
|
|
129
|
+
return;
|
|
130
|
+
// Create a new proxy for this raw object -- frontend libs like react need this to recognize changes along this path.
|
|
131
|
+
const handlers = meta.raw instanceof Set ? setHandlers : objectHandlers;
|
|
132
|
+
const proxy = new Proxy(meta.raw, handlers);
|
|
133
|
+
rawToProxy.set(meta.raw, proxy);
|
|
134
|
+
const signal = propertiesToSignals.get(meta.parent.raw)?.get(meta.key);
|
|
135
|
+
signal?.(proxy);
|
|
136
|
+
}
|
|
137
137
|
/**
|
|
138
138
|
* Walk the metadata chain to build the patch path for a property access.
|
|
139
139
|
* Handles numbers, symbols (using description) and synthetic ID markers.
|
|
@@ -160,7 +160,8 @@ function buildPath(meta, key, skipEscape = false) {
|
|
|
160
160
|
let cursor = meta;
|
|
161
161
|
while (cursor && cursor.parent && cursor.key !== undefined) {
|
|
162
162
|
push(cursor.key, !!cursor.isSyntheticId);
|
|
163
|
-
|
|
163
|
+
replaceProxy(cursor);
|
|
164
|
+
cursor = cursor.parent;
|
|
164
165
|
}
|
|
165
166
|
return path;
|
|
166
167
|
}
|
|
@@ -168,8 +169,8 @@ function buildPath(meta, key, skipEscape = false) {
|
|
|
168
169
|
function resolveContainerPath(meta) {
|
|
169
170
|
if (!meta || !meta.parent || meta.key === undefined)
|
|
170
171
|
return [];
|
|
171
|
-
|
|
172
|
-
return buildPath(
|
|
172
|
+
replaceProxy(meta);
|
|
173
|
+
return buildPath(meta.parent, meta.key, !!meta.isSyntheticId);
|
|
173
174
|
}
|
|
174
175
|
/**
|
|
175
176
|
* Build and enqueue patches for a mutation, only if listeners exist on the root.
|
|
@@ -297,24 +298,25 @@ function initializeObjectTreeIfNoListeners(meta, basePath, value, inSet) {
|
|
|
297
298
|
* Does not proxy and returns `value` if @see shouldProxy returns false.
|
|
298
299
|
* Returns value if parent has no metadata record.
|
|
299
300
|
*/
|
|
300
|
-
function ensureChildProxy(
|
|
301
|
-
if (!shouldProxy(
|
|
302
|
-
return
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
301
|
+
function ensureChildProxy(rawChild, parent, key, isSyntheticId = false) {
|
|
302
|
+
if (!shouldProxy(rawChild))
|
|
303
|
+
return rawChild;
|
|
304
|
+
const parentRaw = parent[RAW_KEY] || parent;
|
|
305
|
+
const parentMeta = rawToMeta?.get(parentRaw);
|
|
306
|
+
if (rawToProxy.has(rawChild)) {
|
|
307
|
+
const proxied = rawToProxy.get(rawChild);
|
|
308
|
+
const proxiedMeta = rawToMeta.get(rawChild);
|
|
306
309
|
if (proxiedMeta) {
|
|
307
|
-
proxiedMeta.parent =
|
|
310
|
+
proxiedMeta.parent = parentMeta;
|
|
308
311
|
proxiedMeta.key = key;
|
|
309
312
|
proxiedMeta.isSyntheticId = isSyntheticId;
|
|
310
313
|
}
|
|
311
314
|
return proxied;
|
|
312
315
|
}
|
|
313
|
-
const parentMeta = getMeta(parentProxy);
|
|
314
316
|
if (!parentMeta)
|
|
315
|
-
return
|
|
317
|
+
return rawChild;
|
|
316
318
|
// Create proxy if none exists yet.
|
|
317
|
-
const proxy = createProxy(
|
|
319
|
+
const proxy = createProxy(rawChild, parentMeta.root, parentMeta.options, parentMeta, key, isSyntheticId);
|
|
318
320
|
return proxy;
|
|
319
321
|
}
|
|
320
322
|
/**
|
|
@@ -338,7 +340,7 @@ function ensureSetInfo(meta) {
|
|
|
338
340
|
* - Add object and id to `idForObject` and `objectForId` maps.
|
|
339
341
|
*/
|
|
340
342
|
function assignSyntheticId(meta, entry, path, inSet) {
|
|
341
|
-
const rawEntry =
|
|
343
|
+
const rawEntry = entry?.[RAW_KEY] ?? entry;
|
|
342
344
|
if (!rawEntry || typeof rawEntry !== "object") {
|
|
343
345
|
return rawEntry;
|
|
344
346
|
}
|
|
@@ -397,30 +399,22 @@ function assignSyntheticId(meta, entry, path, inSet) {
|
|
|
397
399
|
return idString;
|
|
398
400
|
}
|
|
399
401
|
/** Create the appropriate proxy (object vs Set) and track its metadata. */
|
|
400
|
-
function createProxy(target, root, options,
|
|
402
|
+
function createProxy(target, root, options, parentMeta, key, isSyntheticId) {
|
|
401
403
|
const handlers = target instanceof Set ? setHandlers : objectHandlers;
|
|
402
404
|
const proxy = new Proxy(target, handlers);
|
|
403
405
|
const meta = {
|
|
404
406
|
raw: target,
|
|
405
|
-
parent,
|
|
407
|
+
parent: parentMeta,
|
|
406
408
|
key,
|
|
407
409
|
isSyntheticId,
|
|
408
410
|
root,
|
|
409
411
|
options,
|
|
410
412
|
};
|
|
411
|
-
|
|
412
|
-
|
|
413
|
+
propertiesToSignals.set(target, new Map());
|
|
414
|
+
rawToMeta.set(target, meta);
|
|
413
415
|
rawToProxy.set(target, proxy);
|
|
414
416
|
return proxy;
|
|
415
417
|
}
|
|
416
|
-
/** Normalize a value prior to writes, ensuring nested objects are proxied. */
|
|
417
|
-
function ensureValueForWrite(value, receiver, key) {
|
|
418
|
-
const rawValue = getRaw(value);
|
|
419
|
-
const proxied = shouldProxy(rawValue)
|
|
420
|
-
? ensureChildProxy(rawValue, receiver, key)
|
|
421
|
-
: rawValue;
|
|
422
|
-
return { raw: rawValue, proxied };
|
|
423
|
-
}
|
|
424
418
|
/** Return primitive literals (string/number/boolean) for patch serialization. */
|
|
425
419
|
function snapshotLiteral(value) {
|
|
426
420
|
if (typeof value === "string" ||
|
|
@@ -453,8 +447,8 @@ function emitPatchesForNew(value, meta, basePath, inSet = false) {
|
|
|
453
447
|
{
|
|
454
448
|
path: basePath,
|
|
455
449
|
op: "add",
|
|
456
|
-
|
|
457
|
-
|
|
450
|
+
value: value instanceof Set || Array.isArray(value) ? [] : {},
|
|
451
|
+
type: value instanceof Set ? "set" : undefined,
|
|
458
452
|
},
|
|
459
453
|
];
|
|
460
454
|
// The id property name, usually `@id`
|
|
@@ -508,14 +502,136 @@ function emitPatchesForNew(value, meta, basePath, inSet = false) {
|
|
|
508
502
|
}
|
|
509
503
|
return patches;
|
|
510
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* Refresh numeric index signals for an array target so reads return current items
|
|
507
|
+
* and dependent effects are notified. Recreates/proxies values for indices < length
|
|
508
|
+
* and clears signals for indices >= length.
|
|
509
|
+
*/
|
|
510
|
+
function refreshNumericIndexSignals(target, receiver) {
|
|
511
|
+
const sigs = propertiesToSignals.get(target);
|
|
512
|
+
if (!sigs)
|
|
513
|
+
return;
|
|
514
|
+
for (const k of Array.from(sigs.keys())) {
|
|
515
|
+
if (typeof k === "string" && /^\d+$/.test(k)) {
|
|
516
|
+
const idx = Number(k);
|
|
517
|
+
if (idx >= target.length) {
|
|
518
|
+
const existing = sigs.get(k);
|
|
519
|
+
if (existing &&
|
|
520
|
+
typeof existing.set === "function") {
|
|
521
|
+
existing.set(undefined);
|
|
522
|
+
}
|
|
523
|
+
sigs.delete(k);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
const val = target[idx];
|
|
527
|
+
const proxied = ensureChildProxy(val, receiver, idx);
|
|
528
|
+
setSignalValue(sigs, k, proxied);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
let hasWarnedAboutMissingSupportForReverse = false;
|
|
534
|
+
let hasWarnedAboutMissingSupportForSort = false;
|
|
535
|
+
/** Returns proxy function for array-mutating functions. */
|
|
536
|
+
const getArrayMutationProxy = (target, key, receiver) => {
|
|
537
|
+
const meta = rawToMeta.get(target);
|
|
538
|
+
if (key === "reverse") {
|
|
539
|
+
if (!hasWarnedAboutMissingSupportForReverse) {
|
|
540
|
+
console.warn(".reverse() was called on deepSignal array. In place modifications with .sort() and .reverse() are not supported. `toReversed` will be called instead.");
|
|
541
|
+
hasWarnedAboutMissingSupportForReverse = true;
|
|
542
|
+
}
|
|
543
|
+
return target.toReversed;
|
|
544
|
+
}
|
|
545
|
+
else if (key === "sort") {
|
|
546
|
+
if (!hasWarnedAboutMissingSupportForSort) {
|
|
547
|
+
console.warn(".sort() was called on deepSignal array. In place modifications with .sort() and .reverse() are not supported. `toSorted` will be called instead.");
|
|
548
|
+
hasWarnedAboutMissingSupportForSort = true;
|
|
549
|
+
}
|
|
550
|
+
return target.toSorted;
|
|
551
|
+
}
|
|
552
|
+
else if (key === "shift") {
|
|
553
|
+
return () => {
|
|
554
|
+
target.shift();
|
|
555
|
+
schedulePatch(meta, () => ({
|
|
556
|
+
op: "remove",
|
|
557
|
+
path: buildPath(meta, "0"),
|
|
558
|
+
}));
|
|
559
|
+
// Update length of proxy explicitly.
|
|
560
|
+
receiver.length = target.length;
|
|
561
|
+
// Refresh numeric index signals so shifted indices don't return stale values.
|
|
562
|
+
refreshNumericIndexSignals(target, receiver);
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
else if (key === "splice") {
|
|
566
|
+
return (start, deleteCount, ...items) => {
|
|
567
|
+
// Call splice on (non-proxied) target.
|
|
568
|
+
const deletedItems = target.splice(start, deleteCount, ...items);
|
|
569
|
+
// Manually schedule patches.
|
|
570
|
+
schedulePatch(meta, () => {
|
|
571
|
+
const patches = [];
|
|
572
|
+
// All items can be deleted at the same path / index.
|
|
573
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
574
|
+
patches.push({
|
|
575
|
+
op: "remove",
|
|
576
|
+
path: buildPath(meta, String(start)),
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
// All items can be inserted at same path / index, by adding items in reverse order.
|
|
580
|
+
for (const newItem of items.toReversed()) {
|
|
581
|
+
patches.push({
|
|
582
|
+
op: "add",
|
|
583
|
+
path: buildPath(meta, String(start)),
|
|
584
|
+
value: newItem,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
return patches;
|
|
588
|
+
});
|
|
589
|
+
// Ensure newly added items are proxied.
|
|
590
|
+
for (let i = 0; i < items.length; i++) {
|
|
591
|
+
ensureChildProxy(items[i], target, start + i);
|
|
592
|
+
}
|
|
593
|
+
// Refresh numeric index signals so shifted indices don't return stale values.
|
|
594
|
+
refreshNumericIndexSignals(target, receiver);
|
|
595
|
+
// Update length of proxy explicitly.
|
|
596
|
+
receiver.length = target.length;
|
|
597
|
+
return deletedItems;
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
else if (key === "unshift") {
|
|
601
|
+
return (...items) => {
|
|
602
|
+
const deletedItems = target.unshift(...items);
|
|
603
|
+
schedulePatch(meta, () => {
|
|
604
|
+
const patches = [];
|
|
605
|
+
// All items can be inserted at index 0, by adding items in reverse order.
|
|
606
|
+
for (const newItem of items.toReversed()) {
|
|
607
|
+
patches.push({
|
|
608
|
+
op: "add",
|
|
609
|
+
path: buildPath(meta, "0"),
|
|
610
|
+
value: newItem,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
return patches;
|
|
614
|
+
});
|
|
615
|
+
// Ensure newly added items are proxied.
|
|
616
|
+
for (let i = 0; i < items.length; i++) {
|
|
617
|
+
ensureChildProxy(items[i], target, i);
|
|
618
|
+
}
|
|
619
|
+
// Update length of proxy explicitly.
|
|
620
|
+
receiver.length = target.length;
|
|
621
|
+
// Refresh numeric index signals so shifted indices don't return stale values.
|
|
622
|
+
refreshNumericIndexSignals(target, receiver);
|
|
623
|
+
return deletedItems;
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
};
|
|
511
627
|
/** Proxy handler driving reactivity for plain objects and arrays. */
|
|
512
628
|
const objectHandlers = {
|
|
513
629
|
get(target, key, receiver) {
|
|
514
630
|
// Handle meta keys
|
|
515
631
|
if (key === RAW_KEY)
|
|
516
|
-
return
|
|
632
|
+
return target;
|
|
517
633
|
if (key === META_KEY)
|
|
518
|
-
return
|
|
634
|
+
return rawToMeta.get(target);
|
|
519
635
|
// TODO: Why are we doing this?
|
|
520
636
|
if (typeof key === "symbol") {
|
|
521
637
|
if (key === Symbol.iterator) {
|
|
@@ -525,20 +641,25 @@ const objectHandlers = {
|
|
|
525
641
|
if (!isReactiveSymbol(key))
|
|
526
642
|
return Reflect.get(target, key, receiver);
|
|
527
643
|
}
|
|
644
|
+
// Array helper handling. We need that because otherwise, every array position change would go as a separate operation through the proxy.
|
|
645
|
+
// Thus, we need to schedule the patches manually for mutating array functions.
|
|
646
|
+
if (Array.isArray(target)) {
|
|
647
|
+
const mutationProxy = getArrayMutationProxy(target, key, receiver);
|
|
648
|
+
if (mutationProxy) {
|
|
649
|
+
return mutationProxy;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
528
652
|
// Get object map from key to signal.
|
|
529
|
-
const signals = ensureSignalMap(
|
|
530
|
-
// TODO: Why are we doing this?
|
|
653
|
+
const signals = ensureSignalMap(target);
|
|
531
654
|
// Ensure that target object is signal.
|
|
532
655
|
ensureComputed(signals, target, key, receiver);
|
|
533
656
|
// Add signal if it does not exist already and did not have a getter.
|
|
534
657
|
if (!signals.has(key)) {
|
|
535
|
-
let
|
|
536
|
-
if (typeof
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
: rawValue;
|
|
541
|
-
signals.set(key, (0, core_1.signal)(rawValue));
|
|
658
|
+
let rawChild = Reflect.get(target, key, receiver);
|
|
659
|
+
if (typeof rawChild === "function")
|
|
660
|
+
return rawChild.bind(receiver ?? target);
|
|
661
|
+
const childProxyOrRaw = ensureChildProxy(rawChild, receiver, key);
|
|
662
|
+
signals.set(key, (0, core_1.signal)(childProxyOrRaw));
|
|
542
663
|
}
|
|
543
664
|
// Call and return signal
|
|
544
665
|
const sig = signals.get(key);
|
|
@@ -548,7 +669,7 @@ const objectHandlers = {
|
|
|
548
669
|
// Skip reactivity for symbols.
|
|
549
670
|
if (typeof key === "symbol" && !isReactiveSymbol(key))
|
|
550
671
|
return Reflect.set(target, key, value, receiver);
|
|
551
|
-
const meta =
|
|
672
|
+
const meta = rawToMeta.get(target);
|
|
552
673
|
if (meta?.options?.readOnlyProps?.includes(String(key))) {
|
|
553
674
|
throw new Error(`Cannot modify readonly property '${String(key)}'`);
|
|
554
675
|
}
|
|
@@ -556,30 +677,56 @@ const objectHandlers = {
|
|
|
556
677
|
const desc = descriptor(target, key);
|
|
557
678
|
const hasAccessor = !!desc &&
|
|
558
679
|
(typeof desc.get === "function" || typeof desc.set === "function");
|
|
559
|
-
const
|
|
680
|
+
const proxied = ensureChildProxy(value, target, key);
|
|
681
|
+
const rawValue = value?.[RAW_KEY] ?? value;
|
|
560
682
|
const hadKey = Object.prototype.hasOwnProperty.call(target, key);
|
|
561
|
-
const
|
|
562
|
-
|
|
683
|
+
const shouldManuallyTrimLength = Array.isArray(target) && key === "length" && value < target.length;
|
|
684
|
+
// If `length` is used to reduce the size of the array, delete the overflowing slots
|
|
685
|
+
// manually so that existing delete reactivity emits the patches and clears signals.
|
|
686
|
+
if (shouldManuallyTrimLength) {
|
|
687
|
+
for (let i = target.length - 1; i >= value; i -= 1) {
|
|
688
|
+
delete receiver[i];
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// === Set value on actual target ===
|
|
692
|
+
const result = Reflect.set(target, key, rawValue, receiver);
|
|
563
693
|
if (!hasAccessor) {
|
|
564
|
-
const signals = ensureSignalMap(
|
|
694
|
+
const signals = ensureSignalMap(target);
|
|
565
695
|
setSignalValue(signals, key, proxied);
|
|
566
696
|
}
|
|
567
697
|
if (!hadKey)
|
|
568
698
|
touchIterable(target);
|
|
569
|
-
if (meta && path && typeof
|
|
570
|
-
initializeObjectTreeIfNoListeners(meta, path,
|
|
699
|
+
if (meta && path && typeof rawValue === "object") {
|
|
700
|
+
initializeObjectTreeIfNoListeners(meta, path, rawValue, false);
|
|
701
|
+
}
|
|
702
|
+
// Modifications to the length should not emit patches
|
|
703
|
+
if (Array.isArray(target) && key === "length") {
|
|
704
|
+
return result;
|
|
571
705
|
}
|
|
572
706
|
schedulePatch(meta, () => {
|
|
573
707
|
const resolvedPath = path ?? buildPath(meta, key);
|
|
574
|
-
if (!hadKey || typeof
|
|
575
|
-
|
|
708
|
+
if (!hadKey || typeof rawValue === "object") {
|
|
709
|
+
const patches = emitPatchesForNew(rawValue, meta, resolvedPath);
|
|
710
|
+
// TODO: Document
|
|
711
|
+
// If an object is added to an array (this happens in discrete CRDTs), we will eventually receive an @id back.
|
|
712
|
+
// However, the @id is not available from the beginning but frontend frameworks might depend on @id.
|
|
713
|
+
// Thus, we set a temporary @id which will be replaced once we are called back with the real @id.
|
|
714
|
+
// Also, we don't emit a patch for this.
|
|
715
|
+
if (Array.isArray(target) &&
|
|
716
|
+
!isNaN(Number(key)) &&
|
|
717
|
+
value &&
|
|
718
|
+
typeof value === "object" &&
|
|
719
|
+
meta?.options.syntheticIdPropertyName !== "@id") {
|
|
720
|
+
rawValue["@id"] = `tmp-${++tmpIdCounter}`;
|
|
721
|
+
}
|
|
722
|
+
return patches;
|
|
576
723
|
}
|
|
577
|
-
if (snapshotLiteral(
|
|
724
|
+
if (snapshotLiteral(rawValue) === undefined)
|
|
578
725
|
return undefined;
|
|
579
726
|
return {
|
|
580
727
|
path: resolvedPath,
|
|
581
728
|
op: "add",
|
|
582
|
-
value:
|
|
729
|
+
value: rawValue,
|
|
583
730
|
};
|
|
584
731
|
});
|
|
585
732
|
return result;
|
|
@@ -587,13 +734,13 @@ const objectHandlers = {
|
|
|
587
734
|
deleteProperty(target, key) {
|
|
588
735
|
if (typeof key === "symbol" && !isReactiveSymbol(key))
|
|
589
736
|
return Reflect.deleteProperty(target, key);
|
|
590
|
-
const
|
|
591
|
-
const meta = receiver ? getMeta(receiver) : undefined;
|
|
737
|
+
const meta = rawToMeta.get(target);
|
|
592
738
|
const hadKey = Object.prototype.hasOwnProperty.call(target, key);
|
|
593
739
|
const result = Reflect.deleteProperty(target, key);
|
|
594
740
|
if (hadKey) {
|
|
595
|
-
if (
|
|
596
|
-
|
|
741
|
+
if (propertiesToSignals.has(target)) {
|
|
742
|
+
// Trigger signal
|
|
743
|
+
const signals = propertiesToSignals.get(target);
|
|
597
744
|
const existing = signals.get(key);
|
|
598
745
|
if (existing &&
|
|
599
746
|
typeof existing.set === "function") {
|
|
@@ -601,6 +748,7 @@ const objectHandlers = {
|
|
|
601
748
|
}
|
|
602
749
|
signals.delete(key);
|
|
603
750
|
}
|
|
751
|
+
// Notify listeners
|
|
604
752
|
touchIterable(target);
|
|
605
753
|
schedulePatch(meta, () => ({
|
|
606
754
|
path: buildPath(meta, key),
|
|
@@ -618,9 +766,9 @@ const objectHandlers = {
|
|
|
618
766
|
/**
|
|
619
767
|
* Guarantee Set iteration always surfaces proxies, even when raw values were stored.
|
|
620
768
|
*/
|
|
621
|
-
function ensureEntryProxy(
|
|
769
|
+
function ensureEntryProxy(raw, entry, syntheticKey, meta) {
|
|
622
770
|
return shouldProxy(entry)
|
|
623
|
-
? ensureChildProxy(entry,
|
|
771
|
+
? ensureChildProxy(entry, raw, syntheticKey, true)
|
|
624
772
|
: entry;
|
|
625
773
|
}
|
|
626
774
|
/** Wrap the underlying Set iterator so each value is proxied before leaving the trap. */
|
|
@@ -632,8 +780,8 @@ function createSetIterator(target, receiver, mapValue) {
|
|
|
632
780
|
const next = iterator.next();
|
|
633
781
|
if (next.done)
|
|
634
782
|
return next;
|
|
635
|
-
const meta =
|
|
636
|
-
const proxied = ensureEntryProxy(
|
|
783
|
+
const meta = rawToMeta.get(target);
|
|
784
|
+
const proxied = ensureEntryProxy(target, next.value, assignSyntheticId(meta, next.value, [], true), meta);
|
|
637
785
|
return {
|
|
638
786
|
value: mapValue(proxied),
|
|
639
787
|
done: false,
|
|
@@ -643,10 +791,11 @@ function createSetIterator(target, receiver, mapValue) {
|
|
|
643
791
|
/** Proxy handler providing deep-signal semantics for native Set instances. */
|
|
644
792
|
const setHandlers = {
|
|
645
793
|
get(target, key, receiver) {
|
|
794
|
+
const meta = rawToMeta.get(target);
|
|
646
795
|
if (key === RAW_KEY)
|
|
647
|
-
return
|
|
796
|
+
return target;
|
|
648
797
|
if (key === META_KEY)
|
|
649
|
-
return
|
|
798
|
+
return meta;
|
|
650
799
|
if (key === "size") {
|
|
651
800
|
const sig = ensureIterableSignal(target);
|
|
652
801
|
sig();
|
|
@@ -659,21 +808,19 @@ const setHandlers = {
|
|
|
659
808
|
const iterator = target.values().next();
|
|
660
809
|
if (iterator.done)
|
|
661
810
|
return undefined;
|
|
662
|
-
|
|
663
|
-
return ensureEntryProxy(receiver, iterator.value, assignSyntheticId(meta, iterator.value, [], true), meta);
|
|
811
|
+
return ensureEntryProxy(target, iterator.value, assignSyntheticId(meta, iterator.value, [], true), meta);
|
|
664
812
|
};
|
|
665
813
|
}
|
|
666
814
|
if (key === "getById") {
|
|
667
815
|
return function getById(id) {
|
|
668
816
|
const iterableSig = ensureIterableSignal(target);
|
|
669
817
|
iterableSig();
|
|
670
|
-
const meta = getMeta(receiver);
|
|
671
818
|
if (!meta?.setInfo)
|
|
672
819
|
return undefined;
|
|
673
820
|
const entry = meta.setInfo.objectForId.get(String(id));
|
|
674
821
|
if (!entry)
|
|
675
822
|
return undefined;
|
|
676
|
-
return ensureEntryProxy(
|
|
823
|
+
return ensureEntryProxy(target, entry, String(id), meta);
|
|
677
824
|
};
|
|
678
825
|
}
|
|
679
826
|
if (key === "getBy") {
|
|
@@ -685,9 +832,8 @@ const setHandlers = {
|
|
|
685
832
|
}
|
|
686
833
|
if (key === "add") {
|
|
687
834
|
return function add(value) {
|
|
688
|
-
const meta = getMeta(receiver);
|
|
689
835
|
const containerPath = resolveContainerPath(meta);
|
|
690
|
-
const rawValue =
|
|
836
|
+
const rawValue = value[RAW_KEY] ?? value;
|
|
691
837
|
const sizeBefore = target.size;
|
|
692
838
|
const result = target.add(rawValue);
|
|
693
839
|
if (target.size !== sizeBefore) {
|
|
@@ -695,7 +841,7 @@ const setHandlers = {
|
|
|
695
841
|
if (rawValue && typeof rawValue === "object") {
|
|
696
842
|
const synthetic = assignSyntheticId(meta, rawValue, containerPath, true);
|
|
697
843
|
initializeObjectTreeIfNoListeners(meta, [...containerPath, synthetic], rawValue, true);
|
|
698
|
-
ensureEntryProxy(
|
|
844
|
+
ensureEntryProxy(target, rawValue, synthetic, meta);
|
|
699
845
|
schedulePatch(meta, () => emitPatchesForNew(rawValue, meta, [...containerPath, synthetic], true));
|
|
700
846
|
}
|
|
701
847
|
else {
|
|
@@ -715,9 +861,8 @@ const setHandlers = {
|
|
|
715
861
|
}
|
|
716
862
|
if (key === "delete") {
|
|
717
863
|
return function deleteEntry(value) {
|
|
718
|
-
const meta = getMeta(receiver);
|
|
719
864
|
const containerPath = resolveContainerPath(meta);
|
|
720
|
-
const rawValue =
|
|
865
|
+
const rawValue = value?.[RAW_KEY] ?? value;
|
|
721
866
|
const synthetic = rawValue && typeof rawValue === "object"
|
|
722
867
|
? ensureSetInfo(meta).idForObject.get(rawValue)
|
|
723
868
|
: rawValue;
|
|
@@ -748,7 +893,6 @@ const setHandlers = {
|
|
|
748
893
|
}
|
|
749
894
|
if (key === "clear") {
|
|
750
895
|
return function clear() {
|
|
751
|
-
const meta = getMeta(receiver);
|
|
752
896
|
const containerPath = resolveContainerPath(meta);
|
|
753
897
|
if (meta.setInfo) {
|
|
754
898
|
meta.setInfo.objectForId.clear();
|
|
@@ -796,12 +940,11 @@ const setHandlers = {
|
|
|
796
940
|
return function forEach(callback, thisArg) {
|
|
797
941
|
const iterableSig = ensureIterableSignal(target);
|
|
798
942
|
iterableSig();
|
|
799
|
-
const meta = getMeta(receiver);
|
|
800
943
|
let index = 0;
|
|
801
944
|
const expectsIteratorSignature = callback.length <= 2;
|
|
802
945
|
const iteratorCallback = callback;
|
|
803
946
|
target.forEach((entry) => {
|
|
804
|
-
const proxied = ensureEntryProxy(
|
|
947
|
+
const proxied = ensureEntryProxy(target, entry, assignSyntheticId(meta, entry, [], true), meta);
|
|
805
948
|
if (expectsIteratorSignature) {
|
|
806
949
|
iteratorCallback.call(thisArg, proxied, index++);
|
|
807
950
|
}
|
|
@@ -813,7 +956,7 @@ const setHandlers = {
|
|
|
813
956
|
}
|
|
814
957
|
if (key === "has") {
|
|
815
958
|
return function has(value) {
|
|
816
|
-
return target.has(
|
|
959
|
+
return target.has(value?.[RAW_KEY] ?? value);
|
|
817
960
|
};
|
|
818
961
|
}
|
|
819
962
|
return Reflect.get(target, key, receiver);
|
|
@@ -821,7 +964,7 @@ const setHandlers = {
|
|
|
821
964
|
};
|
|
822
965
|
/** Runtime guard that checks whether a value is a deepSignal proxy. */
|
|
823
966
|
function isDeepSignal(value) {
|
|
824
|
-
return !!
|
|
967
|
+
return !!value?.[RAW_KEY];
|
|
825
968
|
}
|
|
826
969
|
/**
|
|
827
970
|
* Create a deep reactive proxy for objects, arrays or Sets.
|
|
@@ -878,7 +1021,7 @@ function subscribeDeepMutations(root, cb, triggerInstantly = false) {
|
|
|
878
1021
|
}
|
|
879
1022
|
/** Return the root identifier symbol for a deepSignal proxy (if any). */
|
|
880
1023
|
function getDeepSignalRootId(value) {
|
|
881
|
-
return
|
|
1024
|
+
return rawToMeta.get(value?.[RAW_KEY] ?? value)?.root;
|
|
882
1025
|
}
|
|
883
1026
|
/** Retrieve the current patch version for a deepSignal root (if tracked). */
|
|
884
1027
|
function getDeepSignalVersion(root) {
|
|
@@ -896,7 +1039,8 @@ function shallow(obj) {
|
|
|
896
1039
|
function setSetEntrySyntheticId(obj, id) {
|
|
897
1040
|
if (!obj || typeof obj !== "object")
|
|
898
1041
|
return;
|
|
899
|
-
|
|
1042
|
+
// @ts-ignore
|
|
1043
|
+
forcedSyntheticIds.set(obj[RAW_KEY] ?? obj, String(id));
|
|
900
1044
|
}
|
|
901
1045
|
/** Convenience helper to add an entry to a proxied Set with a pre-defined synthetic ID. */
|
|
902
1046
|
function addWithId(set, entry, id) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeepSignalOptions } from "
|
|
1
|
+
import { DeepSignalOptions } from "../..";
|
|
2
2
|
/**
|
|
3
3
|
* Create or use an existing deepSignal object in your component.
|
|
4
4
|
* Modifications to the returned deepSignal object cause an immediate rerender.
|
|
@@ -6,9 +6,9 @@ import { DeepSignalOptions } from "../../types.js";
|
|
|
6
6
|
* is rerendered as well.
|
|
7
7
|
*
|
|
8
8
|
* @param object The object that should become reactive
|
|
9
|
-
* @param
|
|
10
|
-
* @returns The deepSignal object of the object param.
|
|
9
|
+
* @param deepSignalOptions When the object is not a deepSignal already, options passed to `deepSignal`.
|
|
10
|
+
* @returns The deepSignal object of the object param. On every change, the returned object will change (a new no-op proxy is created) around the deepSignal object.
|
|
11
11
|
*/
|
|
12
|
-
declare const useSignal: <T extends object>(object: T,
|
|
12
|
+
declare const useSignal: <T extends object>(object: T, deepSignalOptions?: DeepSignalOptions) => import("../..").DeepSignal<T>;
|
|
13
13
|
export default useSignal;
|
|
14
14
|
//# sourceMappingURL=useDeepSignal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDeepSignal.d.ts","sourceRoot":"","sources":["../../../src/hooks/react/useDeepSignal.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useDeepSignal.d.ts","sourceRoot":"","sources":["../../../src/hooks/react/useDeepSignal.ts"],"names":[],"mappings":"AAYA,OAAO,EAAc,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD;;;;;;;;;GASG;AACH,QAAA,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAC/B,QAAQ,CAAC,EACT,oBAAoB,iBAAiB,kCAqCxC,CAAC;AAEF,eAAe,SAAS,CAAC"}
|