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
@@ -5,7 +5,7 @@
5
5
  The `atomic` function wraps a function to batch all reactive effects triggered within it, ensuring effects run only once after the function completes. It can be used both as a function wrapper and as a decorator.
6
6
 
7
7
  ```typescript
8
- import { atomic, reactive, effect } from 'mutts/reactive'
8
+ import { atomic, reactive, effect } from 'mutts'
9
9
 
10
10
  const state = reactive({ a: 0, b: 0 })
11
11
 
@@ -48,7 +48,7 @@ The wrapped function preserves its signature (parameters and return value), and
48
48
  While `atomic()` **wraps** a function for later calls, `atom()` **runs** a function immediately and atomically. It always executes right away, even inside a nested batch.
49
49
 
50
50
  ```typescript
51
- import { atom, reactive, effect } from 'mutts/reactive'
51
+ import { atom, reactive, effect } from 'mutts'
52
52
 
53
53
  const state = reactive({ a: 0, b: 0 })
54
54
 
@@ -85,9 +85,9 @@ update(1, 2) // runs when called
85
85
  When an effect needs to perform an action that would modify state the effect depends on, this can create a reactive cycle. The `addBatchCleanup` function (also exported as `defer` for semantic clarity) allows you to defer such work until after the current batch of effects completes.
86
86
 
87
87
  ```typescript
88
- import { addBatchCleanup, effect, reactive } from 'mutts/reactive'
88
+ import { addBatchCleanup, effect, reactive } from 'mutts'
89
89
  // or use the semantic alias:
90
- // import { defer } from 'mutts/reactive'
90
+ // import { defer } from 'mutts'
91
91
 
92
92
  const state = reactive({
93
93
  items: [],
@@ -298,7 +298,7 @@ effect(() => {
298
298
  Creates a bidirectional binding between a reactive value and a non-reactive external value (like DOM elements), automatically preventing infinite loops.
299
299
 
300
300
  ```typescript
301
- import { biDi, reactive } from 'mutts/reactive'
301
+ import { biDi, reactive } from 'mutts'
302
302
 
303
303
  const model = reactive({ value: '' })
304
304
 
@@ -1142,15 +1142,14 @@ Mutts provides several ways to derive values from reactive state. They differ in
1142
1142
  | `lift(() => ({...}))` | Eager | Reactive object proxy | Yes (per-prop) | **Yes** | `result[cleanup]()` | Derived objects, computed shapes |
1143
1143
  | `morph(source, fn)` | **Lazy** | Reactive proxy | Yes (per-key) | **Yes** | `cleanedBy` | Lazy per-element map with identity tracking |
1144
1144
  | `morph.pure(source, fn)` | **Lazy** | Reactive proxy | Yes (per-key) | **Yes** | `cleanedBy` | Same, but skips per-item dependency tracking |
1145
- | `scan(source, fn, init)` | Eager | Reactive array | Yes (per-index) | **Yes** | `result[cleanup]()` | Running accumulations (prefix sums) |
1146
1145
  | `when(() => cond)` | Eager | Promise\<T\> | N/A | N/A | Auto (on resolve/timeout) | Awaiting a reactive condition |
1147
1146
  | `watch(source, cb)` | Eager | Callback (old/new) | N/A | N/A | Auto (parent/GC) + Returned cleanup | Observing specific changes |
1148
1147
 
1149
1148
  ### Key distinctions
1150
1149
 
1151
1150
  - **Lazy vs Eager**: `memoize` only recomputes when the result is read. Everything else recomputes immediately when dependencies change — even if nobody is consuming the output.
1152
- - **Trackable**: Can downstream effects depend on the result? `memoize`'s return value is trackable because calling it runs inside an effect context. `lift`/`morph`/`scan` return reactive proxies where each property/index is independently trackable.
1153
- - **Identity stable**: `lift`, `morph`, and `scan` return the **same proxy** across recomputations — only changed slots are updated. This is critical for downstream transformations or DOM reconciliation that relies on reference identity.
1151
+ - **Trackable**: Can downstream effects depend on the result? `memoize`'s return value is trackable because calling it runs inside an effect context. `lift`/`morph` return reactive proxies where each property/index is independently trackable.
1152
+ - **Identity stable**: `lift` and `morph` return the **same proxy** across recomputations — only changed slots are updated. This is critical for downstream transformations or DOM reconciliation that relies on reference identity.
1154
1153
 
1155
1154
  ### Common patterns
1156
1155
 
@@ -1229,7 +1228,7 @@ state.items = fetchedItems // deep touch diffs old vs new per-index — no lift
1229
1228
  **Signature**
1230
1229
 
1231
1230
  ```typescript
1232
- import { morph } from 'mutts/reactive'
1231
+ import { morph } from 'mutts'
1233
1232
 
1234
1233
  function morph<I, O>(
1235
1234
  source: readonly I[] | (() => readonly I[]),
@@ -1254,7 +1253,7 @@ function morph<I, O>(
1254
1253
  **Basic usage**
1255
1254
 
1256
1255
  ```typescript
1257
- import { morph, reactive, effect } from 'mutts/reactive'
1256
+ import { morph, reactive, effect } from 'mutts'
1258
1257
 
1259
1258
  const items = reactive(['alice', 'bob', 'charlie'])
1260
1259
  const upper = morph(items, name => name.toUpperCase())
@@ -1355,7 +1354,7 @@ pure[0] // 10 — stale, no per-item effect to invalidate
1355
1354
  **Signature**
1356
1355
 
1357
1356
  ```typescript
1358
- import { memoize } from 'mutts/reactive'
1357
+ import { memoize } from 'mutts'
1359
1358
 
1360
1359
  type Memoizable = object | any[] | symbol
1361
1360
 
@@ -1380,7 +1379,7 @@ function memoize<Result>(
1380
1379
  **Basic usage**
1381
1380
 
1382
1381
  ```typescript
1383
- import { effect, memoize, reactive } from 'mutts/reactive'
1382
+ import { effect, memoize, reactive } from 'mutts'
1384
1383
 
1385
1384
  const source = reactive({ value: 1 })
1386
1385
  const args = { node: source }
@@ -1426,7 +1425,7 @@ Use `maxArgs` when the memoized function should only consider the first _n_ argu
1426
1425
  A flavored variant that gracefully handles non-WeakKey arguments (primitives, `null`, `undefined`). Instead of throwing, it falls back to recomputing the function without caching.
1427
1426
 
1428
1427
  ```typescript
1429
- import { memoize } from 'mutts/reactive'
1428
+ import { memoize } from 'mutts'
1430
1429
 
1431
1430
  const process = memoize.lenient((value: string | { data: string }) => {
1432
1431
  return typeof value === 'string' ? value.toUpperCase() : value.data
@@ -1447,7 +1446,7 @@ This is useful when a memoized function may receive both objects and primitives,
1447
1446
  Apply `@memoize` (or `@memoize.lenient`) to class getters or methods to share the same cache semantics. Getters cache per instance; methods cache per instance and argument tuple.
1448
1447
 
1449
1448
  ```typescript
1450
- import { memoize, reactive } from 'mutts/reactive'
1449
+ import { memoize, reactive } from 'mutts'
1451
1450
 
1452
1451
  class Example {
1453
1452
  state = reactive({ count: 0 })
@@ -137,6 +137,5 @@ attend(
137
137
  | `attend` | — | Reactive lifecycle per key |
138
138
  | `organized` | ✅ | Reactive record mapping with access objects |
139
139
  | `project` | ❌ | Reactive collection mapping (manages a target + projection context) |
140
- | `scan` | ❌ | Reactive accumulation (sequential key dependency) |
141
140
 
142
- `project` and `scan` have additional concerns (target management, sequential dependencies) that go beyond `attend`'s independent-key lifecycle model.
141
+ `project` has additional concerns (target management) that go beyond `attend`'s independent-key lifecycle model.
@@ -259,153 +259,6 @@ effect(() => {
259
259
  })
260
260
  ```
261
261
 
262
- ### `Register`
263
-
264
- `Register` is an ordered, array-like collection that keeps a stable mapping between keys and values. It is useful when you need array semantics (indexable access, ordering, iteration) but also require identity preservation by key—ideal for UI lists keyed by IDs or when you want to memoise entries across reorders.
265
-
266
- ```typescript
267
- import { Register } from 'mutts/reactive'
268
-
269
- // Create a register where the key comes from the `id` field
270
- const list = new Register(({id}: { id: number }) => id, [
271
- { id: 1, label: 'Alpha' },
272
- { id: 2, label: 'Bravo' },
273
- ])
274
-
275
- effect(() => {
276
- console.log('Length:', list.length)
277
- console.log('First label:', list[0]?.label)
278
- })
279
-
280
- // Push uses the key function to keep identities stable
281
- list.push({ id: 3, label: 'Charlie' })
282
-
283
- // Replacing with the same key updates watchers without creating a new identity
284
- list[0] = { id: 1, label: 'Alpha (updated)' }
285
-
286
- // Access by key
287
- const second = list.get(2) // { id: 2, label: 'Bravo' }
288
-
289
- // Duplicate keys share value identity
290
- list.push({ id: 2, label: 'Bravo (new data)' })
291
- console.log(list[1] === list[2]) // true
292
- ```
293
-
294
- **Highlights:**
295
-
296
- - Fully indexable (`list[0]`, `list.at(-1)`, `list.length`, iteration, etc.) thanks to the shared `Indexable` infrastructure.
297
- - Complete array surface forwarding (`map`, `filter`, `reduce`, `concat`, `reverse`, `sort`, `fill`, `copyWithin`, and more) with reactivity preserved.
298
- - Stable key/value map under the hood allows quick lookups via `get()`, `hasKey()`, and `indexOfKey()`.
299
- - When the same key appears multiple times, all slots reference the same underlying value instance, making deduplication and memoisation straightforward.
300
- - Reordering operations emit index-level touches so list reactivity remains predictable in rendered UIs.
301
-
302
- ### Register-specific API (beyond Array)
303
-
304
- The `Register` exposes additional methods and behaviors that standard arrays do not have:
305
-
306
- - `get(key)` / `set(key, value)`
307
- - `get(key: K): T | undefined` returns the latest value for a key.
308
- - `set(key: K, value: T): void` updates the value for an existing key (no-op if key absent).
309
- - Example:
310
- ```typescript
311
- list.set(2, { id: 2, label: 'Bravo (updated)' })
312
- const v = list.get(2)
313
- ```
314
-
315
- - `hasKey(key)` / `indexOfKey(key)`
316
- - `hasKey(key: K): boolean` whether the key is present in any slot.
317
- - `indexOfKey(key: K): number` first index at which the key appears, or `-1`.
318
-
319
- - `remove(key)` / `removeAt(index)`
320
- - `remove(key: K): void` removes all occurrences of `key` from the register.
321
- - `removeAt(index: number): T | undefined` removes a single slot by index and returns its value.
322
-
323
- - `keep(predicate)`
324
- - `keep(predicate: (value: T) => boolean): void` keeps only items for which the predicate returns `true`; items for which it returns `false` are removed. The predicate is evaluated once per distinct key; duplicate keys follow the same decision.
325
-
326
- - `update(...values)`
327
- - `update(...values: T[]): void` updates existing entries by their key; ignores values whose key is not yet present.
328
-
329
- - `upsert(insert, ...values)`
330
- - `upsert(insert: (value: T) => void, ...values: T[]): void` updates by key when present, otherwise calls `insert(value)` so you can decide how to insert (e.g. `push`, `unshift`, or `splice`).
331
- - Example:
332
- ```typescript
333
- list.upsert(v => list.push(v), { id: 4, label: 'Delta' }, { id: 2, label: 'Bravo (again)' })
334
- ```
335
-
336
- - `entries()`
337
- - Iterates `[number, value]` pairs in index order: `IterableIterator<[number, T | undefined]>`.
338
-
339
- - `keys` / `values`
340
- - `keys: ArrayIterator<number>` provides the index iterator (mirrors `Array#keys()`).
341
- - `values: IterableIterator<T>` provides an iterator of values (same as default iteration).
342
-
343
- - `clear()`
344
- - Removes all entries and disposes internal key-tracking effects.
345
-
346
- - `toArray()` / `toString()`
347
- - `toArray(): T[]` materializes the current values into a plain array.
348
- - `toString(): string` returns a concise description like `[Register length=3]`.
349
-
350
- ### Register CRUD Events
351
-
352
- `Register` emits lifecycle events for add, delete, update, and rekey operations. This enables side effects like logging, syncing with external systems, or triggering derived updates.
353
-
354
- ```typescript
355
- const list = register(({id}: { id: number }) => id)
356
-
357
- // Listen to individual events
358
- list.on('add', (item, key, index) => {
359
- console.log(`Added ${key} at index ${index}:`, item)
360
- })
361
-
362
- list.on('delete', (item, key, index) => {
363
- console.log(`Removed ${key} from index ${index}:`, item)
364
- })
365
-
366
- list.on('update', (oldItem, newItem, key, index) => {
367
- console.log(`Updated ${key} at index ${index}`)
368
- })
369
-
370
- list.on('rekey', (item, oldKey, newKey, index) => {
371
- console.log(`Key changed from ${oldKey} to ${newKey}`)
372
- })
373
-
374
- // Bulk event registration
375
- list.on({
376
- add: (item) => console.log('Added:', item),
377
- delete: (item) => console.log('Deleted:', item),
378
- })
379
-
380
- // Global hook - receive all events
381
- const unhook = list.hook((event, ...args) => {
382
- console.log(`Event: ${String(event)}`, args)
383
- })
384
-
385
- // Unsubscribe
386
- const unsubscribe = list.on('add', handler)
387
- unsubscribe()
388
- ```
389
-
390
- **Event Types:**
391
-
392
- | Event | Arguments | Description |
393
- |-------|-----------|-------------|
394
- | `add` | `(item, key, index)` | New item added to register |
395
- | `delete` | `(item, key, index)` | Item removed from register |
396
- | `update` | `(oldItem, newItem, key, index)` | Item value updated (same key) |
397
- | `rekey` | `(item, oldKey, newKey, index)` | Item's key changed |
398
-
399
- **Use Cases:**
400
- - Audit logging
401
- - Syncing with databases
402
- - Triggering notifications
403
- - Cascading updates to dependent systems
404
-
405
- Notes:
406
- - Direct length modification via `list.length = n` is not supported; use `splice` instead.
407
- - Assigning to an index (`list[i] = value`) uses the key function to bind that slot to `value`’s key.
408
-
409
262
  ## Class Reactivity
410
263
 
411
264
  ## Morphing
@@ -417,7 +270,7 @@ Notes:
417
270
  #### Basic Usage
418
271
 
419
272
  ```typescript
420
- import { cleanup, morph, reactive } from 'mutts/reactive'
273
+ import { cleanup, morph, reactive } from 'mutts'
421
274
 
422
275
  // Arrays
423
276
  const users = reactive([{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }])
@@ -548,7 +401,7 @@ result[cleanup]() // Stops all effects and cleans up
548
401
  ### `organized()`
549
402
 
550
403
  ```typescript
551
- import { cleanup, organized, reactive } from 'mutts/reactive'
404
+ import { cleanup, organized, reactive } from 'mutts'
552
405
 
553
406
  const source = reactive<Record<string, number>>({ apples: 1, oranges: 2 })
554
407