@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.
Files changed (63) hide show
  1. package/dist/deepSignal.d.ts.map +1 -1
  2. package/dist/deepSignal.js +244 -100
  3. package/dist/hooks/react/useDeepSignal.d.ts +4 -4
  4. package/dist/hooks/react/useDeepSignal.d.ts.map +1 -1
  5. package/dist/hooks/react/useDeepSignal.js +22 -15
  6. package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +3 -7
  7. package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -1
  8. package/dist/hooks/svelte/useDeepSignal.svelte.js +34 -18
  9. package/dist/hooks/vue/useDeepSignal.d.ts +4 -3
  10. package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -1
  11. package/dist/hooks/vue/useDeepSignal.js +52 -24
  12. package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts +4 -0
  13. package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts.map +1 -0
  14. package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.js +225 -0
  15. package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts +4 -0
  16. package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts.map +1 -0
  17. package/dist/test/frontend/astro-app/src/components/ReactPanel.js +227 -0
  18. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts +4 -0
  19. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts.map +1 -0
  20. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.js +150 -0
  21. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts +4 -0
  22. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts.map +1 -0
  23. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.js +184 -0
  24. package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts +2 -0
  25. package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts.map +1 -0
  26. package/dist/test/frontend/playwright/crossFrameworkHooks.spec.js +171 -0
  27. package/dist/test/frontend/playwright/perfSuite.spec.d.ts +2 -0
  28. package/dist/test/frontend/playwright/perfSuite.spec.d.ts.map +1 -0
  29. package/dist/test/frontend/playwright/perfSuite.spec.js +128 -0
  30. package/dist/test/frontend/utils/mockData.d.ts +53 -0
  31. package/dist/test/frontend/utils/mockData.d.ts.map +1 -0
  32. package/dist/test/frontend/utils/mockData.js +78 -0
  33. package/dist/test/frontend/utils/paths.d.ts +4 -0
  34. package/dist/test/frontend/utils/paths.d.ts.map +1 -0
  35. package/dist/test/frontend/utils/paths.js +28 -0
  36. package/dist/test/frontend/utils/perfScenarios.d.ts +15 -0
  37. package/dist/test/frontend/utils/perfScenarios.d.ts.map +1 -0
  38. package/dist/test/frontend/utils/perfScenarios.js +287 -0
  39. package/dist/test/frontend/utils/renderMetrics.d.ts +13 -0
  40. package/dist/test/frontend/utils/renderMetrics.d.ts.map +1 -0
  41. package/dist/test/frontend/utils/renderMetrics.js +45 -0
  42. package/dist/test/frontend/utils/state.d.ts +57 -0
  43. package/dist/test/frontend/utils/state.d.ts.map +1 -0
  44. package/dist/test/frontend/utils/state.js +79 -0
  45. package/dist/test/lib/core.test.d.ts +2 -0
  46. package/dist/test/lib/core.test.d.ts.map +1 -0
  47. package/dist/test/lib/core.test.js +53 -0
  48. package/dist/test/lib/deepSignalOptions.test.d.ts +2 -0
  49. package/dist/test/lib/deepSignalOptions.test.d.ts.map +1 -0
  50. package/dist/test/lib/deepSignalOptions.test.js +230 -0
  51. package/dist/test/lib/index.test.d.ts +2 -0
  52. package/dist/test/lib/index.test.d.ts.map +1 -0
  53. package/dist/test/lib/index.test.js +807 -0
  54. package/dist/test/lib/misc.test.d.ts +2 -0
  55. package/dist/test/lib/misc.test.d.ts.map +1 -0
  56. package/dist/test/lib/misc.test.js +140 -0
  57. package/dist/test/lib/watch.test.d.ts +2 -0
  58. package/dist/test/lib/watch.test.d.ts.map +1 -0
  59. package/dist/test/lib/watch.test.js +1280 -0
  60. package/dist/types.d.ts +1 -1
  61. package/dist/types.d.ts.map +1 -1
  62. package/package.json +36 -19
  63. package/src/index.ts +5 -0
@@ -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;AAi/BjB,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,QAGtE;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"}
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"}
@@ -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 proxyToMeta = new WeakMap();
24
- const proxySignals = new WeakMap();
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(proxy) {
82
- if (!proxySignals.has(proxy))
83
- proxySignals.set(proxy, new Map());
84
- return proxySignals.get(proxy);
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
- cursor = getMeta(cursor.parent);
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
- const parentMeta = getMeta(meta.parent);
172
- return buildPath(parentMeta, meta.key, !!meta.isSyntheticId);
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(value, parentProxy, key, isSyntheticId = false) {
301
- if (!shouldProxy(value))
302
- return value;
303
- if (rawToProxy.has(value)) {
304
- const proxied = rawToProxy.get(value);
305
- const proxiedMeta = getMeta(proxied);
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 = parentProxy;
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 value;
317
+ return rawChild;
316
318
  // Create proxy if none exists yet.
317
- const proxy = createProxy(value, parentMeta.root, parentMeta.options, parentProxy, key, isSyntheticId);
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 = getRaw(entry);
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, parent, key, isSyntheticId) {
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
- proxyToMeta.set(proxy, meta);
412
- proxySignals.set(proxy, new Map());
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
- type: value instanceof Set ? "set" : "object",
457
- value: value instanceof Set ? [] : undefined,
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 getMeta(receiver)?.raw ?? target;
632
+ return target;
517
633
  if (key === META_KEY)
518
- return getMeta(receiver);
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(receiver);
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 rawValue = Reflect.get(target, key, receiver);
536
- if (typeof rawValue === "function")
537
- return rawValue.bind(receiver ?? target);
538
- rawValue = shouldProxy(rawValue)
539
- ? ensureChildProxy(rawValue, receiver, key)
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 = getMeta(receiver);
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 { raw, proxied } = ensureValueForWrite(value, receiver, key);
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 previous = hadKey ? target[key] : undefined;
562
- const result = Reflect.set(target, key, raw, receiver);
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(receiver);
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 raw === "object") {
570
- initializeObjectTreeIfNoListeners(meta, path, raw, false);
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 raw === "object") {
575
- return emitPatchesForNew(raw, meta, resolvedPath);
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(raw) === undefined)
724
+ if (snapshotLiteral(rawValue) === undefined)
578
725
  return undefined;
579
726
  return {
580
727
  path: resolvedPath,
581
728
  op: "add",
582
- value: raw,
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 receiver = rawToProxy.get(target);
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 (receiver && proxySignals.has(receiver)) {
596
- const signals = proxySignals.get(receiver);
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(receiver, entry, syntheticKey, meta) {
769
+ function ensureEntryProxy(raw, entry, syntheticKey, meta) {
622
770
  return shouldProxy(entry)
623
- ? ensureChildProxy(entry, receiver, syntheticKey, true)
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 = getMeta(receiver);
636
- const proxied = ensureEntryProxy(receiver, next.value, assignSyntheticId(meta, next.value, [], true), meta);
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 getMeta(receiver)?.raw ?? target;
796
+ return target;
648
797
  if (key === META_KEY)
649
- return getMeta(receiver);
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
- const meta = getMeta(receiver);
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(receiver, entry, String(id), meta);
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 = getRaw(value);
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(receiver, rawValue, synthetic, meta);
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 = getRaw(value);
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(receiver, entry, assignSyntheticId(meta, entry, [], true), meta);
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(getRaw(value));
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 !!getMeta(value);
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 getMeta(value)?.root;
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
- forcedSyntheticIds.set(getRaw(obj), String(id));
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 "../../types.js";
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 deepSignalObjects When the object is not a deepSignal already, options passed to `deepSignal`.
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, deepSignalObjects?: DeepSignalOptions) => import("../../types.js").DeepSignal<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":"AAaA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;GASG;AACH,QAAA,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAC/B,QAAQ,CAAC,EACT,oBAAoB,iBAAiB,2CAqBxC,CAAC;AAEF,eAAe,SAAS,CAAC"}
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"}