mutts 1.0.10 → 1.0.12

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 (61) hide show
  1. package/README.md +3 -3
  2. package/dist/browser.cjs +244 -3199
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.ts +2 -2
  5. package/dist/browser.dev.cjs +51 -50
  6. package/dist/browser.dev.cjs.map +1 -1
  7. package/dist/browser.dev.d.ts +2 -2
  8. package/dist/browser.dev.esm.js +2 -2
  9. package/dist/browser.esm.js +15 -13
  10. package/dist/browser.esm.js.map +1 -1
  11. package/dist/chunks/{index-CaaQQlPJ.esm.js → index-BUop6B2U.esm.js} +384 -981
  12. package/dist/chunks/index-BUop6B2U.esm.js.map +1 -0
  13. package/dist/chunks/index-yK0HVxHv.cjs +2612 -0
  14. package/dist/chunks/index-yK0HVxHv.cjs.map +1 -0
  15. package/dist/chunks/{node-nKJBk8iJ.esm.js → node-Bo7WU5S2.esm.js} +2 -2
  16. package/dist/chunks/{node-nKJBk8iJ.esm.js.map → node-Bo7WU5S2.esm.js.map} +1 -1
  17. package/dist/chunks/{async-node-3PrbVAbB.cjs → node-Dd0esp5F.cjs} +4 -4
  18. package/dist/chunks/node-Dd0esp5F.cjs.map +1 -0
  19. package/dist/chunks/{proxy-Dtg-bJ3T.cjs → proxy-BvM4yewA.cjs} +441 -342
  20. package/dist/chunks/proxy-BvM4yewA.cjs.map +1 -0
  21. package/dist/chunks/{proxy-r7lARftl.esm.js → proxy-D2C49sXH.esm.js} +401 -330
  22. package/dist/chunks/proxy-D2C49sXH.esm.js.map +1 -0
  23. package/dist/debug.cjs +17 -4
  24. package/dist/debug.cjs.map +1 -1
  25. package/dist/debug.d.ts +2 -2
  26. package/dist/debug.esm.js +17 -3
  27. package/dist/debug.esm.js.map +1 -1
  28. package/dist/index.d.ts +84 -209
  29. package/dist/mutts.umd.js +842 -1362
  30. package/dist/mutts.umd.js.map +1 -1
  31. package/dist/mutts.umd.min.js +1 -1
  32. package/dist/mutts.umd.min.js.map +1 -1
  33. package/dist/node.cjs +51 -49
  34. package/dist/node.cjs.map +1 -1
  35. package/dist/node.d.ts +2 -2
  36. package/dist/node.dev.cjs +51 -49
  37. package/dist/node.dev.cjs.map +1 -1
  38. package/dist/node.dev.d.ts +2 -2
  39. package/dist/node.dev.esm.js +3 -3
  40. package/dist/node.esm.js +3 -3
  41. package/dist/{types-W5vD6m2n.d.ts → types-Bx2PhORg.d.ts} +38 -47
  42. package/docs/ai/api-reference.md +0 -14
  43. package/docs/ai/manual.md +2 -22
  44. package/docs/reactive/advanced.md +13 -14
  45. package/docs/reactive/attend.md +1 -2
  46. package/docs/reactive/collections.md +2 -149
  47. package/docs/reactive/core.md +218 -96
  48. package/docs/reactive/debugging.md +2 -2
  49. package/docs/reactive/resource.md +1 -1
  50. package/docs/reactive.md +1 -3
  51. package/docs/zone.md +1 -1
  52. package/package.json +18 -9
  53. package/dist/chunks/async-browser-BU_IfxYD.cjs +0 -216
  54. package/dist/chunks/async-browser-BU_IfxYD.cjs.map +0 -1
  55. package/dist/chunks/async-core-CRLKP3l-.cjs +0 -29
  56. package/dist/chunks/async-core-CRLKP3l-.cjs.map +0 -1
  57. package/dist/chunks/async-node-3PrbVAbB.cjs.map +0 -1
  58. package/dist/chunks/index-CaaQQlPJ.esm.js.map +0 -1
  59. package/dist/chunks/proxy-Dtg-bJ3T.cjs.map +0 -1
  60. package/dist/chunks/proxy-r7lARftl.esm.js.map +0 -1
  61. package/docs/reactive/scan.md +0 -324
@@ -4,23 +4,22 @@
4
4
 
5
5
  - [Introduction](#introduction)
6
6
  - [Getting Started](#getting-started)
7
- - [5-Minute Quick Start](#5-minute-quick-start)
7
+ - [5-Minute Quick Start](#5-minute-quick-start)
8
8
  - [Core API](#core-api)
9
9
  - [Effect System](#effect-system)
10
- - [Error Handling](./error-handling.md)
10
+ - [Error Handling](./error-handling.md)
11
11
  - [Atomic Operations](./advanced.md#atomic-operations)
12
12
  - [Advanced Effects](./advanced.md#advanced-effects)
13
13
  - [Evolution Tracking](./advanced.md#evolution-tracking)
14
14
  - [Prototype Chains and Pure Objects](./advanced.md#prototype-chains-and-pure-objects)
15
15
  - [Recursive Touching](./advanced.md#recursive-touching)
16
16
  - [Collections](./collections.md)
17
- - [Register](./collections.md#register)
18
17
  - [Class Reactivity](#class-reactivity)
19
18
  - [Non-Reactive System](#non-reactive-system)
20
19
  - [Morphing](./advanced.md#morph)
21
20
  - [Memoization](./advanced.md#memoization)
22
21
  - [Debugging and Development](./advanced.md#debugging-and-development)
23
- - [Cycle Detection](./advanced.md#cycle-detection)
22
+ - [Cycle Detection](./advanced.md#cycle-detection)
24
23
 
25
24
  ## Introduction
26
25
 
@@ -40,10 +39,13 @@ Reactivity is a programming paradigm where the system automatically tracks depen
40
39
  ### Basic Example
41
40
 
42
41
  ```typescript
43
- import { reactive, effect } from 'mutts/reactive'
42
+ import { reactive, effect } from 'mutts'
44
43
 
45
44
  // Create a reactive object
46
- const user = reactive({ name: "John", age: 30 })
45
+ const user = reactive({
46
+ name: "John",
47
+ age: 30
48
+ })
47
49
 
48
50
  // Create an effect that depends on user properties
49
51
  effect(() => {
@@ -51,8 +53,8 @@ effect(() => {
51
53
  })
52
54
 
53
55
  // When properties change, the effect automatically re-runs
54
- user.name = "Jane" // Triggers effect
55
- user.age = 25 // Triggers effect
56
+ user.name = "Jane" // Triggers effect
57
+ user.age = 25 // Triggers effect
56
58
  ```
57
59
 
58
60
  ## Getting Started
@@ -62,38 +64,45 @@ user.age = 25 // Triggers effect
62
64
  Learn the essentials in 5 minutes:
63
65
 
64
66
  **1. Make state reactive:**
67
+
65
68
  ```typescript
66
- import { reactive, effect } from 'mutts/reactive'
69
+ import { reactive, effect } from 'mutts'
67
70
 
68
- const state = reactive({ count: 0, name: "John" })
71
+ const state = reactive({
72
+ count: 0,
73
+ name: "John"
74
+ })
69
75
  ```
70
76
 
71
77
  **2. React to changes:**
78
+
72
79
  ```typescript
73
80
  // Effects automatically re-run when dependencies change
74
81
  effect(() => {
75
- console.log(`Hello ${state.name}, count is ${state.count}`)
82
+ console.log(`Hello ${state.name}, count is ${state.count}`)
76
83
  })
77
84
 
78
- state.count++ // Triggers effect
79
- state.name = "Jane" // Triggers effect
85
+ state.count++ // Triggers effect
86
+ state.name = "Jane" // Triggers effect
80
87
  ```
81
88
 
82
89
  **3. Work with arrays:**
90
+
83
91
  ```typescript
84
92
  const items = reactive([1, 2, 3])
85
93
 
86
94
  effect(() => {
87
- console.log(`Array length: ${items.length}`)
95
+ console.log(`Array length: ${items.length}`)
88
96
  })
89
97
 
90
- items.push(4) // Triggers effect
98
+ items.push(4) // Triggers effect
91
99
  ```
92
100
 
93
101
  **4. Use memoization:**
102
+
94
103
  ```typescript
95
104
  const memoized = memoize((user: User) => {
96
- return expensiveComputation(user)
105
+ return expensiveComputation(user)
97
106
  })
98
107
 
99
108
  // Only recomputes when user changes
@@ -101,12 +110,12 @@ const result = memoized(user)
101
110
  ```
102
111
 
103
112
  **5. Map over arrays:**
113
+
104
114
  ```typescript
105
115
  const source = reactive([1, 2, 3])
106
- const doubled = morph(source, ({ value }) => value * 2)
107
- // [2, 4, 6]
116
+ const doubled = morph(source, value => value * 2) // [2, 4, 6]
108
117
 
109
- source.push(4) // doubled automatically becomes [2, 4, 6, 8]
118
+ source.push(4) // doubled automatically becomes [2, 4, 6, 8]
110
119
  ```
111
120
 
112
121
  **Ready to go!** Continue reading for advanced features.
@@ -122,7 +131,7 @@ npm install mutts
122
131
  ### Basic Usage
123
132
 
124
133
  ```typescript
125
- import { reactive, effect } from 'mutts/reactive'
134
+ import { reactive, effect } from 'mutts'
126
135
 
127
136
  // Make an object reactive
128
137
  const state = reactive({
@@ -140,17 +149,19 @@ effect(() => {
140
149
  })
141
150
 
142
151
  // Changes trigger effects automatically
143
- state.count++ // Triggers first effect
152
+ state.count++ // Triggers first effect
144
153
  state.message = "Hi" // Triggers second effect
145
154
  ```
146
155
 
147
156
  ### Hello World Example
148
157
 
149
158
  ```typescript
150
- import { reactive, effect } from 'mutts/reactive'
159
+ import { reactive, effect } from 'mutts'
151
160
 
152
161
  // Simple counter
153
- const counter = reactive({ value: 0 })
162
+ const counter = reactive({
163
+ value: 0
164
+ })
154
165
 
155
166
  effect(() => {
156
167
  document.body.innerHTML = `Count: ${counter.value}`
@@ -178,11 +189,14 @@ function reactive<T extends Record<PropertyKey, any>>(target: T): T
178
189
  **Returns:** A reactive proxy of the original object
179
190
 
180
191
  **Example:**
192
+
181
193
  ```typescript
182
- const obj = { count: 0 }
183
- const reactiveObj = reactive(obj)
194
+ const obj = {
195
+ count: 0
196
+ }
197
+
198
+ const reactiveObj = reactive(obj) // reactiveObj is now reactive
184
199
 
185
- // reactiveObj is now reactive
186
200
  effect(() => {
187
201
  console.log(reactiveObj.count) // Tracks dependency
188
202
  })
@@ -192,9 +206,7 @@ reactiveObj.count = 5 // Triggers effect
192
206
 
193
207
  **Note:** The same object will always return the same proxy instance.
194
208
 
195
- **Pure Objects and Prototypes:**
196
-
197
- `reactive()` works with any object type, including:
209
+ **Pure Objects and Prototypes:** `reactive()` works with any object type, including:
198
210
  - Normal objects: `reactive({ x: 1 })`
199
211
  - Pure objects: `reactive(Object.create(null))`
200
212
  - Objects with prototypes: `reactive(Object.create(parent))`
@@ -208,9 +220,9 @@ Creates a reactive effect that automatically re-runs when dependencies change.
208
220
 
209
221
  ```typescript
210
222
  interface DependencyAccess {
211
- tracked: DependencyFunction // Track dependencies in the current effect
212
- ascend: DependencyFunction // Track dependencies in the parent effect
213
- reaction: boolean // true after the first run
223
+ tracked: DependencyFunction // Track dependencies in the current effect
224
+ ascend: DependencyFunction // Track dependencies in the parent effect
225
+ reaction: boolean // true after the first run
214
226
  }
215
227
 
216
228
  function effect(
@@ -231,16 +243,21 @@ function effect(
231
243
  **Returns:** A cleanup function to stop the effect
232
244
 
233
245
  **Example:**
246
+
234
247
  ```typescript
235
- const state = reactive({ count: 0, mood: 'happy' })
248
+ const state = reactive({
249
+ count: 0,
250
+ mood: 'happy'
251
+ })
236
252
 
237
253
  const cleanup = effect(() => {
238
254
  console.log(`Count is: ${state.count}`)
255
+
239
256
  // Optional cleanup called before next run
240
257
  return () => console.log('Cleaning up...')
241
258
  })
242
259
 
243
- state.count++ // Does trigger: 1- the cleaning, 2- the effect
260
+ state.count++ // Does trigger: 1- the cleaning, 2- the effect
244
261
  state.mood = 'surprised' // Does not trigger the effect
245
262
 
246
263
  // Later...
@@ -260,8 +277,13 @@ effect(({ reaction }) => {
260
277
  ```
261
278
 
262
279
  **Using effect with arguments (useful in loops):**
280
+
263
281
  ```typescript
264
- const items = reactive([{ id: 1 }, { id: 2 }, { id: 3 }])
282
+ const items = reactive([
283
+ { id: 1 },
284
+ { id: 2 },
285
+ { id: 3 }
286
+ ])
265
287
 
266
288
  // Create effects in a loop, passing loop variables
267
289
  for (let i = 0; i < items.length; i++) {
@@ -280,8 +302,12 @@ function unwrap<T>(proxy: T): T
280
302
  ```
281
303
 
282
304
  **Example:**
305
+
283
306
  ```typescript
284
- const original = { count: 0 }
307
+ const original = {
308
+ count: 0
309
+ }
310
+
285
311
  const reactive = reactive(original)
286
312
  const unwrapped = unwrap(reactive)
287
313
 
@@ -312,7 +338,10 @@ function isNonReactive(obj: any): boolean
312
338
  Effects are the core of the reactive system. They automatically track dependencies and re-run when those dependencies change.
313
339
 
314
340
  ```typescript
315
- const state = reactive({ count: 0, name: "John" })
341
+ const state = reactive({
342
+ count: 0,
343
+ name: "John"
344
+ })
316
345
 
317
346
  effect(() => {
318
347
  // This effect depends on state.count
@@ -320,7 +349,7 @@ effect(() => {
320
349
  })
321
350
 
322
351
  // Only changing count triggers the effect
323
- state.count = 5 // Triggers effect
352
+ state.count = 5 // Triggers effect
324
353
  state.name = "Jane" // Does NOT trigger effect
325
354
  ```
326
355
 
@@ -329,7 +358,9 @@ state.name = "Jane" // Does NOT trigger effect
329
358
  Effects return cleanup functions that you can call to stop tracking dependencies.
330
359
 
331
360
  ```typescript
332
- const state = reactive({ count: 0 })
361
+ const state = reactive({
362
+ count: 0
363
+ })
333
364
 
334
365
  const stopEffect = effect(() => {
335
366
  console.log(`Count: ${state.count}`)
@@ -349,13 +380,17 @@ The reactive system provides **automatic cleanup** for effects, making memory ma
349
380
 
350
381
  1. **Parent-Child Cleanup**: When an effect is created inside another effect, it becomes a "child" of the parent effect. When the parent effect is cleaned up, it also cleans up its child effects.
351
382
 
352
- 2. **Garbage Collection Cleanup**: For top-level effects (not created inside other effects), the system uses JavaScript's garbage collection to automatically clean them up when their cleanup function no longer referenced.
383
+ 2. **Garbage Collection Cleanup**: For top-level effects (not created inside other effects), the system uses JavaScript's garbage collection to automatically clean them up when their cleanup function is no longer referenced.
353
384
 
354
385
  #### Examples
355
386
 
356
387
  **Parent-Child Cleanup:**
388
+
357
389
  ```typescript
358
- const state = reactive({ a: 1, b: 2 })
390
+ const state = reactive({
391
+ a: 1,
392
+ b: 2
393
+ })
359
394
 
360
395
  const stopParent = effect(() => {
361
396
  state.a
@@ -374,8 +409,11 @@ stopParent() // Logs (order may vary): "Child cleanup", then "Parent cleanup"
374
409
  ```
375
410
 
376
411
  **Garbage Collection Cleanup:**
412
+
377
413
  ```typescript
378
- const state = reactive({ value: 1 })
414
+ const state = reactive({
415
+ value: 1
416
+ })
379
417
 
380
418
  // Top-level effect - automatically cleaned up via garbage collection
381
419
  effect(() => {
@@ -391,7 +429,10 @@ effect(() => {
391
429
  While cleanup can be automatic via GC, you should **store and remember** cleanup functions both to prevent the effect from being garbage-collected (keeping it alive) and to perform immediate cleanup when needed. If you don't hold a reference to the cleanup (or to the effect), the effect can be collected and its cleanup called automatically by GC; storing a reference keeps it active under your control:
392
430
 
393
431
  ```typescript
394
- const state = reactive({ value: 1 })
432
+ const state = reactive({
433
+ value: 1
434
+ })
435
+
395
436
  const activeEffects: (() => void)[] = []
396
437
 
397
438
  // Store cleanup functions for effects with side effects
@@ -417,19 +458,79 @@ activeEffects.forEach(stop => stop())
417
458
  ```
418
459
 
419
460
  **Key Points:**
420
-
421
461
  - **You do not have to call cleanup** - GC may clean up effects when no references remain
422
462
  - **You may want to call cleanup** - especially for effects with side effects
423
463
  - **Store cleanup references to keep effects alive** - holding a reference prevents GC cleanup and gives you explicit stop control
424
464
  - **Parent cleanup cleans child effects** - stopping a parent also stops its child effects
425
465
  - **Child effects are referenced by parent effects** - and therefore are not subject to GC cleanups
426
466
 
467
+ ### Object Lifecycle with `link` / `unlink`
468
+
469
+ While effects handle their own cleanup automatically, **derived reactive objects** (morph results, lifted arrays, processed children, etc.) often need explicit lifecycle management. `link` and `unlink` solve this by forming a **cleanup tree** — an ownership graph independent of the effect hierarchy.
470
+
471
+ ```typescript
472
+ import { link, unlink } from 'mutts'
473
+ ```
474
+
475
+ #### Attaching cleanup dependencies
476
+
477
+ `link(owner, ...deps)` attaches dependencies to an owner object. Dependencies can be:
478
+ - **Functions** — called with an optional `CleanupReason` on disposal
479
+ - **Objects** — recursively `unlink`ed on disposal
480
+
481
+ ```typescript
482
+ const parent = reactive({
483
+ items: []
484
+ })
485
+
486
+ // Attach a cleanup callback
487
+ link(parent, () => console.log('parent disposed'))
488
+
489
+ // Attach child objects — they will be recursively unlinked
490
+ const childA = morph(parent.items, ({ value }) => value * 2)
491
+ const childB = morph(parent.items, ({ value }) => value + 1)
492
+
493
+ link(parent, childA, childB)
494
+
495
+ // Mixed: objects and functions together
496
+ link(parent, childA, () => timer.clear())
497
+ ```
498
+
499
+ #### Disposing an object
500
+
501
+ `unlink(obj)` disposes all dependencies attached to `obj`:
502
+
503
+ ```typescript
504
+ unlink(parent)
505
+
506
+ // 1. childA is recursively unlinked (its own deps disposed)
507
+ // 2. childB is recursively unlinked
508
+ // 3. the callback runs: "parent disposed"
509
+ ```
510
+
511
+ Calling `unlink` twice is safe — the second call is a no-op. Not calling `unlink` is also fine — the object will be cleaned up when garbage is collected.
512
+
513
+ #### When to use `link`/`unlink` vs effects
514
+
515
+ | Scenario | Use |
516
+ |---|---|
517
+ | Reacting to state changes | `effect` |
518
+ | Tying a derived object's lifetime to its owner | `link` / `unlink` |
519
+ | Cleaning up timers, listeners, DOM nodes | `effect` return or `link` callback |
520
+ | Building a cleanup tree across multiple reactive objects | `link` / `unlink` |
521
+
522
+ **Rule of thumb**: if the thing you're cleaning up is a *reactive object* (not a function), prefer `link`. If it's a *side effect* (timer, listener), prefer an `effect` return or a `link` callback.
523
+
427
524
  ### Effect Dependencies
428
525
 
429
526
  Effects automatically track which reactive properties they access.
430
527
 
431
528
  ```typescript
432
- const state = reactive({ a: 1, b: 2, c: 3 })
529
+ const state = reactive({
530
+ a: 1,
531
+ b: 2,
532
+ c: 3
533
+ })
433
534
 
434
535
  effect(() => {
435
536
  // Only tracks state.a and state.b
@@ -444,13 +545,11 @@ state.c = 15 // Does NOT trigger effect
444
545
 
445
546
  ### Async Effects and the `access` Parameter
446
547
 
447
- The `effect` function provides a special `access` parameter with `tracked` and `ascend` functions that restore the active effect context for dependency tracking in asynchronous operations.
448
-
449
- In modern `mutts`, this is powered by the **Zone system**. When zones are registered in `asyncZone`, the active effect context is automatically preserved across `await` points and timers, making manual use of `tracked` optional for these cases.
548
+ The `effect` function provides a special `access` parameter with `tracked` and `ascend` functions that restore the active effect context for dependency tracking in asynchronous operations. In modern `mutts`, this is powered by the **Zone system**. When zones are registered in `asyncZone`, the active effect context is automatically preserved across `await` points and timers, making manual use of `tracked` optional for these cases.
450
549
 
451
550
  #### The Problem with Async Effects
452
551
 
453
- Traditionally, in JavaScript, async functions lose their context when they yield control.
552
+ Traditionally, in JavaScript, async functions lose their context when they yield control.
454
553
 
455
554
  #### Understanding the active effect context
456
555
 
@@ -467,36 +566,28 @@ effect(() => {
467
566
  // Async effect WITHOUT zone registration - context is lost after await
468
567
  effect(async () => {
469
568
  const value = state.count // ✅ Tracked
470
-
471
- await someAsyncOperation()
472
-
473
- // context is lost!
569
+ await someAsyncOperation() // context is lost!
474
570
  const another = state.name // ❌ NOT tracked
475
571
  })
476
572
 
477
573
  // Async effect WITH zone registration - context is preserved
478
574
  effect(async () => {
479
575
  const value = state.count // ✅ Tracked
480
-
481
- await someAsyncOperation()
482
-
483
- // context is automatically restored!
576
+ await someAsyncOperation() // context is automatically restored!
484
577
  const another = state.name // ✅ Tracked
485
578
  })
486
579
 
487
580
  // Using access.tracked() for manual restoration
488
581
  effect(async ({ tracked }) => {
489
- await someAsyncOperation()
490
-
491
- // Useful for non-patched APIs or explicit scoping
582
+ await someAsyncOperation() // Useful for non-patched APIs or explicit scoping
492
583
  const another = tracked(() => state.name) // ✅ Tracked
493
584
  })
494
585
  ```
495
586
 
496
587
  #### Key Benefits of the Zone System
497
588
 
498
- 1. **Automatic Restoration**: With zones registered in `asyncZone`, most native async APIs (Promises, timers) automatically preserve the reactive context.
499
- 2. **Manual Control**: `access.tracked()` allows you to manually "passport" the context into third-party libraries or unmanaged callbacks.
589
+ 1. **Automatic Restoration**: With zones registered in `asyncZone`, most native async APIs (Promises, timers) automatically preserve the reactive context.
590
+ 2. **Manual Control**: `access.tracked()` allows you to manually "passport" the context into third-party libraries or unmanaged callbacks.
500
591
 
501
592
  ### Using `ascend` for Parent Effect Tracking
502
593
 
@@ -532,18 +623,21 @@ effect(({ ascend }) => {
532
623
  Effects can be created inside other effects and will have separate effect scopes:
533
624
 
534
625
  ```typescript
535
- import { effect, reactive } from 'mutts/reactive'
626
+ import { effect, reactive } from 'mutts'
536
627
 
537
- const state = reactive({ a: 0, b: 0 })
628
+ const state = reactive({
629
+ a: 0,
630
+ b: 0
631
+ })
538
632
 
539
633
  const stopOuter = effect(() => {
540
634
  state.a
541
-
635
+
542
636
  // Create an inner effect with its own scope
543
637
  const stopInner = effect(() => {
544
638
  state.b
545
639
  })
546
-
640
+
547
641
  // Return cleanup function for the inner effect
548
642
  return stopInner
549
643
  })
@@ -554,21 +648,25 @@ const stopOuter = effect(() => {
554
648
  The `untracked()` function allows you to run code without tracking dependencies, which can be useful for creating effects or performing operations that shouldn't be part of the current effect's dependency graph.
555
649
 
556
650
  ```typescript
557
- import { effect, untracked, reactive } from 'mutts/reactive'
651
+ import { effect, untracked, reactive } from 'mutts'
558
652
 
559
- const state = reactive({ a: 0, b: 0 })
653
+ const state = reactive({
654
+ a: 0,
655
+ b: 0
656
+ })
560
657
 
561
658
  effect(() => {
562
659
  state.a
563
-
660
+
564
661
  // Create an inner effect without tracking the creation under the outer effect
565
662
  let stopInner: (() => void) | undefined
663
+
566
664
  untracked(() => {
567
665
  stopInner = effect(() => {
568
666
  state.b
569
667
  })
570
668
  })
571
-
669
+
572
670
  // Optionally stop it immediately to avoid accumulating watchers
573
671
  stopInner && stopInner()
574
672
  })
@@ -584,7 +682,7 @@ effect(() => {
584
682
  Configure the reactive system behavior:
585
683
 
586
684
  ```typescript
587
- import { options as reactiveOptions } from 'mutts/reactive'
685
+ import { options as reactiveOptions } from 'mutts'
588
686
 
589
687
  // Set maximum effect chain depth
590
688
  reactiveOptions.maxEffectChain = 50
@@ -610,9 +708,14 @@ The `effect` function provides convenient shortcut modifiers for common options.
610
708
  Creates an opaque effect that tracks object references rather than deep content. This is useful when you want effects to re-run only when the object identity changes, not when its properties change.
611
709
 
612
710
  ```typescript
613
- import { effect, reactive } from 'mutts/reactive'
711
+ import { effect, reactive } from 'mutts'
614
712
 
615
- const item = reactive({ id: 1, data: { value: 10 } })
713
+ const item = reactive({
714
+ id: 1,
715
+ data: {
716
+ value: 10
717
+ }
718
+ })
616
719
 
617
720
  // Regular effect - triggers on any property change
618
721
  effect(() => {
@@ -624,8 +727,9 @@ effect.opaque(() => {
624
727
  console.log('Data object:', item.data) // Only triggers when item.data is replaced
625
728
  })
626
729
 
627
- item.data.value = 20 // Triggers regular effect, NOT opaque effect
628
- item.data = { value: 30 } // Triggers BOTH effects
730
+ item.data.value = 20 // Triggers regular effect, NOT opaque effect
731
+
732
+ item.data = { value: 30 } // Triggers BOTH effects
629
733
  ```
630
734
 
631
735
  **Use cases for opaque effects:**
@@ -638,9 +742,11 @@ item.data = { value: 30 } // Triggers BOTH effects
638
742
  Creates a named effect for easier debugging and profiling. The name appears in DevTools and debug logs.
639
743
 
640
744
  ```typescript
641
- import { effect, reactive } from 'mutts/reactive'
745
+ import { effect, reactive } from 'mutts'
642
746
 
643
- const state = reactive({ count: 0 })
747
+ const state = reactive({
748
+ count: 0
749
+ })
644
750
 
645
751
  // Create a named effect
646
752
  effect.named('counter-effect')(() => {
@@ -689,13 +795,14 @@ createDataEffect(() => {
689
795
  })
690
796
  ```
691
797
 
798
+ ## Class Reactivity
692
799
 
693
800
  ### `@reactive` Decorator
694
801
 
695
802
  The `@reactive` decorator makes class instances automatically reactive. This is the recommended approach for adding reactivity to classes.
696
803
 
697
804
  ```typescript
698
- import { reactive } from 'mutts/reactive'
805
+ import { reactive } from 'mutts'
699
806
 
700
807
  @reactive
701
808
  class User {
@@ -727,7 +834,7 @@ user.name = "Jane" // Triggers effect
727
834
  You can also use the functional syntax for making classes reactive:
728
835
 
729
836
  ```typescript
730
- import { reactive } from 'mutts/reactive'
837
+ import { reactive } from 'mutts'
731
838
 
732
839
  class User {
733
840
  name: string
@@ -759,11 +866,14 @@ user.name = "Jane" // Triggers effect
759
866
  For complex inheritance trees, especially when you need to solve constructor reactivity issues, extend `ReactiveBase`:
760
867
 
761
868
  ```typescript
762
- import { ReactiveBase, reactive } from 'mutts/reactive'
869
+ import { ReactiveBase, reactive } from 'mutts'
763
870
 
764
871
  class GameObject extends ReactiveBase {
765
872
  id = 'game-object'
766
- position = { x: 0, y: 0 }
873
+ position = {
874
+ x: 0,
875
+ y: 0
876
+ }
767
877
  }
768
878
 
769
879
  class Entity extends GameObject {
@@ -783,11 +893,10 @@ effect(() => {
783
893
  })
784
894
 
785
895
  player.position.x = 10 // Triggers effect
786
- player.health = 80 // Triggers effect
896
+ player.health = 80 // Triggers effect
787
897
  ```
788
898
 
789
899
  **Advantages of `ReactiveBase`:**
790
-
791
900
  1. **Constructor Reactivity**: Solves the issue where `this` in the constructor is not yet reactive
792
901
  2. **Inheritance Safety**: Prevents reactivity from being added to prototype chains in complex inheritance trees
793
902
  3. **No Side Effects**: The base class itself has no effect - it only enables proper reactivity when combined with `@reactive`
@@ -799,7 +908,7 @@ player.health = 80 // Triggers effect
799
908
  - You want the cleanest, most modern syntax
800
909
  - You don't need to modify or use `this` in the constructor
801
910
 
802
- **Use `ReactiveBase` + `@reactive` when:**
911
+ **Use `ReactiveBase`+`@reactive` when:**
803
912
  - You have complex inheritance trees (like game objects, UI components)
804
913
  - You need to modify or use `this` in the constructor
805
914
  - You want to prevent reactivity from being added to prototype chains
@@ -854,7 +963,7 @@ effect(() => {
854
963
  console.log('Cart items:', cart.items)
855
964
  })
856
965
 
857
- cart.addItem('Apple') // Triggers effect
966
+ cart.addItem('Apple') // Triggers effect
858
967
  cart.removeItem('Apple') // Triggers effect
859
968
  ```
860
969
 
@@ -907,15 +1016,23 @@ function unreactive(target: Constructor<T>): Constructor<T>
907
1016
 
908
1017
  ```typescript
909
1018
  // Mark individual object as non-reactive
910
- const obj = { count: 0 }
1019
+ const obj = {
1020
+ count: 0
1021
+ }
1022
+
911
1023
  unreactive(obj)
1024
+
912
1025
  const reactiveObj = reactive(obj) // Returns obj unchanged
913
1026
 
914
1027
  // Mark entire class as non-reactive
915
1028
  class Utility {
916
- static helper() { return 'help' }
1029
+ static helper() {
1030
+ return 'help'
1031
+ }
917
1032
  }
1033
+
918
1034
  unreactive(Utility)
1035
+
919
1036
  const instance = new Utility()
920
1037
  const reactiveInstance = reactive(instance) // Returns instance unchanged
921
1038
  ```
@@ -929,7 +1046,6 @@ Mark class properties as non-reactive using class-level syntax.
929
1046
  @unreactive('id')
930
1047
  class User {
931
1048
  id: string = 'user-123'
932
-
933
1049
  name: string = 'John'
934
1050
  age: number = 30
935
1051
  }
@@ -938,7 +1054,7 @@ const user = new User()
938
1054
 
939
1055
  effect(() => {
940
1056
  console.log(user.name, user.age) // Tracks these
941
- console.log(user.id) // Does NOT track this
1057
+ console.log(user.id) // Does NOT track this
942
1058
  })
943
1059
 
944
1060
  user.name = 'Jane' // Triggers effect
@@ -1040,23 +1156,29 @@ Non-reactive objects can improve performance:
1040
1156
  ```typescript
1041
1157
  // Good: Mark large, rarely-changing objects as non-reactive
1042
1158
  const config = unreactive({
1043
- apiEndpoints: { /* large config object */ },
1044
- featureFlags: { /* many flags */ }
1159
+ apiEndpoints: {
1160
+ /* large config object */
1161
+ },
1162
+ featureFlags: {
1163
+ /* many flags */
1164
+ }
1045
1165
  })
1046
1166
 
1047
1167
  // Good: Mark utility classes as non-reactive
1048
1168
  class MathUtils {
1049
1169
  static PI = 3.14159
1050
- static square(x: number) { return x * x }
1170
+
1171
+ static square(x: number) {
1172
+ return x * x
1173
+ }
1051
1174
  }
1175
+
1052
1176
  unreactive(MathUtils)
1053
1177
 
1054
1178
  // Good: Mark properties that don't need reactivity
1055
1179
  class User {
1056
- @unreactive
1057
- metadata: any = {} // Large metadata object
1180
+ @unreactive metadata: any = {} // Large metadata object
1058
1181
 
1059
1182
  name: string = 'John' // This should be reactive
1060
1183
  }
1061
- ```
1062
-
1184
+ ```