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.
- package/README.md +1 -1
- package/dist/chunks/{index-HNVqPzjz.js → index-Cvxdw6Ax.js} +230 -61
- package/dist/chunks/index-Cvxdw6Ax.js.map +1 -0
- package/dist/chunks/{index-DzUDtFc7.esm.js → index-qiWwozOc.esm.js} +228 -62
- package/dist/chunks/index-qiWwozOc.esm.js.map +1 -0
- package/dist/destroyable.esm.js.map +1 -1
- package/dist/destroyable.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/reactive.d.ts +30 -1
- package/dist/reactive.esm.js +1 -1
- package/dist/reactive.js +4 -1
- package/dist/reactive.js.map +1 -1
- package/dist/std-decorators.esm.js.map +1 -1
- package/dist/std-decorators.js.map +1 -1
- package/docs/reactive/core.md +16 -16
- package/docs/reactive.md +7 -0
- package/package.json +1 -1
- package/src/destroyable.ts +2 -2
- package/src/reactive/array.ts +3 -5
- package/src/reactive/change.ts +6 -2
- package/src/reactive/effects.ts +132 -51
- package/src/reactive/index.ts +3 -1
- package/src/reactive/interface.ts +1 -1
- package/src/reactive/map.ts +6 -6
- package/src/reactive/mapped.ts +2 -3
- package/src/reactive/project.ts +103 -6
- package/src/reactive/proxy.ts +5 -1
- package/src/reactive/set.ts +6 -6
- package/src/reactive/types.ts +22 -0
- package/src/reactive/zone.ts +1 -1
- package/src/std-decorators.ts +1 -1
- package/dist/chunks/index-DzUDtFc7.esm.js.map +0 -1
- package/dist/chunks/index-HNVqPzjz.js.map +0 -1
- /package/{src/reactive/project.project.md → docs/reactive/project.md} +0 -0
package/dist/reactive.d.ts
CHANGED
|
@@ -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 };
|
package/dist/reactive.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
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-
|
|
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;
|
package/dist/reactive.js.map
CHANGED
|
@@ -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":";;AAEA;AACA,MAAM,eAAe,GAA4C,EAAE;AACnE;;;AAGG;AACI,MAAM,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAA;QAC3B,OAAO,YAAA;YACN,MAAM,kBAAkB,GAAG,eAAe,CAAC,SAAS,CACnD,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,CAClD;YACD,IAAI,kBAAkB,GAAG,EAAE;AAC1B,gBAAA,MAAM,IAAI,KAAK,CACd,CAAA,8BAAA,EAAiC;qBAC/B,KAAK,CAAC,kBAAkB;qBACxB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAA,EAAG,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA,CAAE;AAC3D,qBAAA,IAAI,CAAC,MAAM,CAAC,CAAA,SAAA,CAAW,CACzB;AACF,YAAA,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;AACzD,YAAA,IAAI;gBACH,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;AAC9B,gBAAA,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;AAC5B,gBAAA,OAAO,EAAE;YACV;oBAAU;gBACT,eAAe,CAAC,GAAG,EAAE;YACtB;AACD,QAAA,CAAC;IACF,CAAC;AACD,CAAA;AAED;;;;;AAKG;AACG,SAAU,QAAQ,CAAC,MAAc,EAAE,WAAwB,EAAA;IAChE,OAAO,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,MAAM,EAAE,WAAW,CAAC;AAC9D;AAEA;;;;;AAKG;SACa,KAAK,CAAC,MAAc,EAAE,WAAwB,EAAE,KAAU,EAAA;IACzE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC;AACtD;AAEA;;;;AAIG;AACG,SAAU,QAAQ,CAAC,UAIxB,EAAA;IACA,OAAO,CAAI,GAAG,UAAuB,KACpC,CAAC,IAAI,KAAI;QACR,OAAO,cAAc,IAAI,CAAA;AACxB,YAAA,WAAA,CAAY,GAAG,IAAW,EAAA;AACzB,gBAAA,KAAK,CAAC,GAAG,IAAI,CAAC;AACd,gBAAA,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE;AAC7B,oBAAA,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE;AAChC,wBAAA,GAAG,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,GAAG,CAAC;AAC7C,wBAAA,GAAG,UAAU;AACb,qBAAA,CAAC;gBACH;YACD;SACA;AACF,IAAA,CAAC;AACH;AAEA;;;AAGG;MACU,UAAU,GAAG,MAAM,CAAC,MAAM,CACtC,SAAS,CAAC;IACT,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAA;QAC3B,OAAO,UAAqB,GAAG,IAAW,EAAA;AACzC,YAAA,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;YAClC,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;AAClC,QAAA,CAAC;IACF,CAAC;IACD,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAA;QAC3B,OAAO,YAAA;AACN,YAAA,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;AAClC,YAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;AAC3B,QAAA,CAAC;IACF,CAAC;IACD,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAA;AAC3B,QAAA,OAAO,UAAqB,KAAU,EAAA;AACrC,YAAA,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;YAClC,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;AAClC,QAAA,CAAC;IACF,CAAC;AACD,IAAA,KAAK,CAAC,QAAQ,EAAA;QACb,OAAO,cAAc,QAAQ,CAAA;AAC5B,YAAA,WAAA,CAAY,GAAG,IAAW,EAAA;AACzB,gBAAA,KAAK,CAAC,GAAG,IAAI,CAAC;AACd,gBAAA,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;YACrC;SACA;IACF,CAAC;AACD,IAAA,OAAO,CAAC,OAAe,EAAA;AACtB,QAAA,OAAO,SAAS,CAAC;YAChB,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAA;gBAC3B,OAAO,UAAqB,GAAG,IAAW,EAAA;oBACzC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC;oBAC3C,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;AAClC,gBAAA,CAAC;YACF,CAAC;YACD,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAA;gBAC3B,OAAO,YAAA;oBACN,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC;AAC3C,oBAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;AAC3B,gBAAA,CAAC;YACF,CAAC;YACD,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAA;AAC3B,gBAAA,OAAO,UAAqB,KAAU,EAAA;oBACrC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC;oBAC3C,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;AAClC,gBAAA,CAAC;YACF,CAAC;AACD,YAAA,KAAK,CAAC,QAAQ,EAAA;gBACb,OAAO,cAAc,QAAQ,CAAA;AAC5B,oBAAA,WAAA,CAAY,GAAG,IAAW,EAAA;AACzB,wBAAA,KAAK,CAAC,GAAG,IAAI,CAAC;wBACd,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC;oBAC9C;iBACA;YACF,CAAC;AACD,SAAA,CAAC;IACH,CAAC;AACD,CAAA,CAAC,EACF;IACC,IAAI,EAAE,CAAC,MAAW,EAAE,WAAwB,EAAE,OAAgB,KAAI;;AAEjE,QAAA,OAAO,CAAC,IAAI,CACX,CAAA,EAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAA,CAAA,EAAI,MAAM,CAAC,WAAW,CAAC,CAAA,cAAA,EAAiB,OAAO,GAAG,CAAA,EAAA,EAAK,OAAO,CAAA,CAAE,GAAG,EAAE,CAAA,CAAE,CACjG;IACF,CAAC;AACD,CAAA;AAGF;;;;AAIG;AACG,SAAU,QAAQ,CAAC,KAAa,EAAA;AACrC,IAAA,OAAO,SAAS,CAAC;QAChB,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAA;YAC5B,IAAI,SAAS,GAAyC,IAAI;YAE1D,OAAO,UAAqB,GAAG,IAAW,EAAA;;gBAEzC,IAAI,SAAS,EAAE;oBACd,YAAY,CAAC,SAAS,CAAC;gBACxB;;AAGA,gBAAA,SAAS,GAAG,UAAU,CAAC,MAAK;AAC3B,oBAAA,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;oBAC1B,SAAS,GAAG,IAAI;gBACjB,CAAC,EAAE,KAAK,CAAC;AACV,YAAA,CAAC;QACF,CAAC;AACD,KAAA,CAAC;AACH;AAEA;;;;AAIG;AACG,SAAU,QAAQ,CAAC,KAAa,EAAA;AACrC,IAAA,OAAO,SAAS,CAAC;QAChB,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAA;YAC5B,IAAI,YAAY,GAAG,CAAC;YACpB,IAAI,SAAS,GAAyC,IAAI;YAE1D,OAAO,UAAqB,GAAG,IAAW,EAAA;AACzC,gBAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;;AAGtB,gBAAA,IAAI,GAAG,GAAG,YAAY,IAAI,KAAK,EAAE;;oBAEhC,IAAI,SAAS,EAAE;wBACd,YAAY,CAAC,SAAS,CAAC;wBACvB,SAAS,GAAG,IAAI;oBACjB;oBACA,YAAY,GAAG,GAAG;oBAClB,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;gBAClC;;gBAGA,IAAI,CAAC,SAAS,EAAE;oBACf,MAAM,aAAa,GAAG,KAAK,IAAI,GAAG,GAAG,YAAY,CAAC;oBAClD,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;AAC/B,oBAAA,SAAS,GAAG,UAAU,CAAC,MAAK;AAC3B,wBAAA,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE;AACzB,wBAAA,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC;wBACnC,SAAS,GAAG,IAAI;oBACjB,CAAC,EAAE,aAAa,CAAC;gBAClB;AACD,YAAA,CAAC;QACF,CAAC;AACD,KAAA,CAAC;AACH;;;;"}
|
|
@@ -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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"}
|
package/docs/reactive/core.md
CHANGED
|
@@ -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
package/src/destroyable.ts
CHANGED
|
@@ -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
|
-
|
|
142
|
+
[forwardProperties]!: PropertyKey[]
|
|
143
143
|
readonly [allocatedValues]: Allocated
|
|
144
144
|
constructor(...args: any[]) {
|
|
145
145
|
super(...args)
|
package/src/reactive/array.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
368
|
-
|
|
365
|
+
length: true,
|
|
366
|
+
})
|
|
369
367
|
)
|
|
370
368
|
}
|
|
371
369
|
}
|
package/src/reactive/change.ts
CHANGED
|
@@ -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.
|
|
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) {
|
package/src/reactive/effects.ts
CHANGED
|
@@ -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
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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
|
}
|
package/src/reactive/index.ts
CHANGED
|
@@ -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'
|