mutts 1.0.2 → 1.0.4

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 (104) hide show
  1. package/README.md +14 -6
  2. package/dist/chunks/{_tslib-C-cuVLvZ.js → _tslib-BgjropY9.js} +9 -1
  3. package/dist/chunks/_tslib-BgjropY9.js.map +1 -0
  4. package/dist/chunks/{_tslib-CMEnd0VE.esm.js → _tslib-Mzh1rNsX.esm.js} +9 -2
  5. package/dist/chunks/_tslib-Mzh1rNsX.esm.js.map +1 -0
  6. package/dist/chunks/{decorator-D4DU97Zg.js → decorator-DLvrD0UF.js} +42 -19
  7. package/dist/chunks/decorator-DLvrD0UF.js.map +1 -0
  8. package/dist/chunks/{decorator-GnHw1Az7.esm.js → decorator-DqiszP7i.esm.js} +42 -19
  9. package/dist/chunks/decorator-DqiszP7i.esm.js.map +1 -0
  10. package/dist/chunks/index-79Kk8D6e.esm.js +4857 -0
  11. package/dist/chunks/index-79Kk8D6e.esm.js.map +1 -0
  12. package/dist/chunks/index-GRBSx0mB.js +4908 -0
  13. package/dist/chunks/index-GRBSx0mB.js.map +1 -0
  14. package/dist/decorator.esm.js +1 -1
  15. package/dist/decorator.js +1 -1
  16. package/dist/destroyable.d.ts +1 -1
  17. package/dist/destroyable.esm.js +1 -1
  18. package/dist/destroyable.esm.js.map +1 -1
  19. package/dist/destroyable.js +1 -1
  20. package/dist/destroyable.js.map +1 -1
  21. package/dist/devtools/devtools.html +9 -0
  22. package/dist/devtools/devtools.js +5 -0
  23. package/dist/devtools/devtools.js.map +1 -0
  24. package/dist/devtools/manifest.json +8 -0
  25. package/dist/devtools/panel.css +72 -0
  26. package/dist/devtools/panel.html +31 -0
  27. package/dist/devtools/panel.js +13048 -0
  28. package/dist/devtools/panel.js.map +1 -0
  29. package/dist/eventful.esm.js +1 -1
  30. package/dist/eventful.js +1 -1
  31. package/dist/index.d.ts +18 -63
  32. package/dist/index.esm.js +4 -4
  33. package/dist/index.js +37 -11
  34. package/dist/index.js.map +1 -1
  35. package/dist/indexable.d.ts +187 -1
  36. package/dist/indexable.esm.js +197 -3
  37. package/dist/indexable.esm.js.map +1 -1
  38. package/dist/indexable.js +198 -2
  39. package/dist/indexable.js.map +1 -1
  40. package/dist/mutts.umd.js +1 -1
  41. package/dist/mutts.umd.js.map +1 -1
  42. package/dist/mutts.umd.min.js +1 -1
  43. package/dist/mutts.umd.min.js.map +1 -1
  44. package/dist/promiseChain.esm.js.map +1 -1
  45. package/dist/promiseChain.js.map +1 -1
  46. package/dist/reactive.d.ts +602 -97
  47. package/dist/reactive.esm.js +3 -3
  48. package/dist/reactive.js +32 -10
  49. package/dist/reactive.js.map +1 -1
  50. package/dist/std-decorators.esm.js +1 -1
  51. package/dist/std-decorators.js +1 -1
  52. package/docs/ai/api-reference.md +133 -0
  53. package/docs/ai/manual.md +105 -0
  54. package/docs/iterableWeak.md +646 -0
  55. package/docs/reactive/advanced.md +1280 -0
  56. package/docs/reactive/collections.md +767 -0
  57. package/docs/reactive/core.md +973 -0
  58. package/docs/reactive.md +21 -9545
  59. package/package.json +18 -5
  60. package/src/decorator.ts +266 -0
  61. package/src/destroyable.ts +199 -0
  62. package/src/eventful.ts +77 -0
  63. package/src/index.d.ts +9 -0
  64. package/src/index.ts +9 -0
  65. package/src/indexable.ts +484 -0
  66. package/src/introspection.ts +59 -0
  67. package/src/iterableWeak.ts +233 -0
  68. package/src/mixins.ts +123 -0
  69. package/src/promiseChain.ts +110 -0
  70. package/src/reactive/array.ts +414 -0
  71. package/src/reactive/change.ts +134 -0
  72. package/src/reactive/debug.ts +517 -0
  73. package/src/reactive/deep-touch.ts +268 -0
  74. package/src/reactive/deep-watch-state.ts +82 -0
  75. package/src/reactive/deep-watch.ts +168 -0
  76. package/src/reactive/effect-context.ts +94 -0
  77. package/src/reactive/effects.ts +1345 -0
  78. package/src/reactive/index.ts +76 -0
  79. package/src/reactive/interface.ts +223 -0
  80. package/src/reactive/map.ts +171 -0
  81. package/src/reactive/mapped.ts +130 -0
  82. package/src/reactive/memoize.ts +107 -0
  83. package/src/reactive/non-reactive-state.ts +49 -0
  84. package/src/reactive/non-reactive.ts +43 -0
  85. package/src/reactive/project.project.md +93 -0
  86. package/src/reactive/project.ts +335 -0
  87. package/src/reactive/proxy-state.ts +27 -0
  88. package/src/reactive/proxy.ts +289 -0
  89. package/src/reactive/record.ts +196 -0
  90. package/src/reactive/register.ts +421 -0
  91. package/src/reactive/set.ts +144 -0
  92. package/src/reactive/tracking.ts +101 -0
  93. package/src/reactive/types.ts +358 -0
  94. package/src/reactive/zone.ts +208 -0
  95. package/src/std-decorators.ts +217 -0
  96. package/src/utils.ts +117 -0
  97. package/dist/chunks/_tslib-C-cuVLvZ.js.map +0 -1
  98. package/dist/chunks/_tslib-CMEnd0VE.esm.js.map +0 -1
  99. package/dist/chunks/decorator-D4DU97Zg.js.map +0 -1
  100. package/dist/chunks/decorator-GnHw1Az7.esm.js.map +0 -1
  101. package/dist/chunks/index-DBScoeCX.esm.js +0 -1960
  102. package/dist/chunks/index-DBScoeCX.esm.js.map +0 -1
  103. package/dist/chunks/index-DOTmXL89.js +0 -1983
  104. package/dist/chunks/index-DOTmXL89.js.map +0 -1
@@ -1,12 +1,13 @@
1
- import { LegacyPropertyDecorator, ModernMethodDecorator, LegacyClassDecorator, ModernClassDecorator, ModernGetterDecorator, ModernAccessorDecorator, GenericClassDecorator } from './decorator.js';
1
+ import { LegacyPropertyDecorator, ModernMethodDecorator, LegacyClassDecorator, ModernClassDecorator, GenericClassDecorator, ModernGetterDecorator, ModernAccessorDecorator } from './decorator.js';
2
+ import { ArrayReadForward, forwardArray, getAt, setAt } from './indexable.js';
2
3
 
3
4
  /**
4
- * Function type for dependency tracking in effects and computed values
5
+ * Function type for dependency tracking in effects
5
6
  * Restores the active effect context for dependency tracking
6
7
  */
7
8
  type DependencyFunction = <T>(cb: () => T) => T;
8
9
  /**
9
- * Dependency access passed to user callbacks within effects/computed/watch
10
+ * Dependency access passed to user callbacks within effects/watch
10
11
  * Provides functions to track dependencies and information about the effect execution
11
12
  */
12
13
  interface DependencyAccess {
@@ -42,13 +43,13 @@ interface DependencyAccess {
42
43
  */
43
44
  ascend: DependencyFunction;
44
45
  /**
45
- * Indicates whether this is the initial execution of the effect
46
- * - `true`: First execution when the effect is created
47
- * - `false`: Subsequent executions triggered by dependency changes
46
+ * Indicates whether the effect is running as a reaction (i.e. not the first call)
47
+ * - `false`: First execution when the effect is created
48
+ * - `true`: Subsequent executions triggered by dependency changes
48
49
  * @example
49
50
  * ```typescript
50
- * effect(({ init }) => {
51
- * if (init) {
51
+ * effect(({ reaction }) => {
52
+ * if (!reaction) {
52
53
  * console.log('Effect initialized')
53
54
  * // Setup code that should only run once
54
55
  * } else {
@@ -58,12 +59,35 @@ interface DependencyAccess {
58
59
  * })
59
60
  * ```
60
61
  */
61
- init: boolean;
62
+ reaction: boolean;
62
63
  }
63
64
  /**
64
65
  * Type for effect cleanup functions
65
66
  */
66
67
  type ScopedCallback = () => void;
68
+ /**
69
+ * Async execution mode for effects
70
+ * - `cancel`: Cancel previous async execution when dependencies change (default)
71
+ * - `queue`: Queue next execution to run after current completes
72
+ * - `ignore`: Ignore new executions while async work is running
73
+ */
74
+ type AsyncExecutionMode = 'cancel' | 'queue' | 'ignore';
75
+ /**
76
+ * Options for effect creation
77
+ */
78
+ interface EffectOptions {
79
+ /**
80
+ * How to handle async effect executions when dependencies change
81
+ * @default 'cancel'
82
+ */
83
+ asyncMode?: AsyncExecutionMode;
84
+ /**
85
+ * If true, this effect is "opaque" to deep optimizations: it sees the object reference itself
86
+ * and must be notified when it changes, regardless of deep content similarity.
87
+ * Use this for effects that depend on object identity (like memoize).
88
+ */
89
+ opaque?: boolean;
90
+ }
67
91
  /**
68
92
  * Type for property evolution events
69
93
  */
@@ -84,14 +108,43 @@ type State = {
84
108
  next: State;
85
109
  } | {};
86
110
  /**
87
- * Object containing internal reactive system state for debugging and profiling
111
+ * Structured error codes for machine-readable diagnosis
88
112
  */
89
- declare const profileInfo: any;
113
+ declare enum ReactiveErrorCode {
114
+ CycleDetected = "CYCLE_DETECTED",
115
+ MaxDepthExceeded = "MAX_DEPTH_EXCEEDED",
116
+ MaxReactionExceeded = "MAX_REACTION_EXCEEDED",
117
+ WriteInComputed = "WRITE_IN_COMPUTED",
118
+ TrackingError = "TRACKING_ERROR"
119
+ }
120
+ type CycleDebugInfo = {
121
+ code: ReactiveErrorCode.CycleDetected;
122
+ cycle: string[];
123
+ details?: string;
124
+ };
125
+ type MaxDepthDebugInfo = {
126
+ code: ReactiveErrorCode.MaxDepthExceeded;
127
+ depth: number;
128
+ chain: string[];
129
+ };
130
+ type MaxReactionDebugInfo = {
131
+ code: ReactiveErrorCode.MaxReactionExceeded;
132
+ count: number;
133
+ effect: string;
134
+ };
135
+ type GenericDebugInfo = {
136
+ code: ReactiveErrorCode;
137
+ causalChain?: string[];
138
+ creationStack?: string;
139
+ [key: string]: any;
140
+ };
141
+ type ReactiveDebugInfo = CycleDebugInfo | MaxDepthDebugInfo | MaxReactionDebugInfo | GenericDebugInfo;
90
142
  /**
91
143
  * Error class for reactive system errors
92
144
  */
93
145
  declare class ReactiveError extends Error {
94
- constructor(message: string);
146
+ debugInfo?: ReactiveDebugInfo;
147
+ constructor(message: string, debugInfo?: ReactiveDebugInfo);
95
148
  }
96
149
  /**
97
150
  * Global options for the reactive system
@@ -101,27 +154,28 @@ declare const options: {
101
154
  * Debug purpose: called when an effect is entered
102
155
  * @param effect - The effect that is entered
103
156
  */
104
- enter: (effect: Function) => void;
157
+ enter: (_effect: Function) => void;
105
158
  /**
106
159
  * Debug purpose: called when an effect is left
107
160
  * @param effect - The effect that is left
108
161
  */
109
- leave: (effect: Function) => void;
162
+ leave: (_effect: Function) => void;
110
163
  /**
111
164
  * Debug purpose: called when an effect is chained
112
165
  * @param target - The effect that is being triggered
113
166
  * @param caller - The effect that is calling the target
114
167
  */
115
- chain: (targets: Function[], caller?: Function) => void;
168
+ chain: (_targets: Function[], _caller?: Function) => void;
116
169
  /**
117
170
  * Debug purpose: called when an effect chain is started
118
171
  * @param target - The effect that is being triggered
119
172
  */
120
- beginChain: (targets: Function[]) => void;
173
+ beginChain: (_targets: Function[]) => void;
121
174
  /**
122
175
  * Debug purpose: called when an effect chain is ended
123
176
  */
124
177
  endChain: () => void;
178
+ garbageCollected: (_fn: Function) => void;
125
179
  /**
126
180
  * Debug purpose: called when an object is touched
127
181
  * @param obj - The object that is touched
@@ -129,7 +183,13 @@ declare const options: {
129
183
  * @param props - The properties that changed
130
184
  * @param deps - The dependencies that changed
131
185
  */
132
- touched: (obj: any, evolution: Evolution, props?: any[], deps?: Set<ScopedCallback>) => void;
186
+ touched: (_obj: any, _evolution: Evolution, _props?: any[], _deps?: Set<ScopedCallback>) => void;
187
+ /**
188
+ * Debug purpose: called when an effect is skipped because it's already running
189
+ * @param effect - The effect that is already running
190
+ * @param runningChain - The array of effects from the detected one to the currently running one
191
+ */
192
+ skipRunningEffect: (_effect: ScopedCallback, _runningChain: ScopedCallback[]) => void;
133
193
  /**
134
194
  * Debug purpose: maximum effect chain (like call stack max depth)
135
195
  * Used to prevent infinite loops
@@ -142,6 +202,15 @@ declare const options: {
142
202
  * @default 'throw'
143
203
  */
144
204
  maxEffectReaction: "throw" | "debug" | "warn";
205
+ /**
206
+ * How to handle cycles detected in effect batches
207
+ * - 'throw': Throw an error with cycle information (default, recommended for development)
208
+ * - 'warn': Log a warning and break the cycle by executing one effect
209
+ * - 'break': Silently break the cycle by executing one effect (recommended for production)
210
+ * - 'strict': Prevent cycle creation by checking graph before execution (throws error)
211
+ * @default 'throw'
212
+ */
213
+ cycleHandling: "throw" | "warn" | "break" | "strict";
145
214
  /**
146
215
  * Maximum depth for deep watching traversal
147
216
  * Used to prevent infinite recursion in circular references
@@ -159,9 +228,184 @@ declare const options: {
159
228
  * @default true
160
229
  */
161
230
  ignoreAccessors: boolean;
231
+ /**
232
+ * Enable recursive touching when objects with the same prototype are replaced
233
+ * When enabled, replacing an object with another of the same prototype triggers
234
+ * recursive diffing instead of notifying parent effects
235
+ * @default true
236
+ */
237
+ recursiveTouching: boolean;
238
+ /**
239
+ * Default async execution mode for effects that return Promises
240
+ * - 'cancel': Cancel previous async execution when dependencies change (default, enables async zone)
241
+ * - 'queue': Queue next execution to run after current completes (enables async zone)
242
+ * - 'ignore': Ignore new executions while async work is running (enables async zone)
243
+ * - false: Disable async zone and async mode handling (effects run concurrently)
244
+ *
245
+ * **When truthy:** Enables async zone (Promise.prototype wrapping) for automatic context
246
+ * preservation in Promise callbacks. Warning: This modifies Promise.prototype globally.
247
+ * Only enable if no other library modifies Promise.prototype.
248
+ *
249
+ * **When false:** Async zone is disabled. Use `tracked()` manually in Promise callbacks.
250
+ *
251
+ * Can be overridden per-effect via EffectOptions
252
+ * @default 'cancel'
253
+ */
254
+ asyncMode: AsyncExecutionMode | false;
162
255
  warn: (...args: any[]) => void;
256
+ /**
257
+ * Configuration for the introspection system
258
+ */
259
+ introspection: {
260
+ /**
261
+ * Whether to keep a history of mutations for debugging
262
+ * @default false
263
+ */
264
+ enableHistory: boolean;
265
+ /**
266
+ * Number of mutations to keep in history
267
+ * @default 50
268
+ */
269
+ historySize: number;
270
+ };
271
+ /**
272
+ * Configuration for zone hooks - control which async APIs are hooked
273
+ * Each option controls whether the corresponding async API is wrapped to preserve effect context
274
+ * Only applies when asyncMode is enabled (truthy)
275
+ */
276
+ zones: {
277
+ /**
278
+ * Hook setTimeout to preserve effect context
279
+ * @default true
280
+ */
281
+ setTimeout: boolean;
282
+ /**
283
+ * Hook setInterval to preserve effect context
284
+ * @default true
285
+ */
286
+ setInterval: boolean;
287
+ /**
288
+ * Hook requestAnimationFrame (runs in untracked context when hooked)
289
+ * @default true
290
+ */
291
+ requestAnimationFrame: boolean;
292
+ /**
293
+ * Hook queueMicrotask to preserve effect context
294
+ * @default true
295
+ */
296
+ queueMicrotask: boolean;
297
+ };
163
298
  };
299
+
300
+ /**
301
+ * Gets the current state of a reactive object for evolution tracking
302
+ * @param obj - The reactive object
303
+ * @returns The current state object
304
+ */
305
+ declare function getState(obj: any): State;
306
+ /**
307
+ * Triggers effects for a single property change
308
+ * @param obj - The object that changed
309
+ * @param evolution - The type of change
310
+ * @param prop - The property that changed
311
+ */
312
+ declare function touched1(obj: any, evolution: Evolution, prop: any): void;
313
+ /**
314
+ * Triggers effects for property changes
315
+ * @param obj - The object that changed
316
+ * @param evolution - The type of change
317
+ * @param props - The properties that changed
318
+ */
319
+ declare function touched(obj: any, evolution: Evolution, props?: Iterable<any>): void;
320
+
321
+ /**
322
+ * Debug utilities for the reactivity system
323
+ * - Captures effect metadata (names, parent relationships)
324
+ * - Records cause → consequence edges with object/prop labels
325
+ * - Provides graph data for tooling (DevTools panel, etc.)
326
+ */
327
+
328
+ type NodeKind = 'effect' | 'external' | 'state';
329
+ type EdgeKind = 'cause' | 'dependency' | 'trigger';
330
+ interface EffectNode {
331
+ id: string;
332
+ label: string;
333
+ type: NodeKind;
334
+ depth: number;
335
+ parentId?: string;
336
+ debugName?: string;
337
+ }
338
+ interface ObjectNode {
339
+ id: string;
340
+ label: string;
341
+ type: NodeKind;
342
+ debugName?: string;
343
+ }
344
+ interface GraphEdge {
345
+ id: string;
346
+ source: string;
347
+ target: string;
348
+ type: EdgeKind;
349
+ label: string;
350
+ count?: number;
351
+ }
352
+ interface ReactivityGraph {
353
+ nodes: Array<EffectNode | ObjectNode>;
354
+ edges: GraphEdge[];
355
+ meta: {
356
+ generatedAt: number;
357
+ devtoolsEnabled: boolean;
358
+ };
359
+ }
360
+ /**
361
+ * Assign a debug-friendly name to an effect (shown in DevTools)
362
+ */
363
+ declare function setEffectName(effect: ScopedCallback, name: string): void;
364
+ /**
365
+ * Assign a debug-friendly name to a reactive object
366
+ */
367
+ declare function setObjectName(obj: object, name: string): void;
368
+ /**
369
+ * Register an effect so it appears in the DevTools graph
370
+ */
371
+ declare function registerEffectForDebug(effect: ScopedCallback): void;
372
+ /**
373
+ * Register a reactive object so it appears in the DevTools graph
374
+ */
375
+ declare function registerObjectForDebug(obj: object): void;
376
+ /**
377
+ * Builds a graph representing current reactive state (effects, objects, and trigger edges)
378
+ */
379
+ declare function buildReactivityGraph(): ReactivityGraph;
380
+ /**
381
+ * Enables the DevTools bridge and exposes the debug API on window.
382
+ * Call as early as possible in development builds.
383
+ */
384
+ declare function enableDevTools(): void;
385
+ declare function isDevtoolsEnabled(): boolean;
386
+
387
+ /**
388
+ * Deep watch an object and all its nested properties
389
+ * @param target - The object to watch deeply
390
+ * @param callback - The callback to call when any nested property changes
391
+ * @param options - Options for the deep watch
392
+ * @returns A cleanup function to stop watching
393
+ */
394
+ /**
395
+ * Sets up deep watching for an object, tracking all nested property changes
396
+ * @param target - The object to watch
397
+ * @param callback - The callback to call when changes occur
398
+ * @param options - Options for deep watching
399
+ * @returns A cleanup function to stop deep watching
400
+ */
401
+ declare function deepWatch<T extends object>(target: T, callback: (value: T) => void, { immediate }?: {
402
+ immediate?: boolean;
403
+ }): (() => void) | undefined;
404
+
405
+ declare function getActiveEffect(): ScopedCallback;
406
+
164
407
  type EffectTracking = (obj: any, evolution: Evolution, prop: any) => void;
408
+
165
409
  /**
166
410
  * Registers a debug callback that is called when the current effect is triggered by a dependency change
167
411
  *
@@ -200,103 +444,104 @@ type EffectTracking = (obj: any, evolution: Evolution, prop: any) => void;
200
444
  * ```
201
445
  */
202
446
  declare function trackEffect(onTouch: EffectTracking): void;
203
- /**
204
- * Gets the current state of a reactive object for evolution tracking
205
- * @param obj - The reactive object
206
- * @returns The current state object
207
- */
208
- declare function getState(obj: any): State;
209
- declare let activeEffect: ScopedCallback | undefined;
210
447
  /**
211
448
  * Adds a cleanup function to be called when the current batch of effects completes
212
449
  * @param cleanup - The cleanup function to add
213
450
  */
214
451
  declare function addBatchCleanup(cleanup: ScopedCallback): void;
215
452
  /**
216
- * Decorator that makes methods atomic - batches all effects triggered within the method
217
- */
218
- declare const atomic: LegacyPropertyDecorator<any> & ModernMethodDecorator<any> & (<Args extends any[], Return>(original: (...args: Args) => Return) => (...args: Args) => Return);
219
- /**
220
- * Base mixin for reactive classes that provides proper constructor reactivity
221
- * Solves constructor reactivity issues in complex inheritance trees
222
- */
223
- declare const ReactiveBase: (new (...args: any[]) => {
224
- [x: string]: any;
225
- }) & (<Base>(base: abstract new (...args: any[]) => Base) => new (...args: any[]) => {
226
- [x: string]: any;
227
- } & Base);
228
- /**
229
- * Always-reactive mixin that makes classes inherently reactive
230
- * Can be used as both a base class and a mixin function
231
- */
232
- declare const Reactive: (new (...args: any[]) => {
233
- [x: string]: any;
234
- }) & (<Base>(base: abstract new (...args: any[]) => Base) => new (...args: any[]) => {
235
- [x: string]: any;
236
- } & Base);
237
- declare function reactiveObject<T>(anyTarget: T): T;
238
- /**
239
- * Main decorator for making classes reactive
240
- * Automatically makes class instances reactive when created
241
- */
242
- declare const reactive: LegacyClassDecorator<new (...args: any[]) => any> & ModernClassDecorator<new (...args: any[]) => any> & typeof reactiveObject;
243
- /**
244
- * Gets the original, non-reactive object from a reactive proxy
245
- * @param proxy - The reactive proxy
246
- * @returns The original object
247
- */
248
- declare function unwrap<T>(proxy: T): T;
249
- /**
250
- * Checks if an object is a reactive proxy
251
- * @param obj - The object to check
252
- * @returns True if the object is reactive
453
+ * Semantic alias for `addBatchCleanup` - defers work to the end of the current reactive batch.
454
+ *
455
+ * Use this when an effect needs to perform an action that would modify state the effect depends on,
456
+ * which would create a reactive cycle. The deferred callback runs after all effects complete.
457
+ *
458
+ * @param callback - The callback to defer until after the current batch completes
459
+ *
460
+ * @example
461
+ * ```typescript
462
+ * effect(() => {
463
+ * processData()
464
+ *
465
+ * // Defer to avoid cycle (createMovement modifies state this effect reads)
466
+ * defer(() => {
467
+ * createMovement(data)
468
+ * })
469
+ * })
470
+ * ```
253
471
  */
254
- declare function isReactive(obj: any): boolean;
472
+ declare const defer: typeof addBatchCleanup;
473
+ declare function batch(effect: ScopedCallback | ScopedCallback[], immediate?: 'immediate'): any;
255
474
  /**
256
- * Executes a function without tracking dependencies
257
- * @param fn - The function to execute
475
+ * Decorator that makes methods atomic - batches all effects triggered within the method
258
476
  */
259
- declare function untracked<T>(fn: () => T): T;
477
+ declare const atomic: LegacyPropertyDecorator<any> & ModernMethodDecorator<any> & (<Args extends any[], Return>(original: (...args: Args) => Return) => (...args: Args) => Return);
260
478
  /**
261
479
  * @param fn - The effect function to run - provides the cleaner
262
480
  * @returns The cleanup function
263
481
  */
264
482
  /**
265
483
  * Creates a reactive effect that automatically re-runs when dependencies change
266
- * @param fn - The effect function that provides dependencies and may return a cleanup function
267
- * @param args - Additional arguments that are forwarded to the effect function
484
+ * @param fn - The effect function that provides dependencies and may return a cleanup function or Promise
485
+ * @param options - Options for effect execution
268
486
  * @returns A cleanup function to stop the effect
269
487
  */
270
- declare function effect<Args extends any[]>(fn: (access: DependencyAccess, ...args: Args) => ScopedCallback | undefined | void, ...args: Args): ScopedCallback;
488
+ declare function effect(fn: (access: DependencyAccess) => ScopedCallback | undefined | void | Promise<any>, effectOptions?: EffectOptions): ScopedCallback;
271
489
  /**
272
- * Set of functions that test if objects are immutable
273
- * Objects that pass these tests will not be made reactive
490
+ * Executes a function without tracking dependencies but maintains parent cleanup relationship
491
+ * Effects created inside will still be cleaned up when the parent effect is destroyed
492
+ * @param fn - The function to execute
274
493
  */
275
- declare const immutables: Set<(tested: any) => boolean>;
494
+ declare function untracked<T>(fn: () => T): T;
276
495
  /**
277
- * Checks if an object is marked as non-reactive
278
- * @param obj - The object to check
279
- * @returns True if the object is non-reactive
496
+ * Executes a function from a virgin/root context - no parent effect, no tracking
497
+ * Creates completely independent effects that won't be cleaned up by any parent
498
+ * @param fn - The function to execute
280
499
  */
281
- declare function isNonReactive(obj: any): boolean;
500
+ declare function root<T>(fn: () => T): T;
282
501
 
283
502
  /**
284
- * Registers a callback to be called when a computed property is invalidated
285
- * @param cb - The callback to register
286
- * @param warn - Whether to warn if used outside of a computed property
503
+ * Creates a bidirectional binding between a reactive value and a non-reactive external value
504
+ * Prevents infinite loops by automatically suppressing circular notifications
505
+ *
506
+ * @param received - Function called when the reactive value changes (external setter)
507
+ * @param get - Getter for the reactive value OR an object with `{ get, set }` properties
508
+ * @param set - Setter for the reactive value (required if `get` is a function)
509
+ * @returns A function to manually provide updates from the external side
510
+ *
511
+ * @example
512
+ * ```typescript
513
+ * const model = reactive({ value: '' })
514
+ * const input = { value: '' }
515
+ *
516
+ * // Bidirectional binding
517
+ * const provide = biDi(
518
+ * (v) => input.value = v, // external setter
519
+ * () => model.value, // reactive getter
520
+ * (v) => model.value = v // reactive setter
521
+ * )
522
+ *
523
+ * // External notification (e.g., from input event)
524
+ * provide('new value') // Updates model.value, doesn't trigger circular loop
525
+ * ```
526
+ *
527
+ * @example Using object syntax
528
+ * ```typescript
529
+ * const provide = biDi(
530
+ * (v) => setHTMLValue(v),
531
+ * { get: () => reactiveObj.value, set: (v) => reactiveObj.value = v }
532
+ * )
533
+ * ```
287
534
  */
288
- declare function invalidateComputed(cb: () => void, warn?: boolean): void;
289
- type ComputedFunction<T> = (dep: DependencyAccess) => T;
290
- declare function computedFunction<T>(getter: ComputedFunction<T>): T;
535
+ declare function biDi<T>(received: (value: T) => void, value: {
536
+ get: () => T;
537
+ set: (value: T) => void;
538
+ }): (value: T) => void;
539
+ declare function biDi<T>(received: (value: T) => void, get: () => T, set: (value: T) => void): (value: T) => void;
540
+
291
541
  /**
292
- * Decorator and function for creating computed properties that cache their values
293
- * Only recomputes when dependencies change
542
+ * Symbol for accessing the cleanup function on cleaned objects
294
543
  */
295
- declare const computed: LegacyPropertyDecorator<any> & ModernGetterDecorator<any> & ModernAccessorDecorator<any> & typeof computedFunction & {
296
- map: typeof computedMap;
297
- memo: typeof computedMapMemo;
298
- self: typeof selfReferencing;
299
- };
544
+ declare const cleanup: unique symbol;
300
545
  /**
301
546
  * Options for the watch function
302
547
  */
@@ -307,7 +552,7 @@ interface WatchOptions {
307
552
  deep?: boolean;
308
553
  }
309
554
  /**
310
- * Watches a computed value and calls a callback when it changes
555
+ * Watches a reactive value and calls a callback when it changes
311
556
  * @param value - Function that returns the value to watch
312
557
  * @param changed - Callback to call when the value changes
313
558
  * @param options - Watch options
@@ -317,7 +562,7 @@ declare function watch<T>(value: (dep: DependencyAccess) => T, changed: (value:
317
562
  deep?: false;
318
563
  }): ScopedCallback;
319
564
  /**
320
- * Watches a computed value with deep watching enabled
565
+ * Watches a reactive value with deep watching enabled
321
566
  * @param value - Function that returns the value to watch
322
567
  * @param changed - Callback to call when the value changes
323
568
  * @param options - Watch options with deep watching enabled
@@ -341,12 +586,272 @@ declare function unreactiveApplication<T extends object>(obj: T): T;
341
586
  * Prevents objects from being made reactive
342
587
  */
343
588
  declare const unreactive: LegacyClassDecorator<new (...args: any[]) => any> & ModernClassDecorator<new (...args: any[]) => any> & typeof unreactiveApplication;
344
- declare function cleanedBy<T extends object>(obj: T, cleanup: ScopedCallback): T & {
345
- cleanup: () => void;
589
+ declare function cleanedBy<T extends object>(obj: T, cleanupFn: ScopedCallback): T & {
590
+ [cleanup]: ScopedCallback;
346
591
  };
347
- declare function computedMap<T, U>(inputs: T[], compute: (input: T, index: number, oldValue?: U) => U, resize?: (newLength: number, oldLength: number) => void): U[];
348
- declare function computedMapMemo<I, O>(inputs: I[] | (() => I[]), compute: (input: I) => O): O[];
349
- declare function selfReferencing<T extends object>(compute: (dep: DependencyAccess) => T): T;
592
+ /**
593
+ * Creates a derived value that automatically recomputes when dependencies change
594
+ * @param compute - Function that computes the derived value
595
+ * @returns Object with value and cleanup function
596
+ */
597
+ declare function derived<T>(compute: (dep: DependencyAccess) => T): {
598
+ value: T;
599
+ [cleanup]: ScopedCallback;
600
+ };
601
+
602
+ declare class ReadOnlyError extends Error {
603
+ }
604
+ declare function mapped<T, U>(inputs: readonly T[], compute: (input: T, index: number, output: U[]) => U, resize?: (newLength: number, oldLength: number) => void): readonly U[];
605
+ declare function reduced<T, U, R extends object = any>(inputs: readonly T[], compute: (input: T, factor: R) => readonly U[]): readonly U[];
606
+
607
+ type Memoizable = object | any[] | symbol | ((...args: any[]) => any);
608
+ declare function memoizeFunction<Result, Args extends Memoizable[]>(fn: (...args: Args) => Result): (...args: Args) => Result;
609
+ declare const memoize: LegacyPropertyDecorator<any> & ModernMethodDecorator<any> & ModernGetterDecorator<any> & ModernAccessorDecorator<any> & typeof memoizeFunction;
610
+
611
+ declare const immutables: Set<(tested: any) => boolean>;
612
+ declare function isNonReactive(obj: any): boolean;
613
+ declare function registerNativeReactivity(originalClass: new (...args: any[]) => any, reactiveClass: new (...args: any[]) => any): void;
614
+
615
+ type KeyFunction<T, K extends PropertyKey> = (item: T) => K;
616
+ interface RegisterInstance<T> extends ArrayReadForward<T> {
617
+ [index: number]: T;
618
+ }
619
+ declare const RegisterClass_base: new () => ArrayReadForward<any> & {
620
+ [x: number]: any;
621
+ toArray(): any[];
622
+ };
623
+ declare class RegisterClass<T, K extends PropertyKey = PropertyKey> extends RegisterClass_base implements RegisterInstance<T> {
624
+ #private;
625
+ protected get [forwardArray](): readonly T[];
626
+ constructor(keyFn: KeyFunction<T, K>, initial?: Iterable<T>);
627
+ private ensureKey;
628
+ private assertValidKey;
629
+ private setKeyValue;
630
+ private cleanupValue;
631
+ private disposeKeyEffects;
632
+ private incrementUsage;
633
+ private decrementUsage;
634
+ private normalizeIndex;
635
+ private assignAt;
636
+ private insertKeyValue;
637
+ private rebuildFrom;
638
+ get length(): number;
639
+ [getAt](index: number): T | undefined;
640
+ [setAt](index: number, value: T): void;
641
+ push(...items: T[]): number;
642
+ pop(): T | undefined;
643
+ shift(): T | undefined;
644
+ unshift(...items: T[]): number;
645
+ splice(start: number, deleteCount?: number, ...items: T[]): T[];
646
+ clear(): void;
647
+ get(key: K): T | undefined;
648
+ set(key: K, value: T): void;
649
+ remove(key: K): void;
650
+ removeAt(index: number): T | undefined;
651
+ /**
652
+ * Keep only the items for which the predicate returns true.
653
+ * Items for which the predicate returns false are removed.
654
+ *
655
+ * The predicate is evaluated once per distinct key; duplicate keys
656
+ * will follow the same keep/remove decision.
657
+ */
658
+ keep(predicate: (value: T) => boolean): void;
659
+ hasKey(key: K): boolean;
660
+ indexOfKey(key: K): number;
661
+ mapKeys(): IterableIterator<K>;
662
+ update(...values: T[]): void;
663
+ upsert(insert: (value: T) => void, ...values: T[]): void;
664
+ entries(): IterableIterator<[number, T]>;
665
+ [Symbol.iterator](): IterableIterator<T>;
666
+ toString(): string;
667
+ at(index: number): T | undefined;
668
+ reverse(): this;
669
+ sort(compareFn?: ((a: T, b: T) => number) | undefined): this;
670
+ fill(value: T, start?: number, end?: number): this;
671
+ copyWithin(target: number, start: number, end?: number): this;
672
+ }
673
+ type Register<T, K extends PropertyKey = PropertyKey> = RegisterClass<T, K> & T[];
674
+ declare const Register: new <T, K extends PropertyKey = PropertyKey>(keyFn: KeyFunction<T, K>, initial?: Iterable<T>) => Register<T, K>;
675
+ declare function register<T, K extends PropertyKey = PropertyKey>(keyFn: KeyFunction<T, K>, initial?: Iterable<T>): Register<T, K>;
676
+
677
+ type ProjectOldValue<Target> = Target extends readonly (infer Item)[] ? Item : Target extends Map<any, infer Item> ? Item : Target extends Record<PropertyKey, infer Item> ? Item : unknown;
678
+ type ProjectAccess<SourceValue, Key, SourceType, Target> = {
679
+ readonly key: Key;
680
+ get(): SourceValue;
681
+ set(value: SourceValue): boolean;
682
+ readonly source: SourceType;
683
+ readonly old?: ProjectOldValue<Target>;
684
+ value: SourceValue;
685
+ };
686
+ type BivariantProjectCallback<Args extends any[], Return> = {
687
+ bivarianceHack(...args: Args): Return;
688
+ }['bivarianceHack'];
689
+ type ProjectCallback<SourceValue, Key, Target extends object, SourceType, Result> = BivariantProjectCallback<[ProjectAccess<SourceValue, Key, SourceType, Target>, Target], Result>;
690
+ type ProjectResult<Target extends object> = Target & {
691
+ [cleanup]: ScopedCallback;
692
+ };
693
+ declare function projectArray<SourceValue, ResultValue>(source: readonly SourceValue[], apply: ProjectCallback<SourceValue, number, ResultValue[], readonly SourceValue[], ResultValue>): ProjectResult<ResultValue[]>;
694
+ declare function projectRegister<Key extends PropertyKey, SourceValue, ResultValue>(source: Register<SourceValue, Key>, apply: ProjectCallback<SourceValue, Key, Map<Key, ResultValue>, Register<SourceValue, Key>, ResultValue>): ProjectResult<Map<Key, ResultValue>>;
695
+ declare function projectRecord<Source extends Record<PropertyKey, any>, ResultValue>(source: Source, apply: ProjectCallback<Source[keyof Source], keyof Source, Record<keyof Source, ResultValue>, Source, ResultValue>): ProjectResult<Record<keyof Source, ResultValue>>;
696
+ declare function projectMap<Key, Value, ResultValue>(source: Map<Key, Value>, apply: ProjectCallback<Value, Key, Map<Key, ResultValue>, Map<Key, Value>, ResultValue>): ProjectResult<Map<Key, ResultValue>>;
697
+ type ProjectOverload = {
698
+ <SourceValue, ResultValue>(source: readonly SourceValue[], apply: ProjectCallback<SourceValue, number, ResultValue[], readonly SourceValue[], ResultValue>): ProjectResult<ResultValue[]>;
699
+ <Key extends PropertyKey, SourceValue, ResultValue>(source: Register<SourceValue, Key>, apply: ProjectCallback<SourceValue, Key, Map<Key, ResultValue>, Register<SourceValue, Key>, ResultValue>): ProjectResult<Map<Key, ResultValue>>;
700
+ <Source extends Record<PropertyKey, any>, ResultValue>(source: Source, apply: ProjectCallback<Source[keyof Source], keyof Source, Record<keyof Source, ResultValue>, Source, ResultValue>): ProjectResult<Record<keyof Source, ResultValue>>;
701
+ <Key, Value, ResultValue>(source: Map<Key, Value>, apply: ProjectCallback<Value, Key, Map<Key, ResultValue>, Map<Key, Value>, ResultValue>): ProjectResult<Map<Key, ResultValue>>;
702
+ array: typeof projectArray;
703
+ register: typeof projectRegister;
704
+ record: typeof projectRecord;
705
+ map: typeof projectMap;
706
+ };
707
+ declare const project: ProjectOverload;
708
+
709
+ declare function unwrap<T>(obj: T): T;
710
+ declare function isReactive(obj: any): boolean;
711
+
712
+ /**
713
+ * Base mixin for reactive classes that provides proper constructor reactivity
714
+ * Solves constructor reactivity issues in complex inheritance trees
715
+ */
716
+ declare const ReactiveBase: (new (...args: any[]) => {
717
+ [x: string]: any;
718
+ }) & (<Base>(base: abstract new (...args: any[]) => Base) => new (...args: any[]) => {
719
+ [x: string]: any;
720
+ } & Base);
721
+ declare function reactiveObject<T>(anyTarget: T): T;
722
+ /**
723
+ * Main decorator for making classes reactive
724
+ * Automatically makes class instances reactive when created
725
+ */
726
+ declare const reactive: LegacyClassDecorator<new (...args: any[]) => any> & ModernClassDecorator<new (...args: any[]) => any> & typeof reactiveObject;
727
+
728
+ /**
729
+ * Provides type-safe access to a source object's property within the organized callback.
730
+ * @template Source - The type of the source object
731
+ * @template Key - The type of the property key in the source object
732
+ */
733
+ type OrganizedAccess<Source extends Record<PropertyKey, any>, Key extends keyof Source> = {
734
+ /** The property key being accessed */
735
+ readonly key: Key;
736
+ /**
737
+ * Gets the current value of the property from the source object
738
+ * @returns The current value of the property
739
+ */
740
+ get(): Source[Key];
741
+ /**
742
+ * Updates the property value in the source object
743
+ * @param value - The new value to set
744
+ * @returns {boolean} True if the update was successful
745
+ */
746
+ set(value: Source[Key]): boolean;
747
+ /**
748
+ * The current value of the property (equivalent to using get()/set() directly)
749
+ */
750
+ value: Source[Key];
751
+ };
752
+ /**
753
+ * Callback function type for the organized function that processes each source property.
754
+ * @template Source - The type of the source object
755
+ * @template Target - The type of the target object
756
+ */
757
+ type OrganizedCallback<Source extends Record<PropertyKey, any>, Target extends object> = <Key extends keyof Source>(
758
+ /**
759
+ * Accessor object for the current source property
760
+ */
761
+ access: OrganizedAccess<Source, Key>,
762
+ /**
763
+ * The target object where organized data will be stored
764
+ */
765
+ target: Target) => ScopedCallback | undefined;
766
+ /**
767
+ * The result type of the organized function, combining the target object with cleanup capability.
768
+ * @template Target - The type of the target object
769
+ */
770
+ type OrganizedResult<Target extends object> = Target & {
771
+ /**
772
+ * Cleanup function to dispose of all reactive bindings created by organized().
773
+ * This is automatically called when the effect that created the organized binding is disposed.
774
+ */
775
+ [cleanup]: ScopedCallback;
776
+ };
777
+ /**
778
+ * Organizes a source object's properties into a target object using a callback function.
779
+ * This creates a reactive mapping between source properties and a target object,
780
+ * automatically handling property additions, updates, and removals.
781
+ *
782
+ * @template Source - The type of the source object
783
+ * @template Target - The type of the target object (defaults to Record<PropertyKey, any>)
784
+ *
785
+ * @param {Source} source - The source object to organize
786
+ * @param {OrganizedCallback<Source, Target>} apply - Callback function that defines how each source property is mapped to the target
787
+ * @param {Target} [baseTarget={}] - Optional base target object to use (will be made reactive if not already)
788
+ *
789
+ * @returns {OrganizedResult<Target>} The target object with cleanup capability
790
+ *
791
+ * @example
792
+ * // Organize user permissions into role-based access
793
+ * const user = reactive({ isAdmin: true, canEdit: false });
794
+ * const permissions = organized(
795
+ * user,
796
+ * (access, target) => {
797
+ * if (access.key === 'isAdmin') {
798
+ * target.hasFullAccess = access.value;
799
+ * }
800
+ * target[`can${access.key.charAt(0).toUpperCase() + access.key.slice(1)}`] = access.value;
801
+ * }
802
+ * );
803
+ *
804
+ * @example
805
+ * // Transform object structure with cleanup
806
+ * const source = reactive({ firstName: 'John', lastName: 'Doe' });
807
+ * const formatted = organized(
808
+ * source,
809
+ * (access, target) => {
810
+ * if (access.key === 'firstName' || access.key === 'lastName') {
811
+ * target.fullName = `${source.firstName} ${source.lastName}`.trim();
812
+ * }
813
+ * }
814
+ * );
815
+ *
816
+ * @example
817
+ * // Using with cleanup in a component
818
+ * effect(() => {
819
+ * const data = fetchData();
820
+ * const organizedData = organized(data, (access, target) => {
821
+ * // Transform data
822
+ * });
823
+ *
824
+ * // The cleanup will be called automatically when the effect is disposed
825
+ * return () => organizedData[cleanup]();
826
+ * });
827
+ */
828
+ declare function organized<Source extends Record<PropertyKey, any>, Target extends object = Record<PropertyKey, any>>(source: Source, apply: OrganizedCallback<Source, Target>, baseTarget?: Target): OrganizedResult<Target>;
829
+ /**
830
+ * Organizes a property on a target object
831
+ * Shortcut for defineProperty/delete with touched signal
832
+ * @param target - The target object
833
+ * @param property - The property to organize
834
+ * @param access - The access object
835
+ * @returns The property descriptor
836
+ */
837
+ declare function organize<T>(target: object, property: PropertyKey, access: {
838
+ get?(): T;
839
+ set?(value: T): boolean;
840
+ }): () => boolean;
841
+
842
+ /**
843
+ * Manually enable/disable the zone (for testing)
844
+ */
845
+ declare function setZoneEnabled(enabled: boolean): void;
846
+ /**
847
+ * Check if zone is currently hooked
848
+ */
849
+ declare function isZoneEnabled(): boolean;
850
+
851
+ /**
852
+ * Object containing internal reactive system state for debugging and profiling
853
+ */
854
+ declare const profileInfo: any;
350
855
 
351
- export { Reactive, ReactiveBase, ReactiveError, activeEffect, addBatchCleanup, atomic, cleanedBy, computed, effect, getState, immutables, invalidateComputed, isNonReactive, isReactive, profileInfo, reactive, options as reactiveOptions, trackEffect, unreactive, untracked, unwrap, watch };
352
- export type { Evolution, ScopedCallback };
856
+ export { ReactiveBase, ReactiveError, ReadOnlyError, Register, addBatchCleanup, atomic, batch, 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 };
857
+ export type { DependencyAccess, DependencyFunction, Evolution, Memoizable, ReactivityGraph, ScopedCallback };