@rlabs-inc/signals 0.1.0 → 0.2.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
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Ultra-lightweight fine-grained reactivity for TypeScript**
4
4
 
5
- A standalone reactive primitives library inspired by Svelte 5 runes, Solid.js, and Vue reactivity. Zero dependencies. ~700 lines of pure TypeScript.
5
+ A standalone reactive primitives library inspired by Svelte 5 runes, Solid.js, and Vue reactivity. Zero dependencies. ~800 lines of pure TypeScript.
6
6
 
7
7
  ## Features
8
8
 
@@ -25,7 +25,7 @@ npm install @rlabs-inc/signals
25
25
  ## Quick Start
26
26
 
27
27
  ```typescript
28
- import { signal, effect, derived, state } from '@rlabs-inc/signals'
28
+ import { signal, effect, derived } from '@rlabs-inc/signals'
29
29
 
30
30
  // Simple signal
31
31
  const count = signal(0)
@@ -43,8 +43,8 @@ count.value = 1
43
43
  const doubled = derived(() => count.value * 2)
44
44
  console.log(doubled.value) // 2
45
45
 
46
- // Deep reactivity with state()
47
- const user = state({
46
+ // Deep reactivity - signal() is deeply reactive for objects/arrays!
47
+ const user = signal({
48
48
  name: 'John',
49
49
  address: {
50
50
  city: 'NYC'
@@ -52,28 +52,47 @@ const user = state({
52
52
  })
53
53
 
54
54
  effect(() => {
55
- console.log('City:', user.address.city)
55
+ console.log('City:', user.value.address.city)
56
56
  })
57
57
  // Logs: "City: NYC"
58
58
 
59
- user.address.city = 'LA'
60
- // Logs: "City: LA"
59
+ user.value.address.city = 'LA'
60
+ // Logs: "City: LA" - mutations at any depth trigger effects!
61
+
62
+ // Even deeply nested arrays work
63
+ const data = signal({ items: [[1, 2], [3, 4]] })
64
+ data.value.items[0][1] = 99 // Triggers effects!
61
65
  ```
62
66
 
63
67
  ## API Reference
64
68
 
65
69
  ### signal(initial, options?)
66
70
 
67
- Create a reactive signal that holds a value.
71
+ Create a reactive signal that holds a value. **For objects and arrays, signals are deeply reactive** - mutations at any depth trigger effects.
68
72
 
69
73
  ```typescript
74
+ // Primitives
70
75
  const count = signal(0)
71
76
  console.log(count.value) // 0
72
-
73
77
  count.value = 5
74
78
  console.log(count.value) // 5
75
79
 
76
- // With custom equality
80
+ // Objects - deeply reactive!
81
+ const user = signal({ name: 'John', address: { city: 'NYC' } })
82
+ user.value.address.city = 'LA' // Triggers effects!
83
+
84
+ // Arrays - deeply reactive!
85
+ const matrix = signal([[1, 2], [3, 4]])
86
+ matrix.value[0][1] = 99 // Triggers effects!
87
+ matrix.value.push([5, 6]) // Triggers effects!
88
+
89
+ // Arbitrarily deep nesting works
90
+ const complex = signal({
91
+ level1: [{ level2: [[{ level3: 'value' }]] }]
92
+ })
93
+ complex.value.level1[0].level2[0][0].level3 = 'mutated' // Triggers!
94
+
95
+ // With custom equality (for full replacement comparison)
77
96
  const obj = signal({ x: 1 }, {
78
97
  equals: (a, b) => a.x === b.x
79
98
  })
@@ -119,9 +138,10 @@ const tripled = derived.by(() => count.value * 3)
119
138
 
120
139
  ### state(initial)
121
140
 
122
- Create a deeply reactive state object. All nested properties are automatically reactive.
141
+ Convenience function for deeply reactive objects **without `.value`**. Use when you want direct property access instead of going through `.value`.
123
142
 
124
143
  ```typescript
144
+ // state() - direct access, no .value needed
125
145
  const app = state({
126
146
  user: {
127
147
  name: 'John',
@@ -133,13 +153,21 @@ const app = state({
133
153
  })
134
154
 
135
155
  effect(() => {
136
- console.log(app.user.preferences.theme)
156
+ console.log(app.user.preferences.theme) // No .value!
137
157
  })
138
158
 
139
159
  app.user.preferences.theme = 'light' // Triggers effect
140
160
  app.items.push(4) // Arrays are reactive too
161
+
162
+ // Equivalent using signal():
163
+ const app2 = signal({ ... })
164
+ app2.value.user.preferences.theme = 'light' // Same, but with .value
141
165
  ```
142
166
 
167
+ **When to use which:**
168
+ - `signal()` - Unified API, always use `.value` (recommended)
169
+ - `state()` - When you want direct property access without `.value`
170
+
143
171
  ### batch(fn)
144
172
 
145
173
  Batch multiple updates into a single reaction cycle.
@@ -278,7 +306,8 @@ scope.stop() // Disposes all effects
278
306
 
279
307
  | Svelte 5 | @rlabs-inc/signals |
280
308
  |----------|-------------------|
281
- | `$state(value)` | `signal(value)` or `state(obj)` |
309
+ | `$state(value)` | `signal(value)` - deeply reactive for objects/arrays |
310
+ | `$state(obj)` direct access | `state(obj)` - no `.value` needed |
282
311
  | `$derived(expr)` | `derived(() => expr)` |
283
312
  | `$derived.by(fn)` | `derived.by(fn)` or `derived(fn)` |
284
313
  | `$effect(fn)` | `effect(fn)` |
@@ -289,6 +318,7 @@ scope.stop() // Disposes all effects
289
318
  - No compiler needed - works with plain TypeScript
290
319
  - Use `.value` to read/write signals (like Vue/Solid)
291
320
  - No Happy DOM or browser environment required
321
+ - `signal()` is deeply reactive out of the box - arrays of arrays of objects just work
292
322
 
293
323
  ## Why?
294
324
 
package/dist/index.d.ts CHANGED
@@ -27,12 +27,22 @@ export declare const shallowEquals: <T>(a: T, b: T) => boolean;
27
27
  /**
28
28
  * Create a reactive signal
29
29
  *
30
+ * For objects and arrays, the signal is deeply reactive - mutations at any
31
+ * depth will trigger effects.
32
+ *
30
33
  * @example
31
34
  * ```ts
32
35
  * const count = signal(0)
33
36
  * console.log(count.value) // 0
34
37
  * count.value = 1
35
38
  * console.log(count.value) // 1
39
+ *
40
+ * // Deep reactivity for objects/arrays
41
+ * const user = signal({ name: 'John', address: { city: 'NYC' } })
42
+ * user.value.address.city = 'LA' // Triggers effects!
43
+ *
44
+ * const items = signal([[1, 2], [3, 4]])
45
+ * items.value[0][1] = 99 // Triggers effects!
36
46
  * ```
37
47
  */
38
48
  export declare function signal<T>(initial: T, options?: SignalOptions<T>): WritableSignal<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -79,14 +79,22 @@ function signal(initial, options) {
79
79
  reactions: new Set,
80
80
  equals: options?.equals ?? defaultEquals
81
81
  };
82
+ let proxyCache = null;
82
83
  return {
83
84
  get value() {
84
85
  track(internal);
86
+ if (internal.v !== null && typeof internal.v === "object") {
87
+ if (!proxyCache) {
88
+ proxyCache = createDeepReactiveForSignal(internal.v, internal);
89
+ }
90
+ return proxyCache;
91
+ }
85
92
  return internal.v;
86
93
  },
87
94
  set value(newValue) {
88
95
  if (!internal.equals(internal.v, newValue)) {
89
96
  internal.v = newValue;
97
+ proxyCache = null;
90
98
  trigger(internal);
91
99
  }
92
100
  }
@@ -329,6 +337,101 @@ function createDeepReactive(target) {
329
337
  return proxy;
330
338
  }
331
339
  var proxyToRaw = new WeakMap;
340
+ var signalProxyCache = new WeakMap;
341
+ function createDeepReactiveForSignal(target, parentSignal) {
342
+ const existingProxy = signalProxyCache.get(target);
343
+ if (existingProxy)
344
+ return existingProxy;
345
+ const propSignals = new Map;
346
+ const getPropSignal = (prop) => {
347
+ let sig = propSignals.get(prop);
348
+ if (!sig) {
349
+ sig = {
350
+ v: target[prop],
351
+ reactions: new Set,
352
+ equals: defaultEquals
353
+ };
354
+ propSignals.set(prop, sig);
355
+ }
356
+ return sig;
357
+ };
358
+ const internal = {
359
+ v: target,
360
+ reactions: new Set,
361
+ equals: defaultEquals
362
+ };
363
+ const proxy = new Proxy(target, {
364
+ get(target2, prop, receiver) {
365
+ if (prop === REACTIVE_MARKER)
366
+ return true;
367
+ const value = Reflect.get(target2, prop, receiver);
368
+ if (Array.isArray(target2) && typeof value === "function") {
369
+ track(internal);
370
+ return value.bind(proxy);
371
+ }
372
+ const sig = getPropSignal(prop);
373
+ track(sig);
374
+ if (value !== null && typeof value === "object") {
375
+ const proto = Object.getPrototypeOf(value);
376
+ if (proto === Object.prototype || proto === Array.prototype || proto === null) {
377
+ if (!value[REACTIVE_MARKER]) {
378
+ return createDeepReactiveForSignal(value, parentSignal);
379
+ }
380
+ }
381
+ }
382
+ return value;
383
+ },
384
+ set(target2, prop, value, receiver) {
385
+ const oldValue = target2[prop];
386
+ const rawValue = value?.[REACTIVE_MARKER] ? proxyToRaw.get(value) ?? value : value;
387
+ if (Object.is(oldValue, rawValue))
388
+ return true;
389
+ const result = Reflect.set(target2, prop, rawValue, receiver);
390
+ if (result) {
391
+ batch(() => {
392
+ const sig = getPropSignal(prop);
393
+ sig.v = rawValue;
394
+ trigger(sig);
395
+ trigger(parentSignal);
396
+ if (Array.isArray(target2)) {
397
+ internal.v = target2;
398
+ trigger(internal);
399
+ }
400
+ });
401
+ }
402
+ return result;
403
+ },
404
+ deleteProperty(target2, prop) {
405
+ const hadKey = prop in target2;
406
+ const result = Reflect.deleteProperty(target2, prop);
407
+ if (result && hadKey) {
408
+ batch(() => {
409
+ const sig = propSignals.get(prop);
410
+ if (sig) {
411
+ sig.v = undefined;
412
+ trigger(sig);
413
+ }
414
+ trigger(parentSignal);
415
+ trigger(internal);
416
+ });
417
+ }
418
+ return result;
419
+ },
420
+ has(target2, prop) {
421
+ if (prop === REACTIVE_MARKER)
422
+ return true;
423
+ track(internal);
424
+ return Reflect.has(target2, prop);
425
+ },
426
+ ownKeys(target2) {
427
+ track(internal);
428
+ return Reflect.ownKeys(target2);
429
+ }
430
+ });
431
+ signalProxyCache.set(target, proxy);
432
+ proxyToRaw.set(proxy, target);
433
+ return proxy;
434
+ }
332
435
  function toRaw(proxy) {
333
436
  if (proxy && typeof proxy === "object" && proxy[REACTIVE_MARKER]) {
334
437
  return proxyToRaw.get(proxy) ?? proxy;
package/dist/index.mjs CHANGED
@@ -30,14 +30,22 @@ function signal(initial, options) {
30
30
  reactions: new Set,
31
31
  equals: options?.equals ?? defaultEquals
32
32
  };
33
+ let proxyCache = null;
33
34
  return {
34
35
  get value() {
35
36
  track(internal);
37
+ if (internal.v !== null && typeof internal.v === "object") {
38
+ if (!proxyCache) {
39
+ proxyCache = createDeepReactiveForSignal(internal.v, internal);
40
+ }
41
+ return proxyCache;
42
+ }
36
43
  return internal.v;
37
44
  },
38
45
  set value(newValue) {
39
46
  if (!internal.equals(internal.v, newValue)) {
40
47
  internal.v = newValue;
48
+ proxyCache = null;
41
49
  trigger(internal);
42
50
  }
43
51
  }
@@ -280,6 +288,101 @@ function createDeepReactive(target) {
280
288
  return proxy;
281
289
  }
282
290
  var proxyToRaw = new WeakMap;
291
+ var signalProxyCache = new WeakMap;
292
+ function createDeepReactiveForSignal(target, parentSignal) {
293
+ const existingProxy = signalProxyCache.get(target);
294
+ if (existingProxy)
295
+ return existingProxy;
296
+ const propSignals = new Map;
297
+ const getPropSignal = (prop) => {
298
+ let sig = propSignals.get(prop);
299
+ if (!sig) {
300
+ sig = {
301
+ v: target[prop],
302
+ reactions: new Set,
303
+ equals: defaultEquals
304
+ };
305
+ propSignals.set(prop, sig);
306
+ }
307
+ return sig;
308
+ };
309
+ const internal = {
310
+ v: target,
311
+ reactions: new Set,
312
+ equals: defaultEquals
313
+ };
314
+ const proxy = new Proxy(target, {
315
+ get(target2, prop, receiver) {
316
+ if (prop === REACTIVE_MARKER)
317
+ return true;
318
+ const value = Reflect.get(target2, prop, receiver);
319
+ if (Array.isArray(target2) && typeof value === "function") {
320
+ track(internal);
321
+ return value.bind(proxy);
322
+ }
323
+ const sig = getPropSignal(prop);
324
+ track(sig);
325
+ if (value !== null && typeof value === "object") {
326
+ const proto = Object.getPrototypeOf(value);
327
+ if (proto === Object.prototype || proto === Array.prototype || proto === null) {
328
+ if (!value[REACTIVE_MARKER]) {
329
+ return createDeepReactiveForSignal(value, parentSignal);
330
+ }
331
+ }
332
+ }
333
+ return value;
334
+ },
335
+ set(target2, prop, value, receiver) {
336
+ const oldValue = target2[prop];
337
+ const rawValue = value?.[REACTIVE_MARKER] ? proxyToRaw.get(value) ?? value : value;
338
+ if (Object.is(oldValue, rawValue))
339
+ return true;
340
+ const result = Reflect.set(target2, prop, rawValue, receiver);
341
+ if (result) {
342
+ batch(() => {
343
+ const sig = getPropSignal(prop);
344
+ sig.v = rawValue;
345
+ trigger(sig);
346
+ trigger(parentSignal);
347
+ if (Array.isArray(target2)) {
348
+ internal.v = target2;
349
+ trigger(internal);
350
+ }
351
+ });
352
+ }
353
+ return result;
354
+ },
355
+ deleteProperty(target2, prop) {
356
+ const hadKey = prop in target2;
357
+ const result = Reflect.deleteProperty(target2, prop);
358
+ if (result && hadKey) {
359
+ batch(() => {
360
+ const sig = propSignals.get(prop);
361
+ if (sig) {
362
+ sig.v = undefined;
363
+ trigger(sig);
364
+ }
365
+ trigger(parentSignal);
366
+ trigger(internal);
367
+ });
368
+ }
369
+ return result;
370
+ },
371
+ has(target2, prop) {
372
+ if (prop === REACTIVE_MARKER)
373
+ return true;
374
+ track(internal);
375
+ return Reflect.has(target2, prop);
376
+ },
377
+ ownKeys(target2) {
378
+ track(internal);
379
+ return Reflect.ownKeys(target2);
380
+ }
381
+ });
382
+ signalProxyCache.set(target, proxy);
383
+ proxyToRaw.set(proxy, target);
384
+ return proxy;
385
+ }
283
386
  function toRaw(proxy) {
284
387
  if (proxy && typeof proxy === "object" && proxy[REACTIVE_MARKER]) {
285
388
  return proxyToRaw.get(proxy) ?? proxy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rlabs-inc/signals",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Ultra-lightweight fine-grained reactivity for TypeScript. Signals, effects, derived values, and reactive collections with deep reactivity support.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",