@but212/atom-effect 0.3.3 → 0.5.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,25 +2,35 @@
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
15
  * Creates a reactive atom holding mutable state.
15
- * @param initialValue - Initial value
16
- * @param options - { sync?: boolean } for immediate notifications
16
+ *
17
+ * Atoms are the building blocks of reactive state. When an atom's value changes,
18
+ * any effects or computed atoms that depend on it will be automatically re-executed.
19
+ *
20
+ * @param initialValue - The initial value of the atom.
21
+ * @param options - Configuration options.
22
+ * @param options.sync - If true, notifications are delivered synchronously when the value changes.
23
+ * @returns A writable atom object.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const count = atom(0);
28
+ * count.value = 1; // Notifies subscribers
29
+ * console.log(count.value); // 1
30
+ * ```
17
31
  */
18
32
  export declare function atom<T>(initialValue: T, options?: AtomOptions): WritableAtom<T>;
19
33
 
20
- /**
21
- * @fileoverview Error class hierarchy for atom-effect library
22
- * @description Structured error classes with cause tracking and recoverability flags
23
- */
24
34
  /**
25
35
  * Base error class for all atom-effect errors
26
36
  *
@@ -50,7 +60,9 @@ export declare class AtomError extends Error {
50
60
  constructor(message: string, cause?: Error | null, recoverable?: boolean);
51
61
  }
52
62
 
63
+ /** Configuration options for creating an atom. */
53
64
  export declare interface AtomOptions {
65
+ /** If true, the atom will notify its subscribers synchronously when its value changes. */
54
66
  sync?: boolean;
55
67
  }
56
68
 
@@ -65,7 +77,7 @@ export declare interface AtomOptions {
65
77
  * @param callback - The function containing batched updates
66
78
  * @returns The result of the callback function
67
79
  * @throws {AtomError} If the callback is not a function
68
- * @throws {AtomError} If an error occurs during batch execution
80
+ * @throws Propagates any error thrown by the callback function
69
81
  *
70
82
  * @example
71
83
  * ```typescript
@@ -85,6 +97,15 @@ export declare interface AtomOptions {
85
97
  */
86
98
  export declare function batch<T>(callback: () => T): T;
87
99
 
100
+ /**
101
+ * Generic Branded Type helper.
102
+ * T: The base type (e.g., number, string)
103
+ * Brand: The unique brand tag
104
+ */
105
+ export declare type Branded<T, Brand> = T & {
106
+ readonly __brand: Brand;
107
+ };
108
+
88
109
  /**
89
110
  * Creates a computed value with automatic dependency tracking.
90
111
  * Supports sync/async computations with caching and lazy evaluation.
@@ -97,13 +118,21 @@ export declare function computed<T>(fn: () => Promise<T>, options: ComputedOptio
97
118
  defaultValue: T;
98
119
  }): ComputedAtom<T>;
99
120
 
121
+ /** Represents a reactive atom whose value is derived from other reactive state. */
100
122
  export declare interface ComputedAtom<T = unknown> extends ReadonlyAtom<T> {
123
+ /** Current asynchronous state of the computation. */
101
124
  readonly state: AsyncStateType;
125
+ /** true if the last computation attempt failed. */
102
126
  readonly hasError: boolean;
127
+ /** The error object from the last failed computation, if any. */
103
128
  readonly lastError: Error | null;
129
+ /** true if an asynchronous computation is currently in progress. */
104
130
  readonly isPending: boolean;
131
+ /** true if the computation has successfully completed and has a value. */
105
132
  readonly isResolved: boolean;
133
+ /** Manually invalidates the cached value, forcing recomputation on next access. */
106
134
  invalidate(): void;
135
+ /** Disposed of the computed atom and its subscriptions. */
107
136
  dispose(): void;
108
137
  }
109
138
 
@@ -122,10 +151,15 @@ export declare class ComputedError extends AtomError {
122
151
  constructor(message: string, cause?: Error | null);
123
152
  }
124
153
 
154
+ /** Configuration options for creating a computed atom. */
125
155
  export declare interface ComputedOptions<T = unknown> {
156
+ /** Optional custom equality check for values. Defaults to `Object.is`. */
126
157
  equal?: (a: T, b: T) => boolean;
158
+ /** Initial value to return while an async computation is pending. */
127
159
  defaultValue?: T;
160
+ /** If true, the computation is deferred until the value is first accessed. */
128
161
  lazy?: boolean;
162
+ /** Optional error handler for computation failures. */
129
163
  onError?: (error: Error) => void;
130
164
  }
131
165
 
@@ -153,17 +187,20 @@ export declare interface DebugConfig {
153
187
  maxDependencies: number;
154
188
  warnInfiniteLoop: boolean;
155
189
  warn(condition: boolean, message: string): void;
156
- checkCircular(dep: unknown, current: unknown, visited?: Set<unknown>): void;
190
+ /** Checks for circular dependencies between reactive nodes */
191
+ checkCircular(dep: Dependency, current: object): void;
157
192
  attachDebugInfo(obj: object, type: string, id: number): void;
158
- getDebugName(obj: unknown): string | undefined;
159
- getDebugType(obj: unknown): string | undefined;
193
+ /** Returns debug name if available (requires obj to have DEBUG_NAME symbol) */
194
+ getDebugName(obj: object | null | undefined): string | undefined;
195
+ /** Returns debug type if available (requires obj to have DEBUG_TYPE symbol) */
196
+ getDebugType(obj: object | null | undefined): string | undefined;
160
197
  }
161
198
 
162
199
  /**
163
200
  * Interface for subscribable dependencies
164
201
  */
165
202
  export declare interface Dependency {
166
- readonly id: number;
203
+ readonly id: DependencyId;
167
204
  version: number;
168
205
  /**
169
206
  * Last epoch seen by this dependency (used for invalidation)
@@ -195,10 +232,33 @@ export declare interface DependencyEntry<T extends object = Dependency> {
195
232
  }
196
233
 
197
234
  /**
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
235
+ * Unique identifier for reactive dependencies (Atoms, Computed, Effects).
236
+ * Base type is number.
237
+ */
238
+ export declare type DependencyId = Branded<number, 'DependencyId'>;
239
+
240
+ /**
241
+ * Creates a reactive effect that re-executes when its dependencies change.
242
+ *
243
+ * An effect automatically tracks any reactive state (atoms, computed) accessed during its execution.
244
+ * When those dependencies change, the effect is scheduled for re-execution.
245
+ *
246
+ * @param fn - The effect function to execute. Can return a cleanup function or a Promise that resolves to one.
247
+ * @param options - Configuration options for the effect.
248
+ * @param options.sync - If true, the effect runs synchronously when dependencies change. Defaults to false (scheduled).
249
+ * @param options.maxExecutionsPerSecond - Rate limiting for the effect.
250
+ * @param options.trackModifications - If true, warns when an effect modifies its own dependencies.
251
+ * @returns An object representing the effect with `run()` and `dispose()` methods.
252
+ * @throws {EffectError} If `fn` is not a function.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * const count = atom(0);
257
+ * const stop = effect(() => {
258
+ * console.log('Count changed:', count.value);
259
+ * return () => console.log('Cleaning up...');
260
+ * });
261
+ * ```
202
262
  */
203
263
  export declare function effect(fn: EffectFunction, options?: EffectOptions): EffectObject;
204
264
 
@@ -217,26 +277,43 @@ export declare class EffectError extends AtomError {
217
277
  constructor(message: string, cause?: Error | null);
218
278
  }
219
279
 
280
+ /**
281
+ * A function to be executed by an effect.
282
+ * Can optionally return a cleanup function or a Promise that resolves to one.
283
+ */
220
284
  export declare type EffectFunction = () => void | (() => void) | Promise<undefined | (() => void)>;
221
285
 
286
+ /** Represents a running effect instance. */
222
287
  export declare interface EffectObject {
288
+ /** Stops the effect and unsubscribes from all dependencies. */
223
289
  dispose(): void;
290
+ /** Manually triggers an execution of the effect. */
224
291
  run(): void;
292
+ /** true if the effect has been disposed. */
225
293
  readonly isDisposed: boolean;
294
+ /** Number of times the effect has executed. */
226
295
  readonly executionCount: number;
227
296
  }
228
297
 
298
+ /** Configuration options for creating an effect. */
229
299
  export declare interface EffectOptions {
300
+ /** If true, the effect runs synchronously whenever its dependencies change. */
230
301
  sync?: boolean;
302
+ /** Maximum number of executions allowed per second for rate limiting. */
231
303
  maxExecutionsPerSecond?: number;
304
+ /** Maximum number of executions allowed per scheduler flush for loop detection. */
232
305
  maxExecutionsPerFlush?: number;
306
+ /** If true, enables detection and warning for effects that modify their own dependencies. */
233
307
  trackModifications?: boolean;
234
308
  }
235
309
 
310
+ /** Checks if the given object is a ReadonlyAtom. */
236
311
  export declare function isAtom(obj: unknown): obj is ReadonlyAtom;
237
312
 
313
+ /** Checks if the given object is a ComputedAtom. */
238
314
  export declare function isComputed(obj: unknown): obj is ComputedAtom;
239
315
 
316
+ /** Checks if the given object is an EffectObject. */
240
317
  export declare function isEffect(obj: unknown): obj is EffectObject;
241
318
 
242
319
  /**
@@ -257,15 +334,24 @@ export declare interface Poolable {
257
334
  reset(): void;
258
335
  }
259
336
 
337
+ /** Represents a read-only reactive atom. */
260
338
  export declare interface ReadonlyAtom<T = unknown> {
339
+ /** The current value of the atom. Accessing this tracks it as a dependency. */
261
340
  readonly value: T;
341
+ /**
342
+ * Subscribes a listener function to changes in the atom's value.
343
+ * @param listener - Callback receiving both the new and old values.
344
+ * @returns An unsubscribe function.
345
+ */
262
346
  subscribe(listener: (newValue?: T, oldValue?: T) => void): () => void;
347
+ /** Returns the current value without registering it as a dependency. */
263
348
  peek(): T;
264
349
  }
265
350
 
266
351
  /**
267
- * Scheduler for reactive updates with batching support.
268
- * Uses epoch-based O(1) deduplication and double buffering.
352
+ * Scheduler for reactive updates.
353
+ * Manages the execution of effects and computed updates using batching and double-buffering.
354
+ * Supports both asynchronous (microtask-based) and synchronous (manual or batch-end) flushing.
269
355
  */
270
356
  declare class Scheduler {
271
357
  private queueA;
@@ -281,11 +367,31 @@ declare class Scheduler {
281
367
  private isFlushingSync;
282
368
  private maxFlushIterations;
283
369
  get phase(): SchedulerPhase;
370
+ /**
371
+ * Schedules a task for execution.
372
+ * Tasks are deduplicated within the same flush cycle using epoch tracking.
373
+ * @param callback - The function to execute.
374
+ * @throws {SchedulerError} If the callback is not a function.
375
+ */
284
376
  schedule(callback: SchedulerJob): void;
285
377
  private flush;
286
378
  private flushSync;
379
+ private _mergeBatchQueue;
380
+ private _drainQueue;
381
+ private _processCurrentQueue;
382
+ private _handleFlushOverflow;
383
+ private _processJobs;
384
+ /** Starts a new batch of updates. Updates will be deferred until endBatch is called. */
287
385
  startBatch(): void;
386
+ /**
387
+ * Ends the current batch. If the batch depth reaches zero, all pending updates are flushed synchronously.
388
+ */
288
389
  endBatch(): void;
390
+ /**
391
+ * Configures the maximum number of iterations allowed during a synchronous flush.
392
+ * Used to prevent infinite loops.
393
+ * @param max - Maximum iterations count.
394
+ */
289
395
  setMaxFlushIterations(max: number): void;
290
396
  }
291
397
 
@@ -310,6 +416,10 @@ export declare const SCHEDULER_CONFIG: {
310
416
  * Increased from 1000 to 5000 based on evaluation report
311
417
  */
312
418
  readonly MAX_EXECUTIONS_PER_FLUSH: 5000;
419
+ /** Maximum iterations for synchronous flush loop to prevent infinite loops */
420
+ readonly MAX_FLUSH_ITERATIONS: 1000;
421
+ /** Minimum allowed value for max flush iterations */
422
+ readonly MIN_FLUSH_ITERATIONS: 10;
313
423
  };
314
424
 
315
425
  /**
@@ -327,9 +437,10 @@ export declare class SchedulerError extends AtomError {
327
437
  constructor(message: string, cause?: Error | null);
328
438
  }
329
439
 
330
- declare type SchedulerJob = (() => void) & {
440
+ declare interface SchedulerJob {
441
+ (): void;
331
442
  _nextEpoch?: number;
332
- };
443
+ }
333
444
 
334
445
  declare enum SchedulerPhase {
335
446
  IDLE = 0,
@@ -360,7 +471,7 @@ export declare type TransformFunction<T, U> = (value: T) => U;
360
471
  * @param fn - The function to execute without tracking
361
472
  * @returns The result of the executed function
362
473
  * @throws {AtomError} If the callback is not a function
363
- * @throws {AtomError} If an error occurs during execution
474
+ * @throws Propagates any error thrown by the callback function
364
475
  *
365
476
  * @example
366
477
  * ```typescript
@@ -374,8 +485,11 @@ export declare type TransformFunction<T, U> = (value: T) => U;
374
485
  */
375
486
  export declare function untracked<T>(fn: () => T): T;
376
487
 
488
+ /** Represents a writable reactive atom. */
377
489
  export declare interface WritableAtom<T = unknown> extends ReadonlyAtom<T> {
490
+ /** The current value of the atom. Setting this will trigger notifications if the value changes. */
378
491
  value: T;
492
+ /** Disposes of the atom and releases associated resources. */
379
493
  dispose(): void;
380
494
  }
381
495