@but212/atom-effect 0.28.0 → 0.30.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
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Async operation states.
2
+ * Async operation states for public API and high-level checks.
3
3
  */
4
- export declare const AsyncState: {
5
- readonly IDLE: "idle";
6
- readonly PENDING: "pending";
7
- readonly RESOLVED: "resolved";
8
- readonly REJECTED: "rejected";
9
- };
4
+ export declare const AsyncState: Readonly<{
5
+ IDLE: "idle";
6
+ PENDING: "pending";
7
+ RESOLVED: "resolved";
8
+ REJECTED: "rejected";
9
+ }>;
10
10
 
11
11
  /**
12
12
  * Async state values.
@@ -22,13 +22,41 @@ export declare type AsyncStateType = (typeof AsyncState)[keyof typeof AsyncState
22
22
  export declare function atom<T>(initialValue: T, options?: AtomOptions): WritableAtom<T>;
23
23
 
24
24
  /**
25
- * Base error class.
25
+ * Base error class for the Atom system.
26
+ * Designed for high performance, traceability, and cycle protection.
26
27
  */
27
28
  export declare class AtomError extends Error {
28
- cause: Error | null;
29
- recoverable: boolean;
29
+ readonly cause: unknown;
30
+ readonly recoverable: boolean;
31
+ readonly code?: string | undefined;
32
+ readonly name: string;
33
+ constructor(message: string, cause?: unknown, recoverable?: boolean, code?: string | undefined);
34
+ /**
35
+ * Returns the entire error chain as an array.
36
+ * Includes the circular node if a cycle is detected.
37
+ */
38
+ getChain(): Array<AtomError | Error | unknown>;
39
+ /**
40
+ * Serializes the error to a structured object for logging.
41
+ * Protected against circular references.
42
+ */
43
+ toJSON(seen?: Set<unknown>): AtomErrorJSON;
44
+ /**
45
+ * Internal helper to format wrapped messages consistently.
46
+ */
47
+ static format(source: string, context: string, message: string): string;
48
+ }
49
+
50
+ /**
51
+ * Structured JSON representation of an AtomError.
52
+ */
53
+ declare interface AtomErrorJSON {
30
54
  name: string;
31
- constructor(message: string, cause?: Error | null, recoverable?: boolean);
55
+ message: string;
56
+ code?: string | undefined;
57
+ recoverable: boolean;
58
+ stack?: string | undefined;
59
+ cause?: unknown | undefined;
32
60
  }
33
61
 
34
62
  /**
@@ -45,19 +73,31 @@ export declare function atomLens<T extends object, P extends Paths<T>>(atom: Wri
45
73
  /**
46
74
  * Atom options.
47
75
  */
48
- export declare interface AtomOptions {
76
+ export declare interface AtomOptions<T = unknown> {
77
+ /** Optional name for debugging. */
78
+ name?: string;
49
79
  /** If true, subscribers are notified synchronously. Default: false (microtask scheduled). */
50
80
  sync?: boolean;
81
+ /** Equality check. */
82
+ equal?: (a: T, b: T) => boolean;
51
83
  }
52
84
 
53
85
  /**
54
- * Batches updates.
86
+ * Groups multiple state updates into a single batch, delaying effects and computations
87
+ * until the batch is closed.
55
88
  *
56
- * @param fn - Batch function.
57
- * @returns - Result of `fn`.
89
+ * @param fn - The function containing state updates.
90
+ * @returns The result of the function execution.
91
+ * @throws {TypeError} If fn is not a function.
58
92
  */
59
93
  export declare function batch<T>(fn: () => T): T;
60
94
 
95
+ /**
96
+ * Global brand symbol for all reactive primitives.
97
+ * Uses a bitwise mask for high-performance type identification.
98
+ */
99
+ declare const BRAND: unique symbol;
100
+
61
101
  /**
62
102
  * Composes an existing lens with a sub-path to create a deeper lens.
63
103
  */
@@ -77,7 +117,8 @@ export declare function computed<T>(fn: () => Promise<T>, options: ComputedOptio
77
117
  /**
78
118
  * Computed atom interface.
79
119
  */
80
- export declare interface ComputedAtom<T = unknown> extends ReadonlyAtom<T>, Disposable {
120
+ export declare interface ComputedAtom<T = unknown> extends ReadonlyAtom<T>, Disposable_2 {
121
+ /* Excluded from this release type: [BRAND] */
81
122
  readonly state: AsyncStateType;
82
123
  readonly hasError: boolean;
83
124
  readonly lastError: Error | null;
@@ -89,18 +130,20 @@ export declare interface ComputedAtom<T = unknown> extends ReadonlyAtom<T>, Disp
89
130
  /** Invalidates atom. */
90
131
  invalidate(): void;
91
132
  dispose(): void;
133
+ [Symbol.dispose](): void;
92
134
  }
93
135
 
94
- /** Computed error. */
136
+ /** Thrown when a computation fails. */
95
137
  export declare class ComputedError extends AtomError {
96
- name: string;
97
- constructor(message: string, cause?: Error | null);
138
+ readonly name = "ComputedError";
98
139
  }
99
140
 
100
141
  /**
101
142
  * Computed options.
102
143
  */
103
144
  export declare interface ComputedOptions<T = unknown> {
145
+ /** Optional name for debugging. */
146
+ name?: string;
104
147
  /** Equality check. */
105
148
  equal?: (a: T, b: T) => boolean;
106
149
  /** Initial value. */
@@ -109,40 +152,42 @@ export declare interface ComputedOptions<T = unknown> {
109
152
  lazy?: boolean;
110
153
  /** Error handler. */
111
154
  onError?: (error: Error) => void;
112
- /** Maximum number of async retries before giving up (default: 3). */
113
- maxAsyncRetries?: number;
114
155
  }
115
156
 
116
157
  /**
117
158
  * Debugging thresholds.
118
159
  */
119
- export declare const DEBUG_CONFIG: {
120
- readonly WARN_INFINITE_LOOP: true;
121
- readonly EFFECT_FREQUENCY_WINDOW: 1000;
122
- };
160
+ export declare const DEBUG_CONFIG: Readonly<{
161
+ WARN_INFINITE_LOOP: true;
162
+ EFFECT_FREQUENCY_WINDOW: 1000;
163
+ LOOP_THRESHOLD: 100;
164
+ }>;
123
165
 
124
- /**
125
- * Global debug controller singleton.
126
- */
127
- export declare const DEBUG_RUNTIME: DebugConfig;
128
-
129
- export declare interface DebugConfig {
166
+ declare interface DebugConfig {
130
167
  enabled: boolean;
131
168
  warnInfiniteLoop: boolean;
132
169
  warn(condition: boolean, message: string): void;
133
- attachDebugInfo(obj: object, type: string, id: number): void;
170
+ attachDebugInfo(obj: object, type: string, id: number, customName?: string): void;
134
171
  getDebugName(obj: object | null | undefined): string | undefined;
135
172
  getDebugType(obj: object | null | undefined): string | undefined;
173
+ trackUpdate(id: DependencyId, name?: string): void;
174
+ registerNode(node: object & {
175
+ id: DependencyId;
176
+ }): void;
177
+ dumpGraph(): Record<string, unknown>[];
136
178
  }
137
179
 
138
180
  /**
139
181
  * Dependency interface.
182
+ * Core contract for reactive nodes. All properties are required to ensure
183
+ * high-performance access within the engine.
140
184
  *
141
185
  * @remarks
142
186
  * Internal fields (`version`, `flags`, `_lastSeenEpoch`) are part of the
143
187
  * engine contract between reactive nodes and must not be mutated externally.
144
188
  */
145
- export declare interface Dependency {
189
+ export declare interface Dependency<T = unknown> {
190
+ /* Excluded from this release type: [BRAND] */
146
191
  readonly id: DependencyId;
147
192
  /* Excluded from this release type: version */
148
193
  /* Excluded from this release type: flags */
@@ -154,17 +199,36 @@ export declare interface Dependency {
154
199
  * The listener may optionally receive the new and previous values.
155
200
  * @param listener - A callback or Subscriber object.
156
201
  */
157
- subscribe(listener: ((newValue?: unknown, oldValue?: unknown) => void) | Subscriber): () => void;
158
- /** Peek hook. */
159
- peek?(): unknown;
160
- /** Value accessor. */
161
- value?: unknown;
202
+ subscribe(listener: ((newValue?: T, oldValue?: T) => void) | Subscriber): () => void;
203
+ /**
204
+ * Non-reactive read of the current value.
205
+ */
206
+ peek(): T;
207
+ /**
208
+ * Current value accessor.
209
+ */
210
+ readonly value: T;
162
211
  }
163
212
 
164
213
  /**
165
214
  * Dependency ID.
166
215
  */
167
- export declare type DependencyId = number;
216
+ declare type DependencyId = number;
217
+
218
+ /**
219
+ * Custom Disposable interface for explicit resource management.
220
+ */
221
+ declare interface Disposable_2 {
222
+ /**
223
+ * Cleans up the object and releases resources.
224
+ */
225
+ dispose(): void;
226
+ /**
227
+ * Support for explicit resource management (TS 5.2+).
228
+ */
229
+ [Symbol.dispose](): void;
230
+ }
231
+ export { Disposable_2 as Disposable }
168
232
 
169
233
  /**
170
234
  * Creates and starts an effect.
@@ -175,19 +239,28 @@ export declare type DependencyId = number;
175
239
  */
176
240
  export declare function effect(fn: EffectFunction, options?: EffectOptions): EffectObject;
177
241
 
178
- /** Effect error. */
242
+ /**
243
+ * Effect cleanup function.
244
+ */
245
+ export declare type EffectCleanup = () => void;
246
+
247
+ /** Thrown when an effect execution or cleanup fails. */
179
248
  export declare class EffectError extends AtomError {
180
- name: string;
181
- constructor(message: string, cause?: Error | null);
249
+ readonly name = "EffectError";
250
+ constructor(message: string, cause?: unknown, recoverable?: boolean, code?: string);
182
251
  }
183
252
 
184
253
  /**
185
- * Effect return type.
254
+ * Effect function type.
255
+ * Sync effects can return a cleanup function.
256
+ * Async effects can return a promise that resolves to a cleanup function or void.
186
257
  */
187
- export declare type EffectFunction = () => void | (() => void) | Promise<undefined | (() => void)>;
258
+ export declare type EffectFunction = () => (void | EffectCleanup) | Promise<void | EffectCleanup>;
188
259
 
189
- export declare interface EffectObject extends Disposable {
260
+ export declare interface EffectObject extends Disposable_2 {
261
+ /* Excluded from this release type: [BRAND] */
190
262
  dispose(): void;
263
+ [Symbol.dispose](): void;
191
264
  run(): void;
192
265
  readonly isDisposed: boolean;
193
266
  readonly executionCount: number;
@@ -207,10 +280,11 @@ export declare interface EffectOptions {
207
280
  */
208
281
  export declare function getPathValue(source: unknown, parts: string[]): unknown;
209
282
 
283
+ /** Global scheduler instance. */
284
+ export declare const globalScheduler: Scheduler_2;
285
+
210
286
  /**
211
287
  * Readonly atom check.
212
- *
213
- * @param obj - Object to check.
214
288
  */
215
289
  export declare function isAtom(obj: unknown): obj is ReadonlyAtom;
216
290
 
@@ -230,46 +304,33 @@ export declare function isEffect(obj: unknown): obj is EffectObject;
230
304
  export declare const lensFor: <T extends object>(atom: WritableAtom<T>) => <P extends Paths<T>>(path: P) => WritableAtom<PathValue<T, P>>;
231
305
 
232
306
  /** Max recursion depth for dot-paths. */
233
- export declare type MaxDepth = 8;
307
+ declare type MaxDepth = 8;
234
308
 
235
309
  /**
236
310
  * Generates a union of all possible dot-separated paths for a given type T.
311
+ * Excludes prototype methods and stops at TerminalTypes.
237
312
  *
238
313
  * Used for `atomLens` to provide IDE autocomplete and type safety when
239
314
  * zooming into deeply nested reactive objects.
240
- *
241
- * @example
242
- * type User = { profile: { name: string } };
243
- * type P = Paths<User>; // "profile" | "profile.name"
244
315
  */
245
- export declare type Paths<T, D extends unknown[] = []> = D['length'] extends MaxDepth ? never : T extends object ? {
246
- [K in keyof T & (string | number)]-?: `${K}` | (T[K] extends object ? `${K}.${Paths<T[K], [...D, 1]>}` : never);
316
+ export declare type Paths<T, D extends unknown[] = []> = D['length'] extends MaxDepth ? never : T extends TerminalTypes ? never : T extends object ? {
317
+ [K in keyof T & (string | number)]: T[K] extends Function ? never : NonNullable<T[K]> extends object ? `${K}` | `${K}.${Paths<NonNullable<T[K]>, [...D, 1]>}` : `${K}`;
247
318
  }[keyof T & (string | number)] : never;
248
319
 
249
320
  /**
250
321
  * Resolves the type of a value at a specific dot-path P within type T.
322
+ * Uses NonNullable to correctly handle optional (?) or nullable properties.
251
323
  *
252
324
  * Works in tandem with `Paths<T>` to ensure that lensed atoms have
253
325
  * the correct inferred type for the member they point to.
254
326
  */
255
- export declare type PathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? StringKeyToNumber<K> extends keyof T ? PathValue<T[StringKeyToNumber<K> & keyof T], Rest> : never : StringKeyToNumber<P> extends keyof T ? T[StringKeyToNumber<P> & keyof T] : never;
256
-
257
- export declare interface PoolStats {
258
- acquired: number;
259
- released: number;
260
- rejected: {
261
- frozen: number;
262
- tooLarge: number;
263
- poolFull: number;
264
- };
265
- leaked: number;
266
- poolSize: number;
267
- }
327
+ export declare type PathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? StringKeyToNumber<K> extends keyof NonNullable<T> ? PathValue<NonNullable<NonNullable<T>[StringKeyToNumber<K> & keyof NonNullable<T>]>, Rest> : never : StringKeyToNumber<P> extends keyof NonNullable<T> ? NonNullable<T>[StringKeyToNumber<P> & keyof NonNullable<T>] : never;
268
328
 
269
329
  /**
270
330
  * Readonly atom interface.
271
331
  */
272
332
  export declare interface ReadonlyAtom<T = unknown> {
333
+ /* Excluded from this release type: [BRAND] */
273
334
  /** The current value of the atom. */
274
335
  readonly value: T;
275
336
  /**
@@ -289,85 +350,125 @@ export declare interface ReadonlyAtom<T = unknown> {
289
350
  }
290
351
 
291
352
  /**
292
- * Scheduler implementation.
353
+ * The global debug singleton instance.
354
+ * Automatically switches between development and production implementations
355
+ * based on the environment configuration (IS_DEV).
356
+ *
357
+ * In production, this becomes a lightweight object with empty methods,
358
+ * allowing engines to inline or ignore calls, effectively providing zero overhead.
359
+ *
360
+ * @public
361
+ */
362
+ export declare const runtimeDebug: DebugConfig;
363
+
364
+ /**
365
+ * Core Scheduler that manages asynchronous and synchronous task execution.
366
+ *
367
+ * Features:
368
+ * - Double buffering for stable queue processing.
369
+ * - Automatic job deduplication via Epoch tagging.
370
+ * - Nested batching support with automatic coalescence.
371
+ * - Microsecond-level scheduling via queueMicrotask.
293
372
  */
294
373
  declare class Scheduler_2 {
295
- /** Queue buffer */
296
- _queueBuffer: [SchedulerJob[], SchedulerJob[]];
297
- _bufferIndex: number;
298
- _size: number;
299
- /** Epoch counter */
300
- _epoch: number;
301
- /** State flags */
302
- _isProcessing: boolean;
303
- _isFlushingSync: boolean;
304
- /** Batching state */
305
- _batchDepth: number;
306
- _batchQueue: SchedulerJob[];
307
- _batchQueueSize: number;
308
- /** Config */
309
- _maxFlushIterations: number;
310
- /** Overflow callback */
374
+ /** Double buffer to allow scheduling new jobs while processing the current queue. */
375
+ private _queueBuffer;
376
+ /** Pointer to the currently active buffer for ingestion. */
377
+ private _bufferIndex;
378
+ /** Current size of the active ingestion buffer. */
379
+ private _size;
380
+ /** Current internal epoch for job tagging. */
381
+ private _epoch;
382
+ /** Flag indicating the scheduler is currently draining a microtask loop. */
383
+ private _isProcessing;
384
+ /** Flag indicating a synchronous flush (batch end) is currently active. */
385
+ private _isFlushingSync;
386
+ /** Number of active nested batch contexts. */
387
+ private _batchDepth;
388
+ /** Temporary holding area for jobs scheduled during an active batch or sync flush. */
389
+ private _batchQueue;
390
+ /** Current number of jobs in the batch holding area. */
391
+ private _batchQueueSize;
392
+ /** Maximum allowed internal loop iterations before assuming an infinite loop. */
393
+ private _maxFlushIterations;
394
+ /** Optional callback fired when the scheduler drops jobs due to overflow. */
311
395
  onOverflow: ((droppedCount: number) => void) | null;
312
- /** Bound run loop for microtask */
313
396
  private readonly _boundRunLoop;
397
+ /** Returns the total number of pending jobs (active + batched). */
314
398
  get queueSize(): number;
399
+ /** Returns true if the scheduler is currently within a `batch()` scope. */
315
400
  get isBatching(): boolean;
316
401
  /**
317
- * Schedules job.
402
+ * Schedules a job for execution.
403
+ * Jobs are deduplicated based on the current epoch; if the same job is scheduled twice
404
+ * in the same epoch, the second call is ignored.
405
+ *
406
+ * @param callback - The task to be executed.
318
407
  */
319
408
  schedule(callback: SchedulerJob): void;
409
+ /** Initiates an asynchronous flush via microtask. */
410
+ private _flush;
411
+ /** Internal microtask execution loop. */
412
+ private _runLoop;
413
+ /** Internal synchronous flush typically triggered at the end of a batch. */
414
+ _flushSync(): void;
320
415
  /**
321
- * Triggers flush.
416
+ * Merges the temporal batch queue into the main active buffer.
417
+ * Increments the epoch to allow previously executed jobs to be re-scheduled if needed.
322
418
  */
323
- _flush(): void;
419
+ private _mergeBatchQueue;
324
420
  /**
325
- * Scheduler loop.
421
+ * Continuous loop that drains both main and batch queues.
422
+ * Processes until all queues are empty or max iterations reached.
326
423
  */
327
- private _runLoop;
328
- _flushSync(): void;
329
- _mergeBatchQueue(): void;
330
- _drainQueue(): void;
331
- _processQueue(): void;
424
+ private _drainQueue;
425
+ /** Executes all jobs currently in the primary buffer and swaps buffers. */
426
+ private _processQueue;
427
+ /** Resets the scheduler state on infinite loop detection and notifies via onOverflow. */
332
428
  private _handleFlushOverflow;
429
+ /** Enters a new batching depth. */
333
430
  startBatch(): void;
431
+ /**
432
+ * Decrements batching depth. If depth reaches 0, triggers a synchronous flush
433
+ * to apply all coherent updates collected during the batch.
434
+ */
334
435
  endBatch(): void;
436
+ /** Configures the maximum safety iterations for the flush loop. */
335
437
  setMaxFlushIterations(max: number): void;
336
438
  }
337
439
 
338
- declare const scheduler_2: Scheduler_2;
339
- export { scheduler_2 as scheduler }
340
-
341
440
  /**
342
441
  * Scheduler configuration.
343
442
  */
344
- export declare const SCHEDULER_CONFIG: {
345
- readonly MAX_EXECUTIONS_PER_SECOND: 1000;
346
- readonly MAX_EXECUTIONS_PER_EFFECT: 100;
347
- readonly MAX_EXECUTIONS_PER_FLUSH: 10000;
348
- readonly MAX_FLUSH_ITERATIONS: 1000;
349
- readonly MIN_FLUSH_ITERATIONS: 10;
350
- readonly BATCH_QUEUE_SHRINK_THRESHOLD: 1000;
351
- };
352
-
353
- /** Scheduler error. */
443
+ export declare const SCHEDULER_CONFIG: Readonly<{
444
+ MAX_EXECUTIONS_PER_SECOND: 1000;
445
+ MAX_EXECUTIONS_PER_EFFECT: 100;
446
+ MAX_EXECUTIONS_PER_FLUSH: 10000;
447
+ MAX_FLUSH_ITERATIONS: 1000;
448
+ MIN_FLUSH_ITERATIONS: 10;
449
+ BATCH_QUEUE_SHRINK_THRESHOLD: 1000;
450
+ }>;
451
+
452
+ /** Thrown by the execution engine or scheduler. */
354
453
  export declare class SchedulerError extends AtomError {
355
- name: string;
356
- constructor(message: string, cause?: Error | null);
454
+ readonly name = "SchedulerError";
455
+ constructor(message: string, cause?: unknown, recoverable?: boolean, code?: string);
357
456
  }
358
457
 
458
+ /** Union type representing any valid schedulable task. */
359
459
  declare type SchedulerJob = SchedulerJobFunction | SchedulerJobObject;
360
460
 
461
+ /** Represents a job that can be executed by the scheduler via a function interface. */
361
462
  declare interface SchedulerJobFunction {
362
463
  (): void;
363
- /** Next scheduled epoch */
364
- _nextEpoch?: number;
464
+ /** Internal tracking for deduplication within the same epoch. */
465
+ _nextEpoch?: number | undefined;
365
466
  }
366
467
 
367
468
  declare interface SchedulerJobObject {
368
469
  execute(): void;
369
- /** Next scheduled epoch */
370
- _nextEpoch?: number;
470
+ /** Internal tracking for deduplication within the same epoch. */
471
+ _nextEpoch?: number | undefined;
371
472
  }
372
473
 
373
474
  /**
@@ -377,14 +478,17 @@ declare interface SchedulerJobObject {
377
478
  export declare function setDeepValue(obj: unknown, keys: string[], index: number, value: unknown): unknown;
378
479
 
379
480
  /** Helper to convert numeric string to number for array indexing. */
380
- export declare type StringKeyToNumber<S extends string> = S extends `${infer N extends number}` ? N : S;
481
+ declare type StringKeyToNumber<S extends string> = S extends `${infer N extends number}` ? N : S;
381
482
 
382
- export declare interface Subscriber {
483
+ declare interface Subscriber {
383
484
  execute(): void;
384
485
  }
385
486
 
487
+ /** Types that should be treated as terminals (no further path exploration). */
488
+ declare type TerminalTypes = Date | RegExp | Map<unknown, unknown> | Set<unknown> | Promise<unknown> | Function;
489
+
386
490
  /**
387
- * Untracked execution.
491
+ * Executes a function without dependency tracking.
388
492
  *
389
493
  * @param fn - Function to execute.
390
494
  * @returns Result of `fn`.
@@ -394,12 +498,13 @@ export declare function untracked<T>(fn: () => T): T;
394
498
  /**
395
499
  * Writable atom interface.
396
500
  */
397
- export declare interface WritableAtom<T = unknown> extends ReadonlyAtom<T>, Disposable {
501
+ export declare interface WritableAtom<T = unknown> extends ReadonlyAtom<T>, Disposable_2 {
398
502
  value: T;
399
503
  /**
400
504
  * Cleans up the atom and releases resources.
401
505
  */
402
506
  dispose(): void;
507
+ [Symbol.dispose](): void;
403
508
  }
404
509
 
405
510
  export { }