@rlabs-inc/signals 1.5.0 → 1.6.0

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 CHANGED
@@ -557,33 +557,49 @@ if (isReactive(value)) {
557
557
  Control when signals trigger updates:
558
558
 
559
559
  ```typescript
560
- import { signal, equals, safeEquals, shallowEquals, neverEquals, alwaysEquals, createEquals } from '@rlabs-inc/signals'
560
+ import { signal, derived, equals, deepEquals, safeEquals, shallowEquals, neverEquals, alwaysEquals, createEquals } from '@rlabs-inc/signals'
561
561
 
562
- // Default - uses Object.is
563
- const a = signal(0) // Uses equals by default
562
+ // signal() - uses Object.is (reference equality)
563
+ const a = signal(0)
564
+
565
+ // derived() - uses Bun.deepEquals (structural equality) by default
566
+ // This prevents unnecessary propagation when computed values are structurally identical
567
+ const items = signal([1, 2, 3])
568
+ const doubled = derived(() => items.value.map(x => x * 2))
569
+ // If doubled produces [2, 4, 6] again, downstream effects won't re-run
570
+
571
+ // Deep equality - uses Bun.deepEquals (36ns for small objects!)
572
+ const c = signal({ a: 1 }, { equals: deepEquals })
573
+ c.value = { a: 1 } // Won't trigger - deeply equal
564
574
 
565
575
  // Safe equality - handles NaN correctly
566
- const b = signal(NaN, { equals: safeEquals })
576
+ const d = signal(NaN, { equals: safeEquals })
567
577
 
568
578
  // Shallow comparison - compares one level deep
569
- const c = signal({ a: 1 }, { equals: shallowEquals })
570
- c.value = { a: 1 } // Won't trigger - shallowly equal
579
+ const e = signal({ a: 1 }, { equals: shallowEquals })
571
580
 
572
581
  // Always trigger updates
573
- const d = signal(0, { equals: neverEquals })
574
- d.value = 0 // Still triggers!
582
+ const f = signal(0, { equals: neverEquals })
583
+ f.value = 0 // Still triggers!
575
584
 
576
585
  // Never trigger updates
577
- const e = signal(0, { equals: alwaysEquals })
578
- e.value = 100 // Doesn't trigger
586
+ const g = signal(0, { equals: alwaysEquals })
587
+ g.value = 100 // Doesn't trigger
579
588
 
580
589
  // Custom equality
581
590
  const customEquals = createEquals((a, b) =>
582
591
  JSON.stringify(a) === JSON.stringify(b)
583
592
  )
584
- const f = signal([], { equals: customEquals })
593
+ const h = signal([], { equals: customEquals })
585
594
  ```
586
595
 
596
+ **Default equality by primitive:**
597
+ | Primitive | Default | Reason |
598
+ |-----------|---------|--------|
599
+ | `signal()` | `Object.is` | User-controlled input - reference equality |
600
+ | `derived()` | `Bun.deepEquals` | Computed output - structural equality prevents unnecessary work |
601
+ | `linkedSignal()` | `Bun.deepEquals` | Computed output - structural equality |
602
+
587
603
  ---
588
604
 
589
605
  ## Error Handling
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { effectScope, getCurrentScope, onScopeDispose } from './primitives/scope
8
8
  export { proxy, toRaw, isReactive } from './deep/proxy.js';
9
9
  export { batch, untrack, peek } from './reactivity/batching.js';
10
10
  export { flushSync, tick } from './reactivity/scheduling.js';
11
- export { equals, safeEquals, safeNotEqual, shallowEquals, createEquals, neverEquals, alwaysEquals, } from './reactivity/equality.js';
11
+ export { equals, deepEquals, safeEquals, safeNotEqual, shallowEquals, createEquals, neverEquals, alwaysEquals, } from './reactivity/equality.js';
12
12
  export { ReactiveMap } from './collections/map.js';
13
13
  export { ReactiveSet } from './collections/set.js';
14
14
  export { ReactiveDate } from './collections/date.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACvF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC1F,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAClI,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAMpF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAM1D,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAA;AAM5D,OAAO,EACL,MAAM,EACN,UAAU,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,GACb,MAAM,0BAA0B,CAAA;AAMjC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAMpD,OAAO,EACL,GAAG,EACH,GAAG,EACH,OAAO,EACP,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,EACf,gBAAgB,GACjB,MAAM,0BAA0B,CAAA;AAMjC,OAAO,EAEL,OAAO,EACP,MAAM,EACN,aAAa,EACb,WAAW,EACX,aAAa,EACb,WAAW,EACX,YAAY,EAGZ,KAAK,EACL,KAAK,EACL,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,KAAK,EACL,UAAU,EACV,gBAAgB,EAGhB,OAAO,EACP,YAAY,EAGZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,EACf,cAAc,EACd,aAAa,GACd,MAAM,qBAAqB,CAAA;AAM5B,OAAO,EACL,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EAGV,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAGnB,cAAc,EACd,eAAe,EACf,aAAa,GACd,MAAM,mBAAmB,CAAA;AAM1B,YAAY,EAEV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAM,EACN,KAAK,EAGL,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,QAAQ,EAGR,MAAM,EACN,aAAa,EACb,YAAY,GACb,MAAM,iBAAiB,CAAA;AAExB,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACpE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACrF,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACvF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC1F,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAClI,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAMpF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAM1D,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAA;AAM5D,OAAO,EACL,MAAM,EACN,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,GACb,MAAM,0BAA0B,CAAA;AAMjC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAMpD,OAAO,EACL,GAAG,EACH,GAAG,EACH,OAAO,EACP,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,EACf,gBAAgB,GACjB,MAAM,0BAA0B,CAAA;AAMjC,OAAO,EAEL,OAAO,EACP,MAAM,EACN,aAAa,EACb,WAAW,EACX,aAAa,EACb,WAAW,EACX,YAAY,EAGZ,KAAK,EACL,KAAK,EACL,WAAW,EACX,oBAAoB,EACpB,SAAS,EACT,KAAK,EACL,UAAU,EACV,gBAAgB,EAGhB,OAAO,EACP,YAAY,EAGZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,EACf,cAAc,EACd,aAAa,GACd,MAAM,qBAAqB,CAAA;AAM5B,OAAO,EACL,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EAGV,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAGnB,cAAc,EACd,eAAe,EACf,aAAa,GACd,MAAM,mBAAmB,CAAA;AAM1B,YAAY,EAEV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAM,EACN,KAAK,EAGL,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,QAAQ,EAGR,MAAM,EACN,aAAa,EACb,YAAY,GACb,MAAM,iBAAiB,CAAA;AAExB,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACpE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACrF,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
package/dist/index.js CHANGED
@@ -80,6 +80,7 @@ __export(exports_src, {
80
80
  disconnectBinding: () => disconnectBinding,
81
81
  destroyEffect: () => destroyEffect,
82
82
  derived: () => derived,
83
+ deepEquals: () => deepEquals,
83
84
  decrementBatchDepth: () => decrementBatchDepth,
84
85
  createSelector: () => createSelector,
85
86
  createEquals: () => createEquals,
@@ -158,6 +159,7 @@ var shallowEquals = (oldValue, newValue) => {
158
159
  }
159
160
  return true;
160
161
  };
162
+ var deepEquals = typeof Bun !== "undefined" && typeof Bun.deepEquals === "function" ? (a, b) => Bun.deepEquals(a, b) : shallowEquals;
161
163
  function createEquals(fn) {
162
164
  return (oldValue, newValue) => fn(oldValue, newValue);
163
165
  }
@@ -972,7 +974,7 @@ function createDerived(fn, options) {
972
974
  f: flags,
973
975
  fn,
974
976
  v: UNINITIALIZED,
975
- equals: options?.equals ?? equals,
977
+ equals: options?.equals ?? deepEquals,
976
978
  reactions: null,
977
979
  deps: null,
978
980
  effects: null,
@@ -1406,7 +1408,7 @@ var peek = untrack;
1406
1408
  function linkedSignal(config) {
1407
1409
  let sourceFn;
1408
1410
  let computation;
1409
- let equalsFn = Object.is;
1411
+ let equalsFn = deepEquals;
1410
1412
  if (typeof config === "function") {
1411
1413
  const fn = config;
1412
1414
  sourceFn = fn;
package/dist/index.mjs CHANGED
@@ -34,6 +34,7 @@ var shallowEquals = (oldValue, newValue) => {
34
34
  }
35
35
  return true;
36
36
  };
37
+ var deepEquals = typeof Bun !== "undefined" && typeof Bun.deepEquals === "function" ? (a, b) => Bun.deepEquals(a, b) : shallowEquals;
37
38
  function createEquals(fn) {
38
39
  return (oldValue, newValue) => fn(oldValue, newValue);
39
40
  }
@@ -848,7 +849,7 @@ function createDerived(fn, options) {
848
849
  f: flags,
849
850
  fn,
850
851
  v: UNINITIALIZED,
851
- equals: options?.equals ?? equals,
852
+ equals: options?.equals ?? deepEquals,
852
853
  reactions: null,
853
854
  deps: null,
854
855
  effects: null,
@@ -1282,7 +1283,7 @@ var peek = untrack;
1282
1283
  function linkedSignal(config) {
1283
1284
  let sourceFn;
1284
1285
  let computation;
1285
- let equalsFn = Object.is;
1286
+ let equalsFn = deepEquals;
1286
1287
  if (typeof config === "function") {
1287
1288
  const fn = config;
1288
1289
  sourceFn = fn;
@@ -1937,6 +1938,7 @@ export {
1937
1938
  disconnectBinding,
1938
1939
  destroyEffect,
1939
1940
  derived,
1941
+ deepEquals,
1940
1942
  decrementBatchDepth,
1941
1943
  createSelector,
1942
1944
  createEquals,
@@ -1 +1 @@
1
- {"version":3,"file":"linked.d.ts","sourceRoot":"","sources":["../../src/primitives/linked.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAY9D;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,EAAE,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,CAAA;IACf,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,CAAC,CAAA;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI,MAAM,CAAC,CAAA;AAE9C;;GAEG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI,qBAAqB,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAM3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;AACpF,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;AA4GxF;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,CAM/E"}
1
+ {"version":3,"file":"linked.d.ts","sourceRoot":"","sources":["../../src/primitives/linked.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAa9D;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,EAAE,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,CAAA;IACf,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,CAAC,CAAA;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI,MAAM,CAAC,CAAA;AAE9C;;GAEG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI,qBAAqB,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAM3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;AACpF,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;AA4GxF;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,CAM/E"}
@@ -19,6 +19,14 @@ export declare const safeEquals: Equals;
19
19
  * Compares own enumerable properties one level deep
20
20
  */
21
21
  export declare const shallowEquals: Equals;
22
+ /**
23
+ * Deep structural equality using Bun.deepEquals.
24
+ * Used as default for derived() and linkedSignal() to prevent unnecessary
25
+ * propagation when computed values are structurally identical.
26
+ *
27
+ * Falls back to shallowEquals in non-Bun environments.
28
+ */
29
+ export declare const deepEquals: Equals;
22
30
  /**
23
31
  * Create a custom equality function
24
32
  */
@@ -1 +1 @@
1
- {"version":3,"file":"equality.d.ts","sourceRoot":"","sources":["../../src/reactivity/equality.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAM9C;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,MAA8D,CAAA;AAMnF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAQnD;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAkE,CAAA;AAM3F;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAkD3B,CAAA;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAEtE;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,MAAoB,CAAA;AAE9C;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAmB,CAAA"}
1
+ {"version":3,"file":"equality.d.ts","sourceRoot":"","sources":["../../src/reactivity/equality.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAM9C;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,MAA8D,CAAA;AAMnF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAQnD;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAkE,CAAA;AAM3F;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAkD3B,CAAA;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,MAGN,CAAA;AAMnB;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAEtE;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,MAAoB,CAAA;AAE9C;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAmB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rlabs-inc/signals",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Production-grade fine-grained reactivity for TypeScript. A complete standalone mirror of Svelte 5's reactivity system - signals, effects, derived values, deep reactivity, reactive collections, and reactive bindings.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",