mutts 1.0.7 → 1.0.9

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 (191) hide show
  1. package/README.md +72 -17
  2. package/dist/browser.cjs +3111 -0
  3. package/dist/browser.cjs.map +1 -0
  4. package/dist/browser.d.ts +1391 -1
  5. package/dist/browser.esm.js +264 -35
  6. package/dist/browser.esm.js.map +1 -1
  7. package/dist/chunks/async-browser-lvzLOCgk.cjs +297 -0
  8. package/dist/chunks/async-browser-lvzLOCgk.cjs.map +1 -0
  9. package/dist/chunks/async-core-CRLKP3l-.cjs +29 -0
  10. package/dist/chunks/async-core-CRLKP3l-.cjs.map +1 -0
  11. package/dist/chunks/async-node-C3DeIb0y.cjs +96 -0
  12. package/dist/chunks/async-node-C3DeIb0y.cjs.map +1 -0
  13. package/dist/chunks/index-2vea86wD.esm.js +3011 -0
  14. package/dist/chunks/index-2vea86wD.esm.js.map +1 -0
  15. package/dist/chunks/index-CtA2AWl3.esm.js +4376 -0
  16. package/dist/chunks/index-CtA2AWl3.esm.js.map +1 -0
  17. package/dist/chunks/index-VTO-b2vR.cjs +4461 -0
  18. package/dist/chunks/index-VTO-b2vR.cjs.map +1 -0
  19. package/dist/debug/debug.d.ts +122 -0
  20. package/dist/debug/debug.d.ts.map +1 -0
  21. package/dist/debug/index.d.ts +4 -0
  22. package/dist/debug/index.d.ts.map +1 -0
  23. package/dist/debug/lineage-panel.d.ts +5 -0
  24. package/dist/debug/lineage-panel.d.ts.map +1 -0
  25. package/dist/debug/lineage.d.ts +79 -0
  26. package/dist/debug/lineage.d.ts.map +1 -0
  27. package/dist/debug.cjs +35 -0
  28. package/dist/debug.cjs.map +1 -0
  29. package/dist/debug.d.ts +209 -0
  30. package/dist/debug.esm.js +2 -0
  31. package/dist/debug.esm.js.map +1 -0
  32. package/dist/devtools/devtool/devtools.d.ts +1 -0
  33. package/dist/devtools/devtool/devtools.d.ts.map +1 -0
  34. package/dist/devtools/devtool/panel.d.ts +2 -0
  35. package/dist/devtools/devtool/panel.d.ts.map +1 -0
  36. package/dist/index.d.ts +1 -0
  37. package/dist/mutts.umd.js +1 -1
  38. package/dist/mutts.umd.js.map +1 -1
  39. package/dist/mutts.umd.min.js +1 -1
  40. package/dist/mutts.umd.min.js.map +1 -1
  41. package/dist/node.cjs +107 -0
  42. package/dist/node.cjs.map +1 -0
  43. package/dist/node.d.ts +2 -2
  44. package/dist/node.esm.js +86 -33
  45. package/dist/node.esm.js.map +1 -1
  46. package/dist/src/async/browser.d.ts +2 -0
  47. package/dist/src/async/browser.d.ts.map +1 -0
  48. package/dist/src/async/index.d.ts +19 -0
  49. package/dist/src/async/index.d.ts.map +1 -0
  50. package/dist/src/async/node.d.ts +2 -0
  51. package/dist/src/async/node.d.ts.map +1 -0
  52. package/dist/src/decorator.d.ts +106 -0
  53. package/dist/src/decorator.d.ts.map +1 -0
  54. package/dist/src/destroyable.d.ts +87 -0
  55. package/dist/src/destroyable.d.ts.map +1 -0
  56. package/dist/src/entry-browser.d.ts +3 -0
  57. package/dist/src/entry-browser.d.ts.map +1 -0
  58. package/dist/src/entry-node.d.ts +3 -0
  59. package/dist/src/entry-node.d.ts.map +1 -0
  60. package/dist/src/eventful.d.ts +20 -0
  61. package/dist/src/eventful.d.ts.map +1 -0
  62. package/dist/src/flavored.d.ts +33 -0
  63. package/dist/src/flavored.d.ts.map +1 -0
  64. package/dist/src/index.d.ts +14 -0
  65. package/dist/src/index.d.ts.map +1 -0
  66. package/dist/src/indexable.d.ts +243 -0
  67. package/dist/src/indexable.d.ts.map +1 -0
  68. package/dist/src/introspection.d.ts +27 -0
  69. package/dist/src/introspection.d.ts.map +1 -0
  70. package/dist/src/iterableWeak.d.ts +53 -0
  71. package/dist/src/iterableWeak.d.ts.map +1 -0
  72. package/dist/src/mixins.d.ts +25 -0
  73. package/dist/src/mixins.d.ts.map +1 -0
  74. package/dist/src/promiseChain.d.ts +20 -0
  75. package/dist/src/promiseChain.d.ts.map +1 -0
  76. package/dist/src/reactive/array.d.ts +48 -0
  77. package/dist/src/reactive/array.d.ts.map +1 -0
  78. package/dist/src/reactive/buffer.d.ts +120 -0
  79. package/dist/src/reactive/buffer.d.ts.map +1 -0
  80. package/dist/src/reactive/change.d.ts +29 -0
  81. package/dist/src/reactive/change.d.ts.map +1 -0
  82. package/dist/src/reactive/deep-touch.d.ts +28 -0
  83. package/dist/src/reactive/deep-touch.d.ts.map +1 -0
  84. package/dist/src/reactive/deep-watch-state.d.ts +25 -0
  85. package/dist/src/reactive/deep-watch-state.d.ts.map +1 -0
  86. package/dist/src/reactive/deep-watch.d.ts +20 -0
  87. package/dist/src/reactive/deep-watch.d.ts.map +1 -0
  88. package/dist/src/reactive/describe.d.ts +12 -0
  89. package/dist/src/reactive/describe.d.ts.map +1 -0
  90. package/dist/src/reactive/effect-context.d.ts +34 -0
  91. package/dist/src/reactive/effect-context.d.ts.map +1 -0
  92. package/dist/src/reactive/effects.d.ts +164 -0
  93. package/dist/src/reactive/effects.d.ts.map +1 -0
  94. package/dist/src/reactive/index.d.ts +19 -0
  95. package/dist/src/reactive/index.d.ts.map +1 -0
  96. package/dist/src/reactive/map.d.ts +28 -0
  97. package/dist/src/reactive/map.d.ts.map +1 -0
  98. package/dist/src/reactive/memoize.d.ts +28 -0
  99. package/dist/src/reactive/memoize.d.ts.map +1 -0
  100. package/dist/src/reactive/non-reactive-state.d.ts +9 -0
  101. package/dist/src/reactive/non-reactive-state.d.ts.map +1 -0
  102. package/dist/src/reactive/non-reactive.d.ts +11 -0
  103. package/dist/src/reactive/non-reactive.d.ts.map +1 -0
  104. package/dist/src/reactive/project.d.ts +40 -0
  105. package/dist/src/reactive/project.d.ts.map +1 -0
  106. package/dist/src/reactive/proxy-state.d.ts +8 -0
  107. package/dist/src/reactive/proxy-state.d.ts.map +1 -0
  108. package/dist/src/reactive/proxy.d.ts +23 -0
  109. package/dist/src/reactive/proxy.d.ts.map +1 -0
  110. package/dist/src/reactive/record.d.ts +115 -0
  111. package/dist/src/reactive/record.d.ts.map +1 -0
  112. package/dist/src/reactive/register.d.ts +125 -0
  113. package/dist/src/reactive/register.d.ts.map +1 -0
  114. package/dist/src/reactive/registry.d.ts +21 -0
  115. package/dist/src/reactive/registry.d.ts.map +1 -0
  116. package/dist/src/reactive/set.d.ts +26 -0
  117. package/dist/src/reactive/set.d.ts.map +1 -0
  118. package/dist/src/reactive/tracking.d.ts +7 -0
  119. package/dist/src/reactive/tracking.d.ts.map +1 -0
  120. package/dist/src/reactive/types.d.ts +424 -0
  121. package/dist/src/reactive/types.d.ts.map +1 -0
  122. package/dist/src/reactive/watch.d.ts +48 -0
  123. package/dist/src/reactive/watch.d.ts.map +1 -0
  124. package/dist/src/std-decorators.d.ts +45 -0
  125. package/dist/src/std-decorators.d.ts.map +1 -0
  126. package/dist/src/utils.d.ts +49 -0
  127. package/dist/src/utils.d.ts.map +1 -0
  128. package/dist/src/zone.d.ts +40 -0
  129. package/dist/src/zone.d.ts.map +1 -0
  130. package/dist/types-DaHFfhlN.d.ts +414 -0
  131. package/docs/ai/api-reference.md +1 -1
  132. package/docs/ai/manual.md +443 -17
  133. package/docs/events.md +57 -2
  134. package/docs/flavored.md +210 -0
  135. package/docs/reactive/advanced.md +2 -3
  136. package/docs/reactive/attend.md +143 -0
  137. package/docs/reactive/collections.md +55 -0
  138. package/docs/reactive/core.md +120 -4
  139. package/docs/reactive/debugging.md +8 -10
  140. package/docs/reactive/describe.md +85 -0
  141. package/docs/reactive/error-handling.md +121 -0
  142. package/docs/reactive/project.md +1 -1
  143. package/docs/reactive/scan.md +215 -0
  144. package/docs/reactive.md +2 -0
  145. package/docs/std-decorators.md +171 -52
  146. package/docs/zone.md +9 -6
  147. package/package.json +44 -27
  148. package/src/async/browser.ts +277 -41
  149. package/src/async/index.ts +23 -4
  150. package/src/async/node.ts +78 -32
  151. package/src/entry-browser.ts +5 -0
  152. package/src/entry-node.ts +5 -0
  153. package/src/eventful.ts +82 -49
  154. package/src/flavored.ts +106 -0
  155. package/src/index.d.ts +12 -9
  156. package/src/index.ts +2 -1
  157. package/src/introspection.ts +1 -1
  158. package/src/reactive/array.ts +160 -80
  159. package/src/reactive/buffer.ts +187 -27
  160. package/src/reactive/change.ts +16 -23
  161. package/src/reactive/deep-touch.ts +19 -14
  162. package/src/reactive/deep-watch-state.ts +3 -3
  163. package/src/reactive/deep-watch.ts +7 -4
  164. package/src/reactive/describe.ts +39 -0
  165. package/src/reactive/effect-context.ts +71 -6
  166. package/src/reactive/effects.ts +445 -393
  167. package/src/reactive/index.ts +12 -14
  168. package/src/reactive/map.ts +8 -24
  169. package/src/reactive/memoize.ts +26 -3
  170. package/src/reactive/project.md +107 -0
  171. package/src/reactive/project.ts +48 -71
  172. package/src/reactive/proxy.ts +21 -3
  173. package/src/reactive/record.ts +30 -45
  174. package/src/reactive/register.ts +138 -19
  175. package/src/reactive/registry.ts +22 -9
  176. package/src/reactive/set.ts +7 -20
  177. package/src/reactive/tracking.ts +9 -12
  178. package/src/reactive/types.ts +104 -24
  179. package/src/reactive/{interface.ts → watch.ts} +29 -59
  180. package/src/std-decorators.ts +56 -17
  181. package/src/utils.ts +3 -4
  182. package/src/zone.ts +43 -28
  183. package/dist/browser.js +0 -161
  184. package/dist/browser.js.map +0 -1
  185. package/dist/chunks/index-BFYK02LG.js +0 -6378
  186. package/dist/chunks/index-BFYK02LG.js.map +0 -1
  187. package/dist/chunks/index-CNR6QRUl.esm.js +0 -6291
  188. package/dist/chunks/index-CNR6QRUl.esm.js.map +0 -1
  189. package/dist/node.js +0 -136
  190. package/dist/node.js.map +0 -1
  191. package/src/reactive/debug.ts +0 -517
@@ -1,4 +1,4 @@
1
- TODO: redo the doc here
1
+
2
2
  # Standard decorators
3
3
 
4
4
  A TypeScript library that provides standard decorators that should stop being re-implemented for the 50th time
@@ -189,13 +189,13 @@ When the decorator is applied to a non-getter method:
189
189
  3. **Memory overhead**: Cached values are stored indefinitely
190
190
  4. **Getter-only**: Can only be applied to getter methods, not setters or regular methods
191
191
 
192
- ## Describe
192
+ ## Descriptor
193
193
 
194
- The `describe` decorator provides a clean, reusable way to configure property descriptors (enumerable, configurable, writable) for class properties. This decorator uses a functional approach that makes it easy to create reusable descriptor configurations.
194
+ The `descriptor` decorator provides a clean, reusable way to configure property descriptors (enumerable, configurable, writable) for class properties. This decorator uses a functional approach that makes it easy to create reusable descriptor configurations.
195
195
 
196
196
  ## API Reference
197
197
 
198
- ### `describe(descriptor: PropertyDescriptor): <T>(...properties: (keyof T)[]) => GenericClassDecorator<T>`
198
+ ### `descriptor(descriptor: PropertyDescriptor): <T>(...properties: (keyof T)[]) => GenericClassDecorator<T>`
199
199
 
200
200
  A function that creates a decorator to configure property descriptors for specified properties.
201
201
 
@@ -207,29 +207,68 @@ A function that creates a decorator to configure property descriptors for specif
207
207
 
208
208
  **Returns:** A function that takes property names and returns a class decorator
209
209
 
210
+ **Flavors:**
211
+ The `descriptor` function is flavored with the following chainable properties:
212
+ - `.enumerable` - Sets enumerable to true
213
+ - `.hidden` - Sets enumerable to false
214
+ - `.configurable` - Sets configurable to true
215
+ - `.frozen` - Sets configurable to false
216
+ - `.writable` - Sets writable to true
217
+ - `.readonly` - Sets writable to false
218
+
210
219
  **Usage Pattern:**
211
220
  ```typescript
212
- const readonly = describe({ writable: false })
213
- const hidden = describe({ enumerable: false })
214
- const locked = describe({ configurable: false })
221
+ // Using flavors for single properties
222
+ @descriptor.readonly('id', 'createdAt')
223
+ @descriptor.hidden('_private')
224
+ class MyClass { }
215
225
 
216
- @readonly('id', 'createdAt')
217
- @hidden('_private')
218
- @locked('critical')
226
+ // For multiple descriptor properties on the same property, use object syntax
227
+ @descriptor({ writable: false, enumerable: false, configurable: false })('secret')
219
228
  class MyClass { }
229
+
230
+ // Note: Stacking multiple flavored decorators on the same property is not recommended
231
+ // as each decorator creates a new subclass and only the last applied takes effect
220
232
  ```
221
233
 
222
234
  ## Usage Examples
223
235
 
236
+ ### Using Flavors
237
+
238
+ ```typescript
239
+ import { descriptor } from 'mutts/std-decorators'
240
+
241
+ // Use flavors directly for cleaner syntax
242
+ @descriptor.readonly('id', 'createdAt')
243
+ @descriptor.hidden('_private', '_cache')
244
+ @descriptor.frozen('critical')
245
+ class User {
246
+ id: string = 'user-123'
247
+ name: string = 'John'
248
+ _private: string = 'secret'
249
+ _cache: Map<string, any> = new Map()
250
+ createdAt: Date = new Date()
251
+ critical: string = 'locked'
252
+
253
+ constructor(name: string) {
254
+ this.name = name
255
+ }
256
+ }
257
+
258
+ const user = new User('Alice')
259
+ console.log(Object.keys(user)) // ['name', 'createdAt'] - only enumerable properties
260
+ // user.id = 'new-id' // TypeError: Cannot assign to read only property 'id'
261
+ ```
262
+
224
263
  ### Creating Reusable Decorators
225
264
 
226
265
  ```typescript
227
- import { describe } from 'mutts/std-decorators'
266
+ import { descriptor } from 'mutts/std-decorators'
228
267
 
229
268
  // Create reusable descriptor configurations
230
- const readonly = describe({ writable: false })
231
- const hidden = describe({ enumerable: false })
232
- const locked = describe({ configurable: false })
269
+ const readonly = descriptor.readonly
270
+ const hidden = descriptor.hidden
271
+ const locked = descriptor.frozen
233
272
 
234
273
  // Use them on classes
235
274
  @readonly('id', 'createdAt')
@@ -255,11 +294,10 @@ console.log(Object.keys(user)) // ['id', 'name', 'createdAt', 'critical'] - only
255
294
  ### Making Properties Non-Enumerable
256
295
 
257
296
  ```typescript
258
- import { describe } from 'mutts/std-decorators'
259
-
260
- const hidden = describe({ enumerable: false })
297
+ import { descriptor } from 'mutts/std-decorators'
261
298
 
262
- @hidden('_internal', '_cache', 'debug')
299
+ // Use the hidden flavor directly
300
+ @descriptor.hidden('_internal', '_cache', 'debug')
263
301
  class CacheManager {
264
302
  public data: any[] = []
265
303
  _internal: Map<string, any> = new Map()
@@ -280,13 +318,10 @@ console.log(Object.getOwnPropertyNames(cache)) // ['data', '_internal', '_cache'
280
318
  ### Read-Only Properties
281
319
 
282
320
  ```typescript
283
- import { describe } from 'mutts/std-decorators'
321
+ import { descriptor } from 'mutts/std-decorators'
284
322
 
285
- const readonly = describe({ writable: false })
286
- const readonlyLocked = describe({ writable: false, configurable: false })
287
-
288
- @readonly('createdAt', 'version')
289
- @readonlyLocked('id')
323
+ // Use flavors for read-only properties
324
+ @descriptor.readonly('createdAt', 'version')
290
325
  class Document {
291
326
  id: string
292
327
  title: string
@@ -306,25 +341,41 @@ class Document {
306
341
  }
307
342
 
308
343
  const doc = new Document('doc-1', 'My Document')
309
- // doc.id = 'new-id' // TypeError: Cannot assign to read only property 'id'
310
344
  // doc.createdAt = new Date() // TypeError: Cannot assign to read only property 'createdAt'
345
+ // doc.version = 2 // TypeError: Cannot assign to read only property 'version'
311
346
  doc.updateTitle('Updated Title') // This works
312
347
  ```
313
348
 
314
- ### Configuration Control
349
+ ### Combining Multiple Flavors
315
350
 
316
351
  ```typescript
317
- import { describe } from 'mutts/std-decorators'
352
+ import { descriptor } from 'mutts/std-decorators'
318
353
 
319
- const locked = describe({ configurable: false })
320
- const frozen = describe({ configurable: false, writable: false })
354
+ // Stack multiple flavored decorators
355
+ @descriptor.readonly('secret')
356
+ @descriptor.hidden('secret')
357
+ @descriptor.frozen('secret')
358
+ class SecureData {
359
+ public info: string = 'public'
360
+ secret: string = 'top secret'
361
+ }
321
362
 
322
- @locked('_sealed')
323
- @frozen('_frozen')
363
+ const data = new SecureData()
364
+ // data.secret = 'leaked' // TypeError: Cannot assign to read only property 'secret'
365
+ console.log(Object.keys(data)) // ['info'] - secret is not enumerable
366
+ // Object.defineProperty(data, 'secret', { value: 'new' }) // TypeError: Cannot redefine property
367
+ ```
368
+
369
+ ### Configuration Control
370
+
371
+ ```typescript
372
+ import { descriptor } from 'mutts/std-decorators'
373
+
374
+ // Use flavors for configuration control
375
+ @descriptor.frozen('_sealed')
324
376
  class SecureObject {
325
377
  public data: any
326
378
  _sealed: string = 'cannot be reconfigured'
327
- _frozen: string = 'cannot be changed or reconfigured'
328
379
 
329
380
  constructor(data: any) {
330
381
  this.data = data
@@ -333,41 +384,41 @@ class SecureObject {
333
384
 
334
385
  const obj = new SecureObject({ key: 'value' })
335
386
  // Object.defineProperty(obj, '_sealed', { value: 'new' }) // TypeError: Cannot redefine property
336
- // Object.defineProperty(obj, '_frozen', { value: 'new' }) // TypeError: Cannot redefine property
337
387
  ```
338
388
 
339
389
  ## Implementation Details
340
390
 
341
391
  ### Functional Approach
342
392
 
343
- The `describe` decorator uses a functional approach that separates descriptor configuration from property selection:
393
+ The `descriptor` function uses a flavored approach that provides direct access to common configurations:
344
394
 
345
395
  ```typescript
346
- // Create reusable configurations once
347
- const readonly = describe({ writable: false })
348
- const hidden = describe({ enumerable: false })
349
-
350
- // Apply to multiple classes with different properties
351
- @readonly('id', 'createdAt')
396
+ // Use flavors directly for cleaner syntax
397
+ @descriptor.readonly('id', 'createdAt')
398
+ @descriptor.hidden('internal')
352
399
  class User { }
353
400
 
401
+ // Or create reusable aliases
402
+ const readonly = descriptor.readonly
403
+ const hidden = descriptor.hidden
404
+
354
405
  @readonly('version', 'buildDate')
355
406
  class Package { }
356
407
  ```
357
408
 
358
409
  ### Common Decorator Patterns
359
410
 
360
- The functional approach makes it easy to create common decorator patterns:
411
+ The flavored approach makes it easy to create common decorator patterns:
361
412
 
362
413
  ```typescript
363
- import { describe } from './std-decorators'
414
+ import { descriptor } from './std-decorators'
364
415
 
365
- // Common reusable decorators
366
- export const readonly = describe({ writable: false })
367
- export const hidden = describe({ enumerable: false })
368
- export const locked = describe({ configurable: false })
369
- export const frozen = describe({ writable: false, configurable: false })
370
- export const private = describe({ enumerable: false, configurable: false })
416
+ // Common reusable decorators using flavors
417
+ export const readonly = descriptor.readonly
418
+ export const hidden = descriptor.hidden
419
+ export const locked = descriptor.frozen
420
+ export const frozen = (() => descriptor.readonly.frozen)()
421
+ export const private = (() => descriptor.hidden.frozen)()
371
422
 
372
423
  // Usage examples
373
424
  @readonly('id', 'createdAt')
@@ -389,7 +440,7 @@ The decorator merges the provided descriptor configuration with the existing pro
389
440
 
390
441
  ```typescript
391
442
  // Original property might have { enumerable: true, writable: true, configurable: true }
392
- // After @readonly('prop') where readonly = describe({ writable: false })
443
+ // After @descriptor.readonly('prop')
393
444
  // Final descriptor: { enumerable: true, writable: false, configurable: true }
394
445
  ```
395
446
 
@@ -399,9 +450,9 @@ The property descriptor configuration is applied in the constructor after callin
399
450
 
400
451
  ## Best Practices
401
452
 
402
- 1. **Create reusable configurations**: Define descriptor configurations once and reuse them
403
- 2. **Use descriptive names**: Name your descriptor configurations clearly (`readonly`, `hidden`, `locked`)
404
- 3. **Combine multiple decorators**: Stack multiple `describe` decorators for complex configurations
453
+ 1. **Use flavors directly**: Prefer `descriptor.readonly` over creating aliases for simple cases
454
+ 2. **Create reusable aliases**: For complex or frequently used combinations, create aliases
455
+ 3. **Combine multiple decorators**: Stack multiple flavored decorators for complex configurations
405
456
  4. **Use for encapsulation**: Hide internal properties from enumeration
406
457
  5. **Control immutability**: Make critical properties read-only
407
458
  6. **Prevent reconfiguration**: Lock important properties from being modified
@@ -551,6 +602,74 @@ deprecated.warn = (target, propertyKey, message?) => {
551
602
  4. **Document migration paths**: Provide clear upgrade instructions
552
603
  5. **Monitor usage**: Track which deprecated features are still being used
553
604
 
605
+ ## Debounce
606
+
607
+ The `debounce` decorator delays the execution of a method until a specified amount of time has passed since the last time it was called. This is useful for handling rapid events like keystrokes or window resizing.
608
+
609
+ ## API Reference
610
+
611
+ ### `@debounce(delay: number)`
612
+
613
+ A decorator that debounces a method.
614
+
615
+ **Parameters:**
616
+ - `delay`: The delay in milliseconds to wait before executing the method.
617
+
618
+ **Returns:** A method decorator.
619
+
620
+ ## Usage Example
621
+
622
+ ```typescript
623
+ import { debounce } from 'mutts/std-decorators'
624
+
625
+ class SearchComponent {
626
+ @debounce(300)
627
+ onSearch(query: string) {
628
+ // This will only run after 300ms of inactivity
629
+ console.log(`Searching for: ${query}`)
630
+ this.performApiCall(query)
631
+ }
632
+
633
+ performApiCall(query: string) {
634
+ // ...
635
+ }
636
+ }
637
+ ```
638
+
639
+ ## Throttle
640
+
641
+ The `throttle` decorator limits the execution of a method to at most once every specified amount of time. This is useful for rate-limiting expensive operations like scroll handlers or animations.
642
+
643
+ ## API Reference
644
+
645
+ ### `@throttle(delay: number)`
646
+
647
+ A decorator that throttles a method.
648
+
649
+ **Parameters:**
650
+ - `delay`: The time interval in milliseconds.
651
+
652
+ **Returns:** A method decorator.
653
+
654
+ ## Usage Example
655
+
656
+ ```typescript
657
+ import { throttle } from 'mutts/std-decorators'
658
+
659
+ class ScrollHandler {
660
+ @throttle(100)
661
+ onScroll(event: Event) {
662
+ // This will run at most once every 100ms
663
+ console.log('Scroll event processed')
664
+ this.updateUI()
665
+ }
666
+
667
+ updateUI() {
668
+ // ...
669
+ }
670
+ }
671
+ ```
672
+
554
673
  ## Related
555
674
 
556
675
  - [JavaScript Decorators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Decorators)
package/docs/zone.md CHANGED
@@ -21,20 +21,17 @@ console.log(myZone.active); // undefined
21
21
 
22
22
  ## Async Propagation
23
23
 
24
- By default, zones are lost when an async operation yields control (e.g., after `await`). To fix this, `mutts` provides `configureAsyncZone()`.
24
+ By default, zones are lost when an async operation yields control (e.g., after `await`). To fix this, register zones in `asyncZone` the global `ZoneAggregator` — which automatically preserves context across async boundaries via `asyncHooks`.
25
25
 
26
26
  ```typescript
27
- import { configureAsyncZone, asyncZone, Zone } from 'mutts/zone';
27
+ import { asyncZone, Zone } from 'mutts/zone';
28
28
 
29
29
  const requestId = new Zone<string>();
30
30
 
31
31
  // 1. Tell the global aggregator to track this zone
32
32
  asyncZone.add(requestId);
33
33
 
34
- // 2. Patch global async primitives (once per app)
35
- configureAsyncZone();
36
-
37
- // 3. Usage
34
+ // 2. Usage
38
35
  requestId.with("req-123", async () => {
39
36
  await somePromise();
40
37
  // Context is automatically preserved across await!
@@ -42,6 +39,12 @@ requestId.with("req-123", async () => {
42
39
  });
43
40
  ```
44
41
 
42
+ > [!WARNING]
43
+ > **Browser Limitations**: In browser environments (where `AsyncLocalStorage` is unavailable), `mutts` relies on monkey-patching global async primitives (Promise, setTimeout, etc.) to propagate zones. This is generally less robust than Node.js's `async_hooks` and may fail to track context across:
44
+ > * Native `async/await` boundaries in some modern browsers if not transpiled.
45
+ > * Concurrent modifications to global prototypes by other libraries.
46
+
47
+
45
48
  ## Core API
46
49
 
47
50
  ### `AZone<T>` (Abstract)
package/package.json CHANGED
@@ -1,50 +1,61 @@
1
1
  {
2
2
  "name": "mutts",
3
3
  "description": "Modern UTility TS: A collection of TypeScript utilities",
4
- "version": "1.0.7",
5
- "main": "dist/browser.js",
4
+ "version": "1.0.9",
5
+ "main": "dist/browser.cjs",
6
6
  "module": "dist/browser.esm.js",
7
7
  "types": "dist/browser.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
+ "test-node": {
11
+ "import": "./src/entry-node.ts"
12
+ },
13
+ "test-browser": {
14
+ "import": "./src/entry-browser.ts"
15
+ },
10
16
  "node": {
11
17
  "types": "./dist/node.d.ts",
12
18
  "import": "./dist/node.esm.js",
13
- "require": "./dist/node.js"
19
+ "require": "./dist/node.cjs"
14
20
  },
15
21
  "default": {
16
22
  "types": "./dist/browser.d.ts",
17
23
  "import": "./dist/browser.esm.js",
18
- "require": "./dist/browser.js"
24
+ "require": "./dist/browser.cjs"
19
25
  }
20
26
  },
21
27
  "./browser": {
22
28
  "types": "./dist/browser.d.ts",
23
29
  "import": "./dist/browser.esm.js",
24
- "require": "./dist/browser.js"
30
+ "require": "./dist/browser.cjs"
25
31
  },
26
32
  "./node": {
27
33
  "types": "./dist/node.d.ts",
28
34
  "import": "./dist/node.esm.js",
29
- "require": "./dist/node.js"
35
+ "require": "./dist/node.cjs"
30
36
  },
31
37
  "./src": {
32
38
  "node": {
33
- "types": "./src/async/node.ts",
34
- "import": "./src/async/node.ts"
39
+ "types": "./src/entry-node.ts",
40
+ "import": "./src/entry-node.ts"
35
41
  },
36
42
  "default": {
37
- "types": "./src/async/browser.ts",
38
- "import": "./src/async/browser.ts"
43
+ "types": "./src/entry-browser.ts",
44
+ "import": "./src/entry-browser.ts"
39
45
  }
40
46
  },
41
47
  "./src/browser": {
42
- "types": "./src/async/browser.ts",
43
- "import": "./src/async/browser.ts"
48
+ "types": "./src/entry-browser.ts",
49
+ "import": "./src/entry-browser.ts"
44
50
  },
45
51
  "./src/node": {
46
- "types": "./src/async/node.ts",
47
- "import": "./src/async/node.ts"
52
+ "types": "./src/entry-node.ts",
53
+ "import": "./src/entry-node.ts"
54
+ },
55
+ "./debug": {
56
+ "types": "./dist/debug.d.ts",
57
+ "import": "./dist/debug.esm.js",
58
+ "require": "./dist/debug.cjs"
48
59
  }
49
60
  },
50
61
  "files": [
@@ -59,14 +70,20 @@
59
70
  "build": "npm run build:js && npm run build:devtools",
60
71
  "build:watch": "rollup -c --watch",
61
72
  "prepublishOnly": "npm run build",
62
- "test": "NODE_OPTIONS=--expose-gc jest",
63
- "test:coverage": "NODE_OPTIONS=--expose-gc jest --coverage",
64
- "test:coverage:watch": "NODE_OPTIONS=--expose-gc jest --coverage --watch",
65
- "test:legacy": "TSCONFIG=tsconfig.legacy.json jest --detectOpenHandles --testPathPatterns=decorator",
66
- "test:modern": "TSCONFIG=tsconfig.modern.json jest --detectOpenHandles --testPathPatterns=decorator",
67
- "test:profile": "RUN_PROFILING=1 NODE_OPTIONS=--expose-gc jest --testPathPatterns=profiling",
68
- "test:profile:benchmark": "RUN_PROFILING=1 NODE_OPTIONS=--expose-gc jest --testPathPatterns=profiling --testNamePattern=benchmark",
69
- "test:profile:detailed": "RUN_PROFILING=1 node --prof node_modules/jest/bin/jest.js --testPathPatterns=profiling --no-coverage",
73
+ "test": "npm run test:node && npm run test:browser",
74
+ "test:node": "TEST_ENV=node NODE_OPTIONS='--expose-gc' vitest run",
75
+ "test:browser": "TEST_ENV=browser vitest run --browser",
76
+ "test:zone:node": "TEST_ENV=node vitest run tests/zone.test.ts",
77
+ "test:zone:browser": "TEST_ENV=browser vitest run tests/zone.test.ts",
78
+ "test:async:node": "TEST_ENV=node vitest run tests/async-hook.test.ts",
79
+ "test:async:browser": "TEST_ENV=browser vitest run tests/async-hook.test.ts",
80
+ "test:coverage": "TEST_ENV=node vitest run --coverage",
81
+ "test:coverage:watch": "TEST_ENV=node vitest run --coverage --watch",
82
+ "test:legacy": "TEST_ENV=node TSCONFIG=tsconfig.legacy.json vitest run --detectOpenHandles",
83
+ "test:modern": "TEST_ENV=node TSCONFIG=tsconfig.modern.json vitest run --detectOpenHandles",
84
+ "test:profile": "RUN_PROFILING=1 NODE_OPTIONS=--expose-gc vitest run",
85
+ "test:profile:benchmark": "RUN_PROFILING=1 NODE_OPTIONS=--expose-gc vitest run -t benchmark",
86
+ "test:profile:detailed": "RUN_PROFILING=1 node --prof node_modules/vitest/vitest.mjs --no-coverage",
70
87
  "benchmark:save": "tsx tests/profiling/benchmark.ts save",
71
88
  "benchmark:compare": "tsx tests/profiling/benchmark.ts compare",
72
89
  "benchmark:list": "tsx tests/profiling/benchmark.ts list",
@@ -102,25 +119,25 @@
102
119
  },
103
120
  "devDependencies": {
104
121
  "@biomejs/biome": "^2.0.6",
105
- "@jest/globals": "^30.2.0",
106
122
  "@rollup/plugin-commonjs": "^28.0.6",
107
123
  "@rollup/plugin-json": "^6.1.0",
108
124
  "@rollup/plugin-node-resolve": "^16.0.1",
109
125
  "@rollup/plugin-terser": "^0.4.4",
110
126
  "@rollup/plugin-typescript": "^12.1.4",
111
- "@types/jest": "^30.0.0",
112
127
  "@types/node": "^22.10.10",
113
- "jest": "^30.0.4",
128
+ "@vitest/browser": "^4.0.18",
129
+ "@vitest/browser-playwright": "^4.0.18",
130
+ "playwright": "^1.58.1",
114
131
  "rollup": "^4.52.2",
115
132
  "rollup-plugin-copy": "^3.5.0",
116
133
  "rollup-plugin-dts": "^6.2.3",
117
134
  "rollup-plugin-typescript2": "^0.36.0",
118
- "ts-jest": "^29.4.0",
119
135
  "ts-node": "^10.9.2",
120
136
  "tslib": "^2.8.1",
121
137
  "tsx": "^4.20.4",
122
138
  "typescript": "^5.8.3",
123
- "vis-network": "^9.1.9"
139
+ "vis-network": "^9.1.9",
140
+ "vitest": "^4.0.18"
124
141
  },
125
142
  "packageManager": "pnpm@10.7.1+sha512.2d92c86b7928dc8284f53494fb4201f983da65f0fb4f0d40baafa5cf628fa31dae3e5968f12466f17df7e97310e30f343a648baea1b9b350685dafafffdf5808"
126
143
  }