mutts 1.0.3 → 1.0.5

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 (40) hide show
  1. package/README.md +1 -1
  2. package/dist/chunks/{index-HNVqPzjz.js → index-Cvxdw6Ax.js} +230 -61
  3. package/dist/chunks/index-Cvxdw6Ax.js.map +1 -0
  4. package/dist/chunks/{index-DzUDtFc7.esm.js → index-qiWwozOc.esm.js} +228 -62
  5. package/dist/chunks/index-qiWwozOc.esm.js.map +1 -0
  6. package/dist/destroyable.esm.js.map +1 -1
  7. package/dist/destroyable.js.map +1 -1
  8. package/dist/index.esm.js +1 -1
  9. package/dist/index.js +4 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/mutts.umd.js +1 -1
  12. package/dist/mutts.umd.js.map +1 -1
  13. package/dist/mutts.umd.min.js +1 -1
  14. package/dist/mutts.umd.min.js.map +1 -1
  15. package/dist/reactive.d.ts +30 -1
  16. package/dist/reactive.esm.js +1 -1
  17. package/dist/reactive.js +4 -1
  18. package/dist/reactive.js.map +1 -1
  19. package/dist/std-decorators.esm.js.map +1 -1
  20. package/dist/std-decorators.js.map +1 -1
  21. package/docs/reactive/core.md +16 -16
  22. package/docs/reactive.md +7 -0
  23. package/package.json +1 -1
  24. package/src/destroyable.ts +2 -2
  25. package/src/reactive/array.ts +3 -5
  26. package/src/reactive/change.ts +6 -2
  27. package/src/reactive/effects.ts +132 -51
  28. package/src/reactive/index.ts +3 -1
  29. package/src/reactive/interface.ts +1 -1
  30. package/src/reactive/map.ts +6 -6
  31. package/src/reactive/mapped.ts +2 -3
  32. package/src/reactive/project.ts +103 -6
  33. package/src/reactive/proxy.ts +5 -1
  34. package/src/reactive/set.ts +6 -6
  35. package/src/reactive/types.ts +22 -0
  36. package/src/reactive/zone.ts +1 -1
  37. package/src/std-decorators.ts +1 -1
  38. package/dist/chunks/index-DzUDtFc7.esm.js.map +0 -1
  39. package/dist/chunks/index-HNVqPzjz.js.map +0 -1
  40. /package/{src/reactive/project.project.md → docs/reactive/project.md} +0 -0
@@ -107,6 +107,16 @@ type State = {
107
107
  evolution: Evolution;
108
108
  next: State;
109
109
  } | {};
110
+ /**
111
+ * Context for a running projection item effect
112
+ */
113
+ interface ProjectionContext {
114
+ source: any;
115
+ key?: any;
116
+ target: any;
117
+ depth: number;
118
+ parent?: ProjectionContext;
119
+ }
110
120
  /**
111
121
  * Structured error codes for machine-readable diagnosis
112
122
  */
@@ -196,6 +206,12 @@ declare const options: {
196
206
  * @default 100
197
207
  */
198
208
  maxEffectChain: number;
209
+ /**
210
+ * Maximum number of times an effect can be triggered by the same cause in a single batch
211
+ * Used to detect aggressive re-computation or infinite loops
212
+ * @default 10
213
+ */
214
+ maxTriggerPerBatch: number;
199
215
  /**
200
216
  * Debug purpose: maximum effect reaction (like call stack max depth)
201
217
  * Used to prevent infinite loops
@@ -406,6 +422,14 @@ declare function getActiveEffect(): ScopedCallback;
406
422
 
407
423
  type EffectTracking = (obj: any, evolution: Evolution, prop: any) => void;
408
424
 
425
+ interface ActivationRecord {
426
+ effect: ScopedCallback;
427
+ obj: any;
428
+ evolution: Evolution;
429
+ prop: any;
430
+ batchId: number;
431
+ }
432
+ declare function getActivationLog(): Omit<ActivationRecord, "batchId">[];
409
433
  /**
410
434
  * Registers a debug callback that is called when the current effect is triggered by a dependency change
411
435
  *
@@ -470,6 +494,7 @@ declare function addBatchCleanup(cleanup: ScopedCallback): void;
470
494
  * ```
471
495
  */
472
496
  declare const defer: typeof addBatchCleanup;
497
+ declare function batch(effect: ScopedCallback | ScopedCallback[], immediate?: 'immediate'): any;
473
498
  /**
474
499
  * Decorator that makes methods atomic - batches all effects triggered within the method
475
500
  */
@@ -673,6 +698,10 @@ type Register<T, K extends PropertyKey = PropertyKey> = RegisterClass<T, K> & T[
673
698
  declare const Register: new <T, K extends PropertyKey = PropertyKey>(keyFn: KeyFunction<T, K>, initial?: Iterable<T>) => Register<T, K>;
674
699
  declare function register<T, K extends PropertyKey = PropertyKey>(keyFn: KeyFunction<T, K>, initial?: Iterable<T>): Register<T, K>;
675
700
 
701
+ /**
702
+ * Returns the projection context of the currently running effect, if any.
703
+ */
704
+ declare function getActiveProjection(): ProjectionContext | undefined;
676
705
  type ProjectOldValue<Target> = Target extends readonly (infer Item)[] ? Item : Target extends Map<any, infer Item> ? Item : Target extends Record<PropertyKey, infer Item> ? Item : unknown;
677
706
  type ProjectAccess<SourceValue, Key, SourceType, Target> = {
678
707
  readonly key: Key;
@@ -852,5 +881,5 @@ declare function isZoneEnabled(): boolean;
852
881
  */
853
882
  declare const profileInfo: any;
854
883
 
855
- export { ReactiveBase, ReactiveError, ReadOnlyError, Register, addBatchCleanup, atomic, biDi, buildReactivityGraph, cleanedBy, cleanup, deepWatch, defer, derived, effect, enableDevTools, getActiveEffect, getState, immutables, isDevtoolsEnabled, isNonReactive, isReactive, isZoneEnabled, mapped, memoize, organize, organized, profileInfo, project, reactive, options as reactiveOptions, reduced, register, registerEffectForDebug, registerNativeReactivity, registerObjectForDebug, root, setEffectName, setObjectName, setZoneEnabled, touched, touched1, trackEffect, unreactive, untracked, unwrap, watch };
884
+ export { ReactiveBase, ReactiveError, ReadOnlyError, Register, addBatchCleanup, atomic, batch, biDi, buildReactivityGraph, cleanedBy, cleanup, deepWatch, defer, derived, effect, enableDevTools, getActivationLog, getActiveEffect, getActiveProjection, getState, immutables, isDevtoolsEnabled, isNonReactive, isReactive, isZoneEnabled, mapped, memoize, organize, organized, profileInfo, project, reactive, options as reactiveOptions, reduced, register, registerEffectForDebug, registerNativeReactivity, registerObjectForDebug, root, setEffectName, setObjectName, setZoneEnabled, touched, touched1, trackEffect, unreactive, untracked, unwrap, watch };
856
885
  export type { DependencyAccess, DependencyFunction, Evolution, Memoizable, ReactivityGraph, ScopedCallback };
@@ -1,4 +1,4 @@
1
- export { L as ReactiveBase, U as ReactiveError, R as ReadOnlyError, Q as Register, j as addBatchCleanup, k as atomic, l as biDi, c as buildReactivityGraph, x as cleanedBy, y as cleanup, h as deepWatch, n as defer, z as derived, o as effect, e as enableDevTools, q as getActiveEffect, g as getState, F as immutables, i as isDevtoolsEnabled, G as isNonReactive, K as isReactive, V as isZoneEnabled, C as mapped, E as memoize, O as organize, P as organized, p as profileInfo, J as project, M as reactive, T as reactiveOptions, D as reduced, S as register, r as registerEffectForDebug, H as registerNativeReactivity, d as registerObjectForDebug, u as root, s as setEffectName, f as setObjectName, W as setZoneEnabled, t as touched, b as touched1, v as trackEffect, A as unreactive, w as untracked, N as unwrap, B as watch } from './chunks/index-DzUDtFc7.esm.js';
1
+ export { O as ReactiveBase, X as ReactiveError, R as ReadOnlyError, U as Register, j as addBatchCleanup, k as atomic, l as batch, n as biDi, c as buildReactivityGraph, z as cleanedBy, A as cleanup, h as deepWatch, o as defer, B as derived, q as effect, e as enableDevTools, u as getActivationLog, v as getActiveEffect, L as getActiveProjection, g as getState, H as immutables, i as isDevtoolsEnabled, J as isNonReactive, N as isReactive, Y as isZoneEnabled, E as mapped, G as memoize, S as organize, T as organized, p as profileInfo, M as project, P as reactive, W as reactiveOptions, F as reduced, V as register, r as registerEffectForDebug, K as registerNativeReactivity, d as registerObjectForDebug, w as root, s as setEffectName, f as setObjectName, Z as setZoneEnabled, t as touched, b as touched1, x as trackEffect, C as unreactive, y as untracked, Q as unwrap, D as watch } from './chunks/index-qiWwozOc.esm.js';
2
2
  import './chunks/decorator-DqiszP7i.esm.js';
3
3
  import './indexable.esm.js';
4
4
  import './chunks/_tslib-Mzh1rNsX.esm.js';
package/dist/reactive.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var reactive = require('./chunks/index-HNVqPzjz.js');
3
+ var reactive = require('./chunks/index-Cvxdw6Ax.js');
4
4
  require('./chunks/decorator-DLvrD0UF.js');
5
5
  require('./indexable.js');
6
6
  require('./chunks/_tslib-BgjropY9.js');
@@ -13,6 +13,7 @@ exports.ReadOnlyError = reactive.ReadOnlyError;
13
13
  exports.Register = reactive.Register;
14
14
  exports.addBatchCleanup = reactive.addBatchCleanup;
15
15
  exports.atomic = reactive.atomic;
16
+ exports.batch = reactive.batch;
16
17
  exports.biDi = reactive.biDi;
17
18
  exports.buildReactivityGraph = reactive.buildReactivityGraph;
18
19
  exports.cleanedBy = reactive.cleanedBy;
@@ -22,7 +23,9 @@ exports.defer = reactive.defer;
22
23
  exports.derived = reactive.derived;
23
24
  exports.effect = reactive.effect;
24
25
  exports.enableDevTools = reactive.enableDevTools;
26
+ exports.getActivationLog = reactive.getActivationLog;
25
27
  exports.getActiveEffect = reactive.getActiveEffect;
28
+ exports.getActiveProjection = reactive.getActiveProjection;
26
29
  exports.getState = reactive.getState;
27
30
  exports.immutables = reactive.immutables;
28
31
  exports.isDevtoolsEnabled = reactive.isDevtoolsEnabled;
@@ -1 +1 @@
1
- {"version":3,"file":"reactive.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"reactive.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"std-decorators.esm.js","sources":["../src/std-decorators.ts"],"sourcesContent":["import { decorator, GenericClassDecorator } from './decorator'\n\n// In order to avoid async re-entrance, we could use zone.js or something like that.\nconst syncCalculating: { object: object; prop: PropertyKey }[] = []\n/**\n * Decorator that caches the result of a getter method and only recomputes when dependencies change\n * Prevents circular dependencies and provides automatic cache invalidation\n */\nexport const cached = decorator({\n\tgetter(original, propertyKey) {\n\t\treturn function (this: any) {\n\t\t\tconst alreadyCalculating = syncCalculating.findIndex(\n\t\t\t\t(c) => c.object === this && c.prop === propertyKey\n\t\t\t)\n\t\t\tif (alreadyCalculating > -1)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Circular dependency detected: ${syncCalculating\n\t\t\t\t\t\t.slice(alreadyCalculating)\n\t\t\t\t\t\t.map((c) => `${c.object.constructor.name}.${String(c.prop)}`)\n\t\t\t\t\t\t.join(' -> ')} -> again`\n\t\t\t\t)\n\t\t\tsyncCalculating.push({ object: this, prop: propertyKey })\n\t\t\ttry {\n\t\t\t\tconst rv = original.call(this)\n\t\t\t\tcache(this, propertyKey, rv)\n\t\t\t\treturn rv\n\t\t\t} finally {\n\t\t\t\tsyncCalculating.pop()\n\t\t\t}\n\t\t}\n\t},\n})\n\n/**\n * Checks if a property is cached (has a cached value)\n * @param object - The object to check\n * @param propertyKey - The property key to check\n * @returns True if the property has a cached value\n */\nexport function isCached(object: Object, propertyKey: PropertyKey) {\n\treturn !!Object.getOwnPropertyDescriptor(object, propertyKey)\n}\n\n/**\n * Caches a value for a property on an object\n * @param object - The object to cache the value on\n * @param propertyKey - The property key to cache\n * @param value - The value to cache\n */\nexport function cache(object: Object, propertyKey: PropertyKey, value: any) {\n\tObject.defineProperty(object, propertyKey, { value })\n}\n\n/**\n * Creates a decorator that modifies property descriptors for specified properties\n * @param descriptor - The descriptor properties to apply\n * @returns A class decorator that applies the descriptor to specified properties\n */\nexport function describe(descriptor: {\n\tenumerable?: boolean\n\tconfigurable?: boolean // Not modifiable once the property has been defined ?\n\twritable?: boolean\n}) {\n\treturn <T>(...properties: (keyof T)[]): GenericClassDecorator<T> =>\n\t\t(Base) => {\n\t\t\treturn class extends Base {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tfor (const key of properties) {\n\t\t\t\t\t\tObject.defineProperty(this, key, {\n\t\t\t\t\t\t\t...Object.getOwnPropertyDescriptor(this, key),\n\t\t\t\t\t\t\t...descriptor,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n}\n\n/**\n * Decorator that marks methods, properties, or classes as deprecated\n * Provides warning messages when deprecated items are used\n */\nexport const deprecated = Object.assign(\n\tdecorator({\n\t\tmethod(original, propertyKey) {\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.apply(this, args)\n\t\t\t}\n\t\t},\n\t\tgetter(original, propertyKey) {\n\t\t\treturn function (this: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this)\n\t\t\t}\n\t\t},\n\t\tsetter(original, propertyKey) {\n\t\t\treturn function (this: any, value: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this, value)\n\t\t\t}\n\t\t},\n\t\tclass(original) {\n\t\t\treturn class extends original {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tdeprecated.warn(this, 'constructor')\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdefault(message: string) {\n\t\t\treturn decorator({\n\t\t\t\tmethod(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, value: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this, value)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tclass(original) {\n\t\t\t\t\treturn class extends original {\n\t\t\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\t\t\tsuper(...args)\n\t\t\t\t\t\t\tdeprecated.warn(this, 'constructor', message)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\t\t},\n\t}),\n\t{\n\t\twarn: (target: any, propertyKey: PropertyKey, message?: string) => {\n\t\t\t// biome-ignore lint/suspicious/noConsole: To be overridden\n\t\t\tconsole.warn(\n\t\t\t\t`${target.constructor.name}.${String(propertyKey)} is deprecated${message ? `: ${message}` : ''}`\n\t\t\t)\n\t\t},\n\t}\n)\n\n/**\n * Creates a debounced method decorator that delays execution until after the delay period has passed\n * @param delay - The delay in milliseconds\n * @returns A method decorator that debounces method calls\n */\nexport function debounce(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t// Clear existing timeout\n\t\t\t\tif (timeoutId) {\n\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t}\n\n\t\t\t\t// Set new timeout\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\toriginal.apply(this, args)\n\t\t\t\t\ttimeoutId = null\n\t\t\t\t}, delay)\n\t\t\t}\n\t\t},\n\t})\n}\n\n/**\n * Creates a throttled method decorator that limits execution to once per delay period\n * @param delay - The delay in milliseconds\n * @returns A method decorator that throttles method calls\n */\nexport function throttle(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet lastCallTime = 0\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tconst now = Date.now()\n\n\t\t\t\t// If enough time has passed since last call, execute immediately\n\t\t\t\tif (now - lastCallTime >= delay) {\n\t\t\t\t\t// Clear any pending timeout since we're executing now\n\t\t\t\t\tif (timeoutId) {\n\t\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}\n\t\t\t\t\tlastCallTime = now\n\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t}\n\n\t\t\t\t// Otherwise, schedule execution for when the delay period ends\n\t\t\t\tif (!timeoutId) {\n\t\t\t\t\tconst remainingTime = delay - (now - lastCallTime)\n\t\t\t\t\tconst scheduledArgs = [...args] // Capture args at scheduling time\n\t\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\t\tlastCallTime = Date.now()\n\t\t\t\t\t\toriginal.apply(this, scheduledArgs)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}, remainingTime)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t})\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"std-decorators.esm.js","sources":["../src/std-decorators.ts"],"sourcesContent":["import { decorator, type GenericClassDecorator } from './decorator'\n\n// In order to avoid async re-entrance, we could use zone.js or something like that.\nconst syncCalculating: { object: object; prop: PropertyKey }[] = []\n/**\n * Decorator that caches the result of a getter method and only recomputes when dependencies change\n * Prevents circular dependencies and provides automatic cache invalidation\n */\nexport const cached = decorator({\n\tgetter(original, propertyKey) {\n\t\treturn function (this: any) {\n\t\t\tconst alreadyCalculating = syncCalculating.findIndex(\n\t\t\t\t(c) => c.object === this && c.prop === propertyKey\n\t\t\t)\n\t\t\tif (alreadyCalculating > -1)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Circular dependency detected: ${syncCalculating\n\t\t\t\t\t\t.slice(alreadyCalculating)\n\t\t\t\t\t\t.map((c) => `${c.object.constructor.name}.${String(c.prop)}`)\n\t\t\t\t\t\t.join(' -> ')} -> again`\n\t\t\t\t)\n\t\t\tsyncCalculating.push({ object: this, prop: propertyKey })\n\t\t\ttry {\n\t\t\t\tconst rv = original.call(this)\n\t\t\t\tcache(this, propertyKey, rv)\n\t\t\t\treturn rv\n\t\t\t} finally {\n\t\t\t\tsyncCalculating.pop()\n\t\t\t}\n\t\t}\n\t},\n})\n\n/**\n * Checks if a property is cached (has a cached value)\n * @param object - The object to check\n * @param propertyKey - The property key to check\n * @returns True if the property has a cached value\n */\nexport function isCached(object: Object, propertyKey: PropertyKey) {\n\treturn !!Object.getOwnPropertyDescriptor(object, propertyKey)\n}\n\n/**\n * Caches a value for a property on an object\n * @param object - The object to cache the value on\n * @param propertyKey - The property key to cache\n * @param value - The value to cache\n */\nexport function cache(object: Object, propertyKey: PropertyKey, value: any) {\n\tObject.defineProperty(object, propertyKey, { value })\n}\n\n/**\n * Creates a decorator that modifies property descriptors for specified properties\n * @param descriptor - The descriptor properties to apply\n * @returns A class decorator that applies the descriptor to specified properties\n */\nexport function describe(descriptor: {\n\tenumerable?: boolean\n\tconfigurable?: boolean // Not modifiable once the property has been defined ?\n\twritable?: boolean\n}) {\n\treturn <T>(...properties: (keyof T)[]): GenericClassDecorator<T> =>\n\t\t(Base) => {\n\t\t\treturn class extends Base {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tfor (const key of properties) {\n\t\t\t\t\t\tObject.defineProperty(this, key, {\n\t\t\t\t\t\t\t...Object.getOwnPropertyDescriptor(this, key),\n\t\t\t\t\t\t\t...descriptor,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n}\n\n/**\n * Decorator that marks methods, properties, or classes as deprecated\n * Provides warning messages when deprecated items are used\n */\nexport const deprecated = Object.assign(\n\tdecorator({\n\t\tmethod(original, propertyKey) {\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.apply(this, args)\n\t\t\t}\n\t\t},\n\t\tgetter(original, propertyKey) {\n\t\t\treturn function (this: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this)\n\t\t\t}\n\t\t},\n\t\tsetter(original, propertyKey) {\n\t\t\treturn function (this: any, value: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this, value)\n\t\t\t}\n\t\t},\n\t\tclass(original) {\n\t\t\treturn class extends original {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tdeprecated.warn(this, 'constructor')\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdefault(message: string) {\n\t\t\treturn decorator({\n\t\t\t\tmethod(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, value: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this, value)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tclass(original) {\n\t\t\t\t\treturn class extends original {\n\t\t\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\t\t\tsuper(...args)\n\t\t\t\t\t\t\tdeprecated.warn(this, 'constructor', message)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\t\t},\n\t}),\n\t{\n\t\twarn: (target: any, propertyKey: PropertyKey, message?: string) => {\n\t\t\t// biome-ignore lint/suspicious/noConsole: To be overridden\n\t\t\tconsole.warn(\n\t\t\t\t`${target.constructor.name}.${String(propertyKey)} is deprecated${message ? `: ${message}` : ''}`\n\t\t\t)\n\t\t},\n\t}\n)\n\n/**\n * Creates a debounced method decorator that delays execution until after the delay period has passed\n * @param delay - The delay in milliseconds\n * @returns A method decorator that debounces method calls\n */\nexport function debounce(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t// Clear existing timeout\n\t\t\t\tif (timeoutId) {\n\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t}\n\n\t\t\t\t// Set new timeout\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\toriginal.apply(this, args)\n\t\t\t\t\ttimeoutId = null\n\t\t\t\t}, delay)\n\t\t\t}\n\t\t},\n\t})\n}\n\n/**\n * Creates a throttled method decorator that limits execution to once per delay period\n * @param delay - The delay in milliseconds\n * @returns A method decorator that throttles method calls\n */\nexport function throttle(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet lastCallTime = 0\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tconst now = Date.now()\n\n\t\t\t\t// If enough time has passed since last call, execute immediately\n\t\t\t\tif (now - lastCallTime >= delay) {\n\t\t\t\t\t// Clear any pending timeout since we're executing now\n\t\t\t\t\tif (timeoutId) {\n\t\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}\n\t\t\t\t\tlastCallTime = now\n\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t}\n\n\t\t\t\t// Otherwise, schedule execution for when the delay period ends\n\t\t\t\tif (!timeoutId) {\n\t\t\t\t\tconst remainingTime = delay - (now - lastCallTime)\n\t\t\t\t\tconst scheduledArgs = [...args] // Capture args at scheduling time\n\t\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\t\tlastCallTime = Date.now()\n\t\t\t\t\t\toriginal.apply(this, scheduledArgs)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}, remainingTime)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t})\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"std-decorators.js","sources":["../src/std-decorators.ts"],"sourcesContent":["import { decorator, GenericClassDecorator } from './decorator'\n\n// In order to avoid async re-entrance, we could use zone.js or something like that.\nconst syncCalculating: { object: object; prop: PropertyKey }[] = []\n/**\n * Decorator that caches the result of a getter method and only recomputes when dependencies change\n * Prevents circular dependencies and provides automatic cache invalidation\n */\nexport const cached = decorator({\n\tgetter(original, propertyKey) {\n\t\treturn function (this: any) {\n\t\t\tconst alreadyCalculating = syncCalculating.findIndex(\n\t\t\t\t(c) => c.object === this && c.prop === propertyKey\n\t\t\t)\n\t\t\tif (alreadyCalculating > -1)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Circular dependency detected: ${syncCalculating\n\t\t\t\t\t\t.slice(alreadyCalculating)\n\t\t\t\t\t\t.map((c) => `${c.object.constructor.name}.${String(c.prop)}`)\n\t\t\t\t\t\t.join(' -> ')} -> again`\n\t\t\t\t)\n\t\t\tsyncCalculating.push({ object: this, prop: propertyKey })\n\t\t\ttry {\n\t\t\t\tconst rv = original.call(this)\n\t\t\t\tcache(this, propertyKey, rv)\n\t\t\t\treturn rv\n\t\t\t} finally {\n\t\t\t\tsyncCalculating.pop()\n\t\t\t}\n\t\t}\n\t},\n})\n\n/**\n * Checks if a property is cached (has a cached value)\n * @param object - The object to check\n * @param propertyKey - The property key to check\n * @returns True if the property has a cached value\n */\nexport function isCached(object: Object, propertyKey: PropertyKey) {\n\treturn !!Object.getOwnPropertyDescriptor(object, propertyKey)\n}\n\n/**\n * Caches a value for a property on an object\n * @param object - The object to cache the value on\n * @param propertyKey - The property key to cache\n * @param value - The value to cache\n */\nexport function cache(object: Object, propertyKey: PropertyKey, value: any) {\n\tObject.defineProperty(object, propertyKey, { value })\n}\n\n/**\n * Creates a decorator that modifies property descriptors for specified properties\n * @param descriptor - The descriptor properties to apply\n * @returns A class decorator that applies the descriptor to specified properties\n */\nexport function describe(descriptor: {\n\tenumerable?: boolean\n\tconfigurable?: boolean // Not modifiable once the property has been defined ?\n\twritable?: boolean\n}) {\n\treturn <T>(...properties: (keyof T)[]): GenericClassDecorator<T> =>\n\t\t(Base) => {\n\t\t\treturn class extends Base {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tfor (const key of properties) {\n\t\t\t\t\t\tObject.defineProperty(this, key, {\n\t\t\t\t\t\t\t...Object.getOwnPropertyDescriptor(this, key),\n\t\t\t\t\t\t\t...descriptor,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n}\n\n/**\n * Decorator that marks methods, properties, or classes as deprecated\n * Provides warning messages when deprecated items are used\n */\nexport const deprecated = Object.assign(\n\tdecorator({\n\t\tmethod(original, propertyKey) {\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.apply(this, args)\n\t\t\t}\n\t\t},\n\t\tgetter(original, propertyKey) {\n\t\t\treturn function (this: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this)\n\t\t\t}\n\t\t},\n\t\tsetter(original, propertyKey) {\n\t\t\treturn function (this: any, value: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this, value)\n\t\t\t}\n\t\t},\n\t\tclass(original) {\n\t\t\treturn class extends original {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tdeprecated.warn(this, 'constructor')\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdefault(message: string) {\n\t\t\treturn decorator({\n\t\t\t\tmethod(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, value: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this, value)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tclass(original) {\n\t\t\t\t\treturn class extends original {\n\t\t\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\t\t\tsuper(...args)\n\t\t\t\t\t\t\tdeprecated.warn(this, 'constructor', message)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\t\t},\n\t}),\n\t{\n\t\twarn: (target: any, propertyKey: PropertyKey, message?: string) => {\n\t\t\t// biome-ignore lint/suspicious/noConsole: To be overridden\n\t\t\tconsole.warn(\n\t\t\t\t`${target.constructor.name}.${String(propertyKey)} is deprecated${message ? `: ${message}` : ''}`\n\t\t\t)\n\t\t},\n\t}\n)\n\n/**\n * Creates a debounced method decorator that delays execution until after the delay period has passed\n * @param delay - The delay in milliseconds\n * @returns A method decorator that debounces method calls\n */\nexport function debounce(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t// Clear existing timeout\n\t\t\t\tif (timeoutId) {\n\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t}\n\n\t\t\t\t// Set new timeout\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\toriginal.apply(this, args)\n\t\t\t\t\ttimeoutId = null\n\t\t\t\t}, delay)\n\t\t\t}\n\t\t},\n\t})\n}\n\n/**\n * Creates a throttled method decorator that limits execution to once per delay period\n * @param delay - The delay in milliseconds\n * @returns A method decorator that throttles method calls\n */\nexport function throttle(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet lastCallTime = 0\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tconst now = Date.now()\n\n\t\t\t\t// If enough time has passed since last call, execute immediately\n\t\t\t\tif (now - lastCallTime >= delay) {\n\t\t\t\t\t// Clear any pending timeout since we're executing now\n\t\t\t\t\tif (timeoutId) {\n\t\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}\n\t\t\t\t\tlastCallTime = now\n\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t}\n\n\t\t\t\t// Otherwise, schedule execution for when the delay period ends\n\t\t\t\tif (!timeoutId) {\n\t\t\t\t\tconst remainingTime = delay - (now - lastCallTime)\n\t\t\t\t\tconst scheduledArgs = [...args] // Capture args at scheduling time\n\t\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\t\tlastCallTime = Date.now()\n\t\t\t\t\t\toriginal.apply(this, scheduledArgs)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}, remainingTime)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t})\n}\n"],"names":["decorator"],"mappings":"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"}
1
+ {"version":3,"file":"std-decorators.js","sources":["../src/std-decorators.ts"],"sourcesContent":["import { decorator, type GenericClassDecorator } from './decorator'\n\n// In order to avoid async re-entrance, we could use zone.js or something like that.\nconst syncCalculating: { object: object; prop: PropertyKey }[] = []\n/**\n * Decorator that caches the result of a getter method and only recomputes when dependencies change\n * Prevents circular dependencies and provides automatic cache invalidation\n */\nexport const cached = decorator({\n\tgetter(original, propertyKey) {\n\t\treturn function (this: any) {\n\t\t\tconst alreadyCalculating = syncCalculating.findIndex(\n\t\t\t\t(c) => c.object === this && c.prop === propertyKey\n\t\t\t)\n\t\t\tif (alreadyCalculating > -1)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Circular dependency detected: ${syncCalculating\n\t\t\t\t\t\t.slice(alreadyCalculating)\n\t\t\t\t\t\t.map((c) => `${c.object.constructor.name}.${String(c.prop)}`)\n\t\t\t\t\t\t.join(' -> ')} -> again`\n\t\t\t\t)\n\t\t\tsyncCalculating.push({ object: this, prop: propertyKey })\n\t\t\ttry {\n\t\t\t\tconst rv = original.call(this)\n\t\t\t\tcache(this, propertyKey, rv)\n\t\t\t\treturn rv\n\t\t\t} finally {\n\t\t\t\tsyncCalculating.pop()\n\t\t\t}\n\t\t}\n\t},\n})\n\n/**\n * Checks if a property is cached (has a cached value)\n * @param object - The object to check\n * @param propertyKey - The property key to check\n * @returns True if the property has a cached value\n */\nexport function isCached(object: Object, propertyKey: PropertyKey) {\n\treturn !!Object.getOwnPropertyDescriptor(object, propertyKey)\n}\n\n/**\n * Caches a value for a property on an object\n * @param object - The object to cache the value on\n * @param propertyKey - The property key to cache\n * @param value - The value to cache\n */\nexport function cache(object: Object, propertyKey: PropertyKey, value: any) {\n\tObject.defineProperty(object, propertyKey, { value })\n}\n\n/**\n * Creates a decorator that modifies property descriptors for specified properties\n * @param descriptor - The descriptor properties to apply\n * @returns A class decorator that applies the descriptor to specified properties\n */\nexport function describe(descriptor: {\n\tenumerable?: boolean\n\tconfigurable?: boolean // Not modifiable once the property has been defined ?\n\twritable?: boolean\n}) {\n\treturn <T>(...properties: (keyof T)[]): GenericClassDecorator<T> =>\n\t\t(Base) => {\n\t\t\treturn class extends Base {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tfor (const key of properties) {\n\t\t\t\t\t\tObject.defineProperty(this, key, {\n\t\t\t\t\t\t\t...Object.getOwnPropertyDescriptor(this, key),\n\t\t\t\t\t\t\t...descriptor,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n}\n\n/**\n * Decorator that marks methods, properties, or classes as deprecated\n * Provides warning messages when deprecated items are used\n */\nexport const deprecated = Object.assign(\n\tdecorator({\n\t\tmethod(original, propertyKey) {\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.apply(this, args)\n\t\t\t}\n\t\t},\n\t\tgetter(original, propertyKey) {\n\t\t\treturn function (this: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this)\n\t\t\t}\n\t\t},\n\t\tsetter(original, propertyKey) {\n\t\t\treturn function (this: any, value: any) {\n\t\t\t\tdeprecated.warn(this, propertyKey)\n\t\t\t\treturn original.call(this, value)\n\t\t\t}\n\t\t},\n\t\tclass(original) {\n\t\t\treturn class extends original {\n\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\tsuper(...args)\n\t\t\t\t\tdeprecated.warn(this, 'constructor')\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdefault(message: string) {\n\t\t\treturn decorator({\n\t\t\t\tmethod(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsetter(original, propertyKey) {\n\t\t\t\t\treturn function (this: any, value: any) {\n\t\t\t\t\t\tdeprecated.warn(this, propertyKey, message)\n\t\t\t\t\t\treturn original.call(this, value)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tclass(original) {\n\t\t\t\t\treturn class extends original {\n\t\t\t\t\t\tconstructor(...args: any[]) {\n\t\t\t\t\t\t\tsuper(...args)\n\t\t\t\t\t\t\tdeprecated.warn(this, 'constructor', message)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\t\t},\n\t}),\n\t{\n\t\twarn: (target: any, propertyKey: PropertyKey, message?: string) => {\n\t\t\t// biome-ignore lint/suspicious/noConsole: To be overridden\n\t\t\tconsole.warn(\n\t\t\t\t`${target.constructor.name}.${String(propertyKey)} is deprecated${message ? `: ${message}` : ''}`\n\t\t\t)\n\t\t},\n\t}\n)\n\n/**\n * Creates a debounced method decorator that delays execution until after the delay period has passed\n * @param delay - The delay in milliseconds\n * @returns A method decorator that debounces method calls\n */\nexport function debounce(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t// Clear existing timeout\n\t\t\t\tif (timeoutId) {\n\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t}\n\n\t\t\t\t// Set new timeout\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\toriginal.apply(this, args)\n\t\t\t\t\ttimeoutId = null\n\t\t\t\t}, delay)\n\t\t\t}\n\t\t},\n\t})\n}\n\n/**\n * Creates a throttled method decorator that limits execution to once per delay period\n * @param delay - The delay in milliseconds\n * @returns A method decorator that throttles method calls\n */\nexport function throttle(delay: number) {\n\treturn decorator({\n\t\tmethod(original, _propertyKey) {\n\t\t\tlet lastCallTime = 0\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null\n\n\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\tconst now = Date.now()\n\n\t\t\t\t// If enough time has passed since last call, execute immediately\n\t\t\t\tif (now - lastCallTime >= delay) {\n\t\t\t\t\t// Clear any pending timeout since we're executing now\n\t\t\t\t\tif (timeoutId) {\n\t\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}\n\t\t\t\t\tlastCallTime = now\n\t\t\t\t\treturn original.apply(this, args)\n\t\t\t\t}\n\n\t\t\t\t// Otherwise, schedule execution for when the delay period ends\n\t\t\t\tif (!timeoutId) {\n\t\t\t\t\tconst remainingTime = delay - (now - lastCallTime)\n\t\t\t\t\tconst scheduledArgs = [...args] // Capture args at scheduling time\n\t\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\t\tlastCallTime = Date.now()\n\t\t\t\t\t\toriginal.apply(this, scheduledArgs)\n\t\t\t\t\t\ttimeoutId = null\n\t\t\t\t\t}, remainingTime)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t})\n}\n"],"names":["decorator"],"mappings":"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"}
@@ -7,22 +7,22 @@
7
7
  - [5-Minute Quick Start](#5-minute-quick-start)
8
8
  - [Core API](#core-api)
9
9
  - [Effect System](#effect-system)
10
- - [Atomic Operations](#atomic-operations)
11
- - [Advanced Effects](#advanced-effects)
12
- - [Evolution Tracking](#evolution-tracking)
13
- - [Prototype Chains and Pure Objects](#prototype-chains-and-pure-objects)
14
- - [Recursive Touching](#recursive-touching)
15
- - [Why Not Deep Watching?](#why-not-deep-watching)
16
- - [Collections](#collections)
17
- - [Register](#register)
18
- - [Class Reactivity](#class-reactivity)
19
- - [Non-Reactive System](#non-reactive-system)
20
- - [Array Mapping](#array-mapping)
21
- - [Projection](#projection)
22
- - [Record Organization](#record-organization)
23
- - [Memoization](#memoization)
24
- - [Debugging and Development](#debugging-and-development)
25
- - [Cycle Detection](#cycle-detection)
10
+ - [Atomic Operations](./advanced.md#atomic-operations)
11
+ - [Advanced Effects](./advanced.md#advanced-effects)
12
+ - [Evolution Tracking](./advanced.md#evolution-tracking)
13
+ - [Prototype Chains and Pure Objects](./advanced.md#prototype-chains-and-pure-objects)
14
+ - [Recursive Touching](./advanced.md#recursive-touching)
15
+ - [Why Not Deep Watching?](./advanced.md#why-not-deep-watching)
16
+ - [Collections](./advanced.md#collections)
17
+ - [Register](./advanced.md#register)
18
+ - [Class Reactivity](./advanced.md#class-reactivity)
19
+ - [Non-Reactive System](./advanced.md#non-reactive-system)
20
+ - [Array Mapping](./advanced.md#array-mapping)
21
+ - [Projection](./advanced.md#projection)
22
+ - [Record Organization](./advanced.md#record-organization)
23
+ - [Memoization](./advanced.md#memoization)
24
+ - [Debugging and Development](./advanced.md#debugging-and-development)
25
+ - [Cycle Detection](./advanced.md#cycle-detection)
26
26
 
27
27
  ## Introduction
28
28
 
package/docs/reactive.md CHANGED
@@ -19,3 +19,10 @@ The Mutts Reactive System documentation has been split into focused sections for
19
19
  * **[Prototype Chains](./reactive/advanced.md#prototype-chains-and-pure-objects)**: Advanced inheritance patterns
20
20
  * **[Memoization](./reactive/advanced.md#memoization)**: Caching strategies
21
21
  * **[Debugging](./reactive/advanced.md#debugging-and-development)**: Cycle detection and troubleshooting
22
+
23
+ ## Debugging tools
24
+
25
+ The reactive system is currently in a gamma state. The interface is quite fixed, the debugging tools are in place but :
26
+ - still unfinished
27
+ - not deactivatable
28
+ - harming the performances of the application
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mutts",
3
3
  "description": "Modern UTility TS: A collection of TypeScript utilities",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
7
7
  "types": "dist/index.d.ts",
@@ -117,7 +117,7 @@ export function Destroyable<
117
117
  base = undefined
118
118
  }
119
119
  if (!base) {
120
- base = class {} as T
120
+ base = class { } as T
121
121
  }
122
122
 
123
123
  return class Destroyable extends (base as T) {
@@ -139,7 +139,7 @@ export function Destroyable<
139
139
  return Destroyable.destructors.has(obj)
140
140
  }
141
141
 
142
- declare [forwardProperties]: PropertyKey[]
142
+ [forwardProperties]!: PropertyKey[]
143
143
  readonly [allocatedValues]: Allocated
144
144
  constructor(...args: any[]) {
145
145
  super(...args)
@@ -10,13 +10,12 @@ export const native = Symbol('native')
10
10
  const isArray = Array.isArray
11
11
  Array.isArray = ((value: any) =>
12
12
  isArray(value) ||
13
- // biome-ignore lint/suspicious/useIsArray: We are defining it
14
13
  (value &&
15
14
  typeof value === 'object' &&
16
15
  prototypeForwarding in value &&
17
16
  Array.isArray(value[prototypeForwarding]))) as any
18
17
  export class ReactiveBaseArray {
19
- declare readonly [native]: any[]
18
+ readonly [native]!: any[]
20
19
 
21
20
  // Safe array access with negative indices
22
21
  at(index: number): any {
@@ -293,7 +292,6 @@ export class ReactiveArray extends Indexable(ReactiveBaseArray, {
293
292
  }
294
293
  },
295
294
  }) {
296
- declare length: number
297
295
  constructor(original: any[]) {
298
296
  super()
299
297
  Object.defineProperties(this, {
@@ -364,8 +362,8 @@ export class ReactiveArray extends Indexable(ReactiveBaseArray, {
364
362
  deleteCount === items.length
365
363
  ? range(start, start + deleteCount)
366
364
  : range(start, oldLength + Math.max(items.length - deleteCount, 0), {
367
- length: true,
368
- })
365
+ length: true,
366
+ })
369
367
  )
370
368
  }
371
369
  }
@@ -1,7 +1,7 @@
1
1
  import { recordTriggerLink } from './debug'
2
2
  import { bubbleUpChange, objectsWithDeepWatchers } from './deep-watch-state'
3
3
  import { getActiveEffect, isRunning } from './effect-context'
4
- import { batch, effectTrackers, opaqueEffects } from './effects'
4
+ import { batch, effectTrackers, hasBatched, opaqueEffects, recordActivation } from './effects'
5
5
  import { unwrap } from './proxy-state'
6
6
  import { watchers } from './tracking'
7
7
  import { allProps, type Evolution, options, type ScopedCallback, type State } from './types'
@@ -49,7 +49,10 @@ export function collectEffects(
49
49
  options.skipRunningEffect(effect, runningChain as any)
50
50
  continue
51
51
  }
52
- effects.add(effect)
52
+ if (!effects.has(effect)) {
53
+ effects.add(effect)
54
+ if (!hasBatched(effect)) recordActivation(effect, obj, evolution, key)
55
+ }
53
56
  const trackers = effectTrackers.get(effect)
54
57
  recordTriggerLink(sourceEffect, effect, obj, key, evolution)
55
58
  if (trackers) {
@@ -119,6 +122,7 @@ export function touchedOpaque(obj: any, evolution: Evolution, prop: any) {
119
122
  continue
120
123
  }
121
124
  effects.add(effect)
125
+ recordActivation(effect, obj, evolution, prop)
122
126
  const trackers = effectTrackers.get(effect)
123
127
  recordTriggerLink(sourceEffect, effect, obj, prop, evolution)
124
128
  if (trackers) {
@@ -60,6 +60,69 @@ import { ensureZoneHooked } from './zone'
60
60
  type EffectTracking = (obj: any, evolution: Evolution, prop: any) => void
61
61
 
62
62
  export { captureEffectStack, withEffectStack, getActiveEffect, effectStack }
63
+ export interface ActivationRecord {
64
+ effect: ScopedCallback
65
+ obj: any
66
+ evolution: Evolution
67
+ prop: any
68
+ batchId: number
69
+ }
70
+
71
+ // Nested map structure for efficient counting and batch cleanup
72
+ // batchId -> effect root -> obj -> prop -> count
73
+ let activationRegistry: Map<Function, Map<any, Map<any, number>>> | undefined
74
+
75
+ export const activationLog: Omit<ActivationRecord, 'batchId'>[] = new Array(100)
76
+
77
+ export function getActivationLog() {
78
+ return activationLog
79
+ }
80
+
81
+ export function recordActivation(
82
+ effect: ScopedCallback,
83
+ obj: any,
84
+ evolution: Evolution,
85
+ prop: any
86
+ ) {
87
+ const root = getRoot(effect)
88
+
89
+ if (!activationRegistry) return
90
+ let effectData = activationRegistry.get(root)
91
+ if (!effectData) {
92
+ effectData = new Map()
93
+ activationRegistry.set(root, effectData)
94
+ }
95
+ let objData = effectData.get(obj)
96
+ if (!objData) {
97
+ objData = new Map()
98
+ effectData.set(obj, objData)
99
+ }
100
+ const count = (objData.get(prop) ?? 0) + 1
101
+ objData.set(prop, count)
102
+
103
+ // Keep a limited history for diagnostics
104
+ activationLog.unshift({
105
+ effect,
106
+ obj,
107
+ evolution,
108
+ prop,
109
+ })
110
+ activationLog.pop()
111
+
112
+ if (count >= options.maxTriggerPerBatch) {
113
+ const effectName = (root as any)?.name || 'anonymous'
114
+ const message = `Aggressive trigger detected: effect "${effectName}" triggered ${count} times in the batch by the same cause.`
115
+ if (options.maxEffectReaction === 'throw') {
116
+ throw new ReactiveError(message, {
117
+ code: ReactiveErrorCode.MaxReactionExceeded,
118
+ count,
119
+ effect: effectName,
120
+ })
121
+ }
122
+ options.warn(`[reactive] ${message}`)
123
+ }
124
+ }
125
+
63
126
  /**
64
127
  * Registers a debug callback that is called when the current effect is triggered by a dependency change
65
128
  *
@@ -380,6 +443,9 @@ interface BatchQueue {
380
443
  // Track currently executing effects to prevent re-execution
381
444
  // These are all the effects triggered under `activeEffect`
382
445
  let batchQueue: BatchQueue | undefined
446
+ export function hasBatched(effect: ScopedCallback) {
447
+ return batchQueue?.all.has(getRoot(effect))
448
+ }
383
449
  const batchCleanups = new Set<ScopedCallback>()
384
450
 
385
451
  /**
@@ -835,19 +901,24 @@ export function batch(effect: ScopedCallback | ScopedCallback[], immediate?: 'im
835
901
  addToBatch(effect[i], caller, immediate === 'immediate')
836
902
  }
837
903
  if (immediate) {
904
+ const firstReturn: { value?: any } = {}
838
905
  // Execute immediately (before batch returns)
839
906
  for (let i = 0; i < effect.length; i++) {
840
907
  try {
841
- effect[i]()
908
+ const rv = effect[i]()
909
+ if (rv !== undefined && !('value' in firstReturn)) firstReturn.value = rv
842
910
  } finally {
843
911
  const root = getRoot(effect[i])
844
912
  batchQueue.all.delete(root)
845
913
  }
846
914
  }
915
+ return firstReturn.value
847
916
  }
848
917
  // Otherwise, effects will be picked up in next executeNext() call
849
918
  } else {
850
919
  // New batch - initialize
920
+ if (!activationRegistry) activationRegistry = new Map()
921
+ else throw new Error('Batch already in progress')
851
922
  options.beginChain(roots)
852
923
  batchQueue = {
853
924
  all: new Map(),
@@ -928,6 +999,7 @@ export function batch(effect: ScopedCallback | ScopedCallback[], immediate?: 'im
928
999
  for (const cleanup of cleanups) cleanup()
929
1000
  return firstReturn.value
930
1001
  } finally {
1002
+ activationRegistry = undefined
931
1003
  batchQueue = undefined
932
1004
  options.endChain()
933
1005
  }
@@ -935,63 +1007,72 @@ export function batch(effect: ScopedCallback | ScopedCallback[], immediate?: 'im
935
1007
  // Execute in dependency order
936
1008
  const firstReturn: { value?: any } = {}
937
1009
  try {
938
- while (batchQueue.all.size > 0) {
939
- if (effectuatedRoots.length > options.maxEffectChain) {
940
- const cycle = findCycleInChain(effectuatedRoots as any)
941
- const trace = formatRoots(effectuatedRoots as any)
942
- const message = cycle
943
- ? `Max effect chain reached (cycle detected: ${formatRoots(cycle)})`
944
- : `Max effect chain reached (trace: ${trace})`
945
-
946
- const queuedRoots = batchQueue ? Array.from(batchQueue.all.keys()) : []
947
- const queued = queuedRoots.map((r) => r.name || '<anonymous>')
948
- const debugInfo = {
949
- code: ReactiveErrorCode.MaxDepthExceeded,
950
- effectuatedRoots,
951
- cycle,
952
- trace,
953
- maxEffectChain: options.maxEffectChain,
954
- queued: queued.slice(0, 50),
955
- queuedCount: queued.length,
956
- // Try to get causation for the last effect
957
- causalChain:
958
- effectuatedRoots.length > 0
959
- ? getTriggerChain(
960
- batchQueue.all.get(effectuatedRoots[effectuatedRoots.length - 1])!
961
- )
962
- : [],
1010
+ // Outer loop: continue while there are effects OR cleanups pending.
1011
+ // This ensures effects triggered by cleanups are not lost.
1012
+ while (batchQueue.all.size > 0 || batchCleanups.size > 0) {
1013
+ // Inner loop: execute all pending effects
1014
+ while (batchQueue.all.size > 0) {
1015
+ if (effectuatedRoots.length > options.maxEffectChain) {
1016
+ const cycle = findCycleInChain(effectuatedRoots as any)
1017
+ const trace = formatRoots(effectuatedRoots as any)
1018
+ const message = cycle
1019
+ ? `Max effect chain reached (cycle detected: ${formatRoots(cycle)})`
1020
+ : `Max effect chain reached (trace: ${trace})`
1021
+
1022
+ const queuedRoots = batchQueue ? Array.from(batchQueue.all.keys()) : []
1023
+ const queued = queuedRoots.map((r) => r.name || '<anonymous>')
1024
+ const debugInfo = {
1025
+ code: ReactiveErrorCode.MaxDepthExceeded,
1026
+ effectuatedRoots,
1027
+ cycle,
1028
+ trace,
1029
+ maxEffectChain: options.maxEffectChain,
1030
+ queued: queued.slice(0, 50),
1031
+ queuedCount: queued.length,
1032
+ // Try to get causation for the last effect
1033
+ causalChain:
1034
+ effectuatedRoots.length > 0
1035
+ ? getTriggerChain(
1036
+ batchQueue.all.get(effectuatedRoots[effectuatedRoots.length - 1])!
1037
+ )
1038
+ : [],
1039
+ }
1040
+ switch (options.maxEffectReaction) {
1041
+ case 'throw':
1042
+ throw new ReactiveError(`[reactive] ${message}`, debugInfo)
1043
+ case 'debug':
1044
+ // biome-ignore lint/suspicious/noDebugger: This is the whole point here
1045
+ debugger
1046
+ throw new ReactiveError(`[reactive] ${message}`, debugInfo)
1047
+ case 'warn':
1048
+ options.warn(
1049
+ `[reactive] ${message} (queued: ${queued.slice(0, 10).join(', ')}${queued.length > 10 ? ', …' : ''})`
1050
+ )
1051
+ break
1052
+ }
963
1053
  }
964
- switch (options.maxEffectReaction) {
965
- case 'throw':
966
- throw new ReactiveError(`[reactive] ${message}`, debugInfo)
967
- case 'debug':
968
- // biome-ignore lint/suspicious/noDebugger: This is the whole point here
969
- debugger
970
- throw new ReactiveError(`[reactive] ${message}`, debugInfo)
971
- case 'warn':
972
- options.warn(
973
- `[reactive] ${message} (queued: ${queued.slice(0, 10).join(', ')}${queued.length > 10 ? ', …' : ''})`
974
- )
975
- break
1054
+ const rv = executeNext(effectuatedRoots)
1055
+ // executeNext() returns null when batch is complete or cycle detected (throws error)
1056
+ // But functions can legitimately return null, so we check batchQueue.all.size instead
1057
+ if (batchQueue.all.size === 0) {
1058
+ // Batch complete
1059
+ break
976
1060
  }
1061
+ // If executeNext() returned null but batch is not empty, it means a cycle was detected
1062
+ // and an error was thrown, so we won't reach here
1063
+ if (rv !== undefined && !('value' in firstReturn)) firstReturn.value = rv
1064
+ // Note: executeNext() already removed it from batchQueue, so we track by count
977
1065
  }
978
- const rv = executeNext(effectuatedRoots)
979
- // executeNext() returns null when batch is complete or cycle detected (throws error)
980
- // But functions can legitimately return null, so we check batchQueue.all.size instead
981
- if (batchQueue.all.size === 0) {
982
- // Batch complete
983
- break
1066
+ // Process cleanups. If they trigger new effects, the outer loop will catch them.
1067
+ if (batchCleanups.size > 0) {
1068
+ const cleanups = Array.from(batchCleanups)
1069
+ batchCleanups.clear()
1070
+ for (const cleanup of cleanups) cleanup()
984
1071
  }
985
- // If executeNext() returned null but batch is not empty, it means a cycle was detected
986
- // and an error was thrown, so we won't reach here
987
- if (rv !== undefined && !('value' in firstReturn)) firstReturn.value = rv
988
- // Note: executeNext() already removed it from batchQueue, so we track by count
989
1072
  }
990
- const cleanups = Array.from(batchCleanups)
991
- batchCleanups.clear()
992
- for (const cleanup of cleanups) cleanup()
993
1073
  return firstReturn.value
994
1074
  } finally {
1075
+ activationRegistry = undefined
995
1076
  batchQueue = undefined
996
1077
  options.endChain()
997
1078
  }
@@ -13,9 +13,11 @@ export { deepWatch } from './deep-watch'
13
13
  export {
14
14
  addBatchCleanup,
15
15
  atomic,
16
+ batch, // TODO: Batch is now exported for testing purposes, though it shouldn't be - modify the tests to go through `atomic`
16
17
  biDi,
17
18
  defer,
18
19
  effect,
20
+ getActivationLog,
19
21
  getActiveEffect,
20
22
  root,
21
23
  trackEffect,
@@ -25,7 +27,7 @@ export { cleanedBy, cleanup, derived, unreactive, watch } from './interface'
25
27
  export { mapped, ReadOnlyError, reduced } from './mapped'
26
28
  export { type Memoizable, memoize } from './memoize'
27
29
  export { immutables, isNonReactive, registerNativeReactivity } from './non-reactive'
28
- export { project } from './project'
30
+ export { getActiveProjection, project } from './project'
29
31
  export { isReactive, ReactiveBase, reactive, unwrap } from './proxy'
30
32
  export { organize, organized } from './record'
31
33
  export { Register, register } from './register'
@@ -1,4 +1,4 @@
1
- import { decorator, GenericClassDecorator } from '../decorator'
1
+ import { decorator, type GenericClassDecorator } from '../decorator'
2
2
  import { deepWatch } from './deep-watch'
3
3
  import { withEffect } from './effect-context'
4
4
  import { effect, getActiveEffect, untracked } from './effects'