@but212/atom-effect 0.3.2 → 0.4.0

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/dist/index.d.ts CHANGED
@@ -2,42 +2,22 @@
2
2
  * Async computation states for computed atoms
3
3
  */
4
4
  export declare const AsyncState: {
5
- IDLE: "idle";
6
- PENDING: "pending";
7
- RESOLVED: "resolved";
8
- REJECTED: "rejected";
5
+ readonly IDLE: "idle";
6
+ readonly PENDING: "pending";
7
+ readonly RESOLVED: "resolved";
8
+ readonly REJECTED: "rejected";
9
9
  };
10
10
 
11
- export declare type AsyncStateType = 'idle' | 'pending' | 'resolved' | 'rejected';
11
+ /** Type derived from AsyncState constant values */
12
+ export declare type AsyncStateType = (typeof AsyncState)[keyof typeof AsyncState];
12
13
 
13
14
  /**
14
- * Creates a new atom with the given initial value.
15
- *
16
- * @template T - The type of value stored in the atom
17
- * @param initialValue - The initial value of the atom
18
- * @param options - Optional configuration options
19
- * @returns A writable atom instance
20
- *
21
- * @example
22
- * ```ts
23
- * // Basic usage
24
- * const count = atom(0);
25
- *
26
- * // With sync option for immediate notifications
27
- * const syncCount = atom(0, { sync: true });
28
- *
29
- * // Reading and writing
30
- * console.log(count.value); // 0
31
- * count.value = 5;
32
- * console.log(count.peek()); // 5 (non-tracking read)
33
- * ```
15
+ * Creates a reactive atom holding mutable state.
16
+ * @param initialValue - Initial value
17
+ * @param options - { sync?: boolean } for immediate notifications
34
18
  */
35
19
  export declare function atom<T>(initialValue: T, options?: AtomOptions): WritableAtom<T>;
36
20
 
37
- /**
38
- * @fileoverview Error class hierarchy for atom-effect library
39
- * @description Structured error classes with cause tracking and recoverability flags
40
- */
41
21
  /**
42
22
  * Base error class for all atom-effect errors
43
23
  *
@@ -103,33 +83,10 @@ export declare interface AtomOptions {
103
83
  export declare function batch<T>(callback: () => T): T;
104
84
 
105
85
  /**
106
- * Creates a computed value that automatically tracks and reacts to dependencies
107
- *
108
- * Computed atoms are derived reactive state that:
109
- * - Automatically track dependencies accessed during computation
110
- * - Lazily recompute only when dependencies change (dirty checking)
111
- * - Support both synchronous and asynchronous computations
112
- * - Cache results until dependencies change (memoization)
113
- * - Use bit flags for efficient state management
114
- * - Provide async state tracking (idle/pending/resolved/rejected)
115
- *
116
- * @template T - The type of the computed value
117
- * @param fn - Computation function (can return T or Promise<T>)
118
- * @param options - Configuration options
119
- * @returns A readonly computed atom with automatic dependency tracking
120
- *
121
- * @example
122
- * ```ts
123
- * // Synchronous computed
124
- * const count = atom(0);
125
- * const doubled = computed(() => count.value * 2);
126
- *
127
- * // Asynchronous computed with default value
128
- * const userData = computed(
129
- * async () => fetch(`/api/user/${userId.value}`).then(r => r.json()),
130
- * { defaultValue: null }
131
- * );
132
- * ```
86
+ * Creates a computed value with automatic dependency tracking.
87
+ * Supports sync/async computations with caching and lazy evaluation.
88
+ * @param fn - Computation function (sync or async)
89
+ * @param options - { equal?, defaultValue?, onError?, lazy? }
133
90
  */
134
91
  export declare function computed<T>(fn: () => T, options?: ComputedOptions<T>): ComputedAtom<T>;
135
92
 
@@ -180,28 +137,8 @@ export declare const DEBUG_CONFIG: {
180
137
  };
181
138
 
182
139
  /**
183
- * Debug configuration instance with runtime utilities.
184
- *
185
- * Provides development-time features including:
186
- * - Circular dependency detection (direct and indirect)
187
- * - Large dependency graph warnings
188
- * - Debug metadata attachment for inspection
189
- *
190
- * @remarks
191
- * Most features are only active when `NODE_ENV === 'development'`
192
- * to avoid performance overhead in production builds.
193
- *
194
- * @example
195
- * ```typescript
196
- * // Check for circular dependencies
197
- * debug.checkCircular(dependencyAtom, computedAtom);
198
- *
199
- * // Warn about potential issues
200
- * debug.warn(count > 100, 'Large dependency count detected');
201
- *
202
- * // Attach debug info to a reactive object
203
- * debug.attachDebugInfo(atom, 'atom', 42);
204
- * ```
140
+ * Debug utilities for development-time dependency tracking and circular detection.
141
+ * Most features only active when `NODE_ENV === 'development'`.
205
142
  */
206
143
  export declare const DEBUG_RUNTIME: DebugConfig;
207
144
 
@@ -213,10 +150,13 @@ export declare interface DebugConfig {
213
150
  maxDependencies: number;
214
151
  warnInfiniteLoop: boolean;
215
152
  warn(condition: boolean, message: string): void;
216
- checkCircular(dep: unknown, current: unknown, visited?: Set<unknown>): void;
153
+ /** Checks for circular dependencies between reactive nodes */
154
+ checkCircular(dep: Dependency, current: object): void;
217
155
  attachDebugInfo(obj: object, type: string, id: number): void;
218
- getDebugName(obj: unknown): string | undefined;
219
- getDebugType(obj: unknown): string | undefined;
156
+ /** Returns debug name if available (requires obj to have DEBUG_NAME symbol) */
157
+ getDebugName(obj: object | null | undefined): string | undefined;
158
+ /** Returns debug type if available (requires obj to have DEBUG_TYPE symbol) */
159
+ getDebugType(obj: object | null | undefined): string | undefined;
220
160
  }
221
161
 
222
162
  /**
@@ -255,65 +195,10 @@ export declare interface DependencyEntry<T extends object = Dependency> {
255
195
  }
256
196
 
257
197
  /**
258
- * Creates a reactive effect that automatically re-executes when its dependencies change.
259
- *
260
- * @param fn - The effect function to execute. May return a cleanup function
261
- * or a Promise that resolves to a cleanup function.
262
- * @param options - Configuration options for the effect
263
- * @param options.sync - If true, re-executes synchronously on dependency changes.
264
- * Defaults to false (scheduled/batched execution).
265
- * @param options.maxExecutionsPerSecond - Maximum executions per second before
266
- * infinite loop detection triggers.
267
- * Defaults to `SCHEDULER_CONFIG.MAX_EXECUTIONS_PER_SECOND`.
268
- * @param options.trackModifications - If true, tracks and warns about dependencies
269
- * that are both read and modified. Defaults to false.
270
- *
271
- * @returns An {@link EffectObject} with `run()`, `dispose()`, and state properties
272
- *
273
- * @throws {EffectError} If `fn` is not a function
274
- *
275
- * @remarks
276
- * Effects are the primary way to perform side effects in response to reactive
277
- * state changes. They automatically track which reactive values (atoms, computed)
278
- * are accessed during execution and re-run when those values change.
279
- *
280
- * The effect function may return a cleanup function that will be called before
281
- * the next execution or when the effect is disposed. This is useful for
282
- * cleaning up subscriptions, timers, or other resources.
283
- *
284
- * @example
285
- * Basic usage:
286
- * ```typescript
287
- * const counter = atom(0);
288
- *
289
- * const fx = effect(() => {
290
- * console.log('Counter:', counter.value);
291
- * });
292
- * // Logs: "Counter: 0"
293
- *
294
- * counter.value = 1;
295
- * // Logs: "Counter: 1"
296
- *
297
- * fx.dispose(); // Stop the effect
298
- * ```
299
- *
300
- * @example
301
- * With cleanup function:
302
- * ```typescript
303
- * const fx = effect(() => {
304
- * const timer = setInterval(() => console.log('tick'), 1000);
305
- * return () => clearInterval(timer); // Cleanup
306
- * });
307
- * ```
308
- *
309
- * @example
310
- * Synchronous execution:
311
- * ```typescript
312
- * const fx = effect(
313
- * () => console.log(counter.value),
314
- * { sync: true }
315
- * );
316
- * ```
198
+ * Creates a reactive effect that re-executes when dependencies change.
199
+ * @param fn - Effect function (may return cleanup function or Promise)
200
+ * @param options - { sync?: boolean, maxExecutionsPerSecond?: number, trackModifications?: boolean }
201
+ * @throws {EffectError} If fn is not a function
317
202
  */
318
203
  export declare function effect(fn: EffectFunction, options?: EffectOptions): EffectObject;
319
204
 
@@ -378,139 +263,32 @@ export declare interface ReadonlyAtom<T = unknown> {
378
263
  peek(): T;
379
264
  }
380
265
 
266
+ /**
267
+ * Scheduler for reactive updates with batching support.
268
+ * Uses epoch-based O(1) deduplication and double buffering.
269
+ */
381
270
  declare class Scheduler {
382
- /** Queue of callbacks waiting for microtask execution */
383
- /** Queue buffers for double buffering optimization */
384
271
  private queueA;
385
272
  private queueB;
386
- /** Currently active queue receiving new tasks */
387
273
  private queue;
388
274
  private queueSize;
389
- /** Epoch for O(1) deduplication */
390
275
  private _epoch;
391
- /** Whether the scheduler is currently processing the queue */
392
276
  private isProcessing;
393
- /** Whether batching is currently active */
394
277
  isBatching: boolean;
395
- /** Current nesting depth of batch operations */
396
278
  private batchDepth;
397
- /** Array of callbacks queued during batching */
398
279
  private batchQueue;
399
- /** Current size of the batch queue (for array reuse) */
400
280
  private batchQueueSize;
401
- /** Whether synchronous flush is in progress */
402
281
  private isFlushingSync;
403
- /** Maximum iterations allowed during flush to prevent infinite loops */
404
282
  private maxFlushIterations;
405
- /**
406
- * Gets the current phase of the scheduler.
407
- */
408
283
  get phase(): SchedulerPhase;
409
- /**
410
- * Schedules a callback for execution.
411
- *
412
- * If batching is active or a sync flush is in progress, the callback
413
- * is added to the batch queue. Otherwise, it's added to the main queue
414
- * and a flush is triggered via microtask.
415
- *
416
- * @param callback - The function to schedule for execution
417
- * @throws {SchedulerError} If callback is not a function
418
- *
419
- * @example
420
- * ```typescript
421
- * scheduler.schedule(() => {
422
- * // This runs in the next microtask (or sync if batching)
423
- * updateUI();
424
- * });
425
- * ```
426
- */
427
284
  schedule(callback: SchedulerJob): void;
428
- /**
429
- * Flushes the queue asynchronously via microtask.
430
- *
431
- * Executes all queued callbacks in a microtask, allowing the current
432
- * synchronous execution to complete first. Errors in individual
433
- * callbacks are caught and logged without interrupting others.
434
- *
435
- * @private
436
- * @remarks
437
- * This method is idempotent - calling it multiple times while
438
- * processing is active has no effect.
439
- */
440
285
  private flush;
441
- /**
442
- * Flushes all queued callbacks synchronously.
443
- *
444
- * This method is called when a batch ends. It processes all callbacks
445
- * in the batch queue and main queue synchronously, allowing callbacks
446
- * to schedule additional callbacks that are processed in the same flush.
447
- *
448
- * @private
449
- * @remarks
450
- * - Includes infinite loop protection via maxFlushIterations
451
- * - Errors in callbacks are caught and logged individually
452
- * - The isFlushingSync flag prevents re-entrancy issues
453
- */
454
286
  private flushSync;
455
- /**
456
- * Starts a new batch operation.
457
- *
458
- * While batching is active, all scheduled callbacks are deferred
459
- * until endBatch() is called. Batches can be nested - only the
460
- * outermost endBatch() triggers execution.
461
- *
462
- * @example
463
- * ```typescript
464
- * scheduler.startBatch();
465
- * // All updates here are deferred
466
- * atom1.value = 'a';
467
- * atom2.value = 'b';
468
- * scheduler.endBatch(); // Both updates processed together
469
- * ```
470
- */
471
287
  startBatch(): void;
472
- /**
473
- * Ends a batch operation.
474
- *
475
- * Decrements the batch depth counter. When depth reaches zero,
476
- * all queued callbacks are flushed synchronously and batching
477
- * is disabled.
478
- *
479
- * @remarks
480
- * Safe to call even if startBatch() wasn't called - depth is
481
- * clamped to zero minimum.
482
- *
483
- * @example
484
- * ```typescript
485
- * scheduler.startBatch();
486
- * try {
487
- * // ... batched operations
488
- * } finally {
489
- * scheduler.endBatch(); // Always end batch, even on error
490
- * }
491
- * ```
492
- */
493
288
  endBatch(): void;
494
- /**
495
- * Sets the maximum number of flush iterations allowed.
496
- *
497
- * This limit prevents infinite loops when reactive dependencies
498
- * form cycles. If exceeded, the queue is cleared and an error
499
- * is logged.
500
- *
501
- * @param max - Maximum iterations (must be at least 10)
502
- * @throws {SchedulerError} If max is less than 10
503
- *
504
- * @example
505
- * ```typescript
506
- * // Increase limit for complex dependency graphs
507
- * scheduler.setMaxFlushIterations(5000);
508
- * ```
509
- */
510
289
  setMaxFlushIterations(max: number): void;
511
290
  }
512
291
 
513
- /** Global scheduler instance for reactive updates */
514
292
  export declare const scheduler: Scheduler;
515
293
 
516
294
  /**
@@ -532,6 +310,10 @@ export declare const SCHEDULER_CONFIG: {
532
310
  * Increased from 1000 to 5000 based on evaluation report
533
311
  */
534
312
  readonly MAX_EXECUTIONS_PER_FLUSH: 5000;
313
+ /** Maximum iterations for synchronous flush loop to prevent infinite loops */
314
+ readonly MAX_FLUSH_ITERATIONS: 1000;
315
+ /** Minimum allowed value for max flush iterations */
316
+ readonly MIN_FLUSH_ITERATIONS: 10;
535
317
  };
536
318
 
537
319
  /**
@@ -553,34 +335,6 @@ declare type SchedulerJob = (() => void) & {
553
335
  _nextEpoch?: number;
554
336
  };
555
337
 
556
- /**
557
- * Scheduler for managing reactive updates and batching operations.
558
- *
559
- * The Scheduler is responsible for coordinating when reactive computations
560
- * are executed. It supports both immediate (microtask) execution and
561
- * batched synchronous execution for optimal performance.
562
- *
563
- * Key features:
564
- * - Deduplication of callbacks via Set
565
- * - Nested batch support with depth tracking
566
- * - Infinite loop protection with configurable iteration limit
567
- * - Error isolation to prevent one callback from breaking others
568
- *
569
- * @example
570
- * ```typescript
571
- * // Schedule a callback for microtask execution
572
- * scheduler.schedule(() => console.log('Updated!'));
573
- *
574
- * // Batch multiple updates
575
- * scheduler.startBatch();
576
- * scheduler.schedule(() => console.log('Update 1'));
577
- * scheduler.schedule(() => console.log('Update 2'));
578
- * scheduler.endBatch(); // Both execute synchronously here
579
- * ```
580
- */
581
- /**
582
- * Phases of the scheduler execution cycle.
583
- */
584
338
  declare enum SchedulerPhase {
585
339
  IDLE = 0,
586
340
  BATCHING = 1,