@but212/atom-effect 0.29.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. */
@@ -114,33 +157,37 @@ export declare interface ComputedOptions<T = unknown> {
114
157
  /**
115
158
  * Debugging thresholds.
116
159
  */
117
- export declare const DEBUG_CONFIG: {
118
- readonly WARN_INFINITE_LOOP: true;
119
- readonly EFFECT_FREQUENCY_WINDOW: 1000;
120
- };
160
+ export declare const DEBUG_CONFIG: Readonly<{
161
+ WARN_INFINITE_LOOP: true;
162
+ EFFECT_FREQUENCY_WINDOW: 1000;
163
+ LOOP_THRESHOLD: 100;
164
+ }>;
121
165
 
122
- /**
123
- * Global debug controller singleton.
124
- */
125
- export declare const DEBUG_RUNTIME: DebugConfig;
126
-
127
- export declare interface DebugConfig {
166
+ declare interface DebugConfig {
128
167
  enabled: boolean;
129
168
  warnInfiniteLoop: boolean;
130
169
  warn(condition: boolean, message: string): void;
131
- attachDebugInfo(obj: object, type: string, id: number): void;
170
+ attachDebugInfo(obj: object, type: string, id: number, customName?: string): void;
132
171
  getDebugName(obj: object | null | undefined): string | undefined;
133
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>[];
134
178
  }
135
179
 
136
180
  /**
137
181
  * Dependency interface.
182
+ * Core contract for reactive nodes. All properties are required to ensure
183
+ * high-performance access within the engine.
138
184
  *
139
185
  * @remarks
140
186
  * Internal fields (`version`, `flags`, `_lastSeenEpoch`) are part of the
141
187
  * engine contract between reactive nodes and must not be mutated externally.
142
188
  */
143
- export declare interface Dependency {
189
+ export declare interface Dependency<T = unknown> {
190
+ /* Excluded from this release type: [BRAND] */
144
191
  readonly id: DependencyId;
145
192
  /* Excluded from this release type: version */
146
193
  /* Excluded from this release type: flags */
@@ -152,17 +199,36 @@ export declare interface Dependency {
152
199
  * The listener may optionally receive the new and previous values.
153
200
  * @param listener - A callback or Subscriber object.
154
201
  */
155
- subscribe(listener: ((newValue?: unknown, oldValue?: unknown) => void) | Subscriber): () => void;
156
- /** Peek hook. */
157
- peek?(): unknown;
158
- /** Value accessor. */
159
- 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;
160
211
  }
161
212
 
162
213
  /**
163
214
  * Dependency ID.
164
215
  */
165
- 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 }
166
232
 
167
233
  /**
168
234
  * Creates and starts an effect.
@@ -173,19 +239,28 @@ export declare type DependencyId = number;
173
239
  */
174
240
  export declare function effect(fn: EffectFunction, options?: EffectOptions): EffectObject;
175
241
 
176
- /** Effect error. */
242
+ /**
243
+ * Effect cleanup function.
244
+ */
245
+ export declare type EffectCleanup = () => void;
246
+
247
+ /** Thrown when an effect execution or cleanup fails. */
177
248
  export declare class EffectError extends AtomError {
178
- name: string;
179
- constructor(message: string, cause?: Error | null);
249
+ readonly name = "EffectError";
250
+ constructor(message: string, cause?: unknown, recoverable?: boolean, code?: string);
180
251
  }
181
252
 
182
253
  /**
183
- * 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.
184
257
  */
185
- export declare type EffectFunction = () => void | (() => void) | Promise<undefined | (() => void)>;
258
+ export declare type EffectFunction = () => (void | EffectCleanup) | Promise<void | EffectCleanup>;
186
259
 
187
- export declare interface EffectObject extends Disposable {
260
+ export declare interface EffectObject extends Disposable_2 {
261
+ /* Excluded from this release type: [BRAND] */
188
262
  dispose(): void;
263
+ [Symbol.dispose](): void;
189
264
  run(): void;
190
265
  readonly isDisposed: boolean;
191
266
  readonly executionCount: number;
@@ -205,10 +280,11 @@ export declare interface EffectOptions {
205
280
  */
206
281
  export declare function getPathValue(source: unknown, parts: string[]): unknown;
207
282
 
283
+ /** Global scheduler instance. */
284
+ export declare const globalScheduler: Scheduler_2;
285
+
208
286
  /**
209
287
  * Readonly atom check.
210
- *
211
- * @param obj - Object to check.
212
288
  */
213
289
  export declare function isAtom(obj: unknown): obj is ReadonlyAtom;
214
290
 
@@ -228,46 +304,33 @@ export declare function isEffect(obj: unknown): obj is EffectObject;
228
304
  export declare const lensFor: <T extends object>(atom: WritableAtom<T>) => <P extends Paths<T>>(path: P) => WritableAtom<PathValue<T, P>>;
229
305
 
230
306
  /** Max recursion depth for dot-paths. */
231
- export declare type MaxDepth = 8;
307
+ declare type MaxDepth = 8;
232
308
 
233
309
  /**
234
310
  * Generates a union of all possible dot-separated paths for a given type T.
311
+ * Excludes prototype methods and stops at TerminalTypes.
235
312
  *
236
313
  * Used for `atomLens` to provide IDE autocomplete and type safety when
237
314
  * zooming into deeply nested reactive objects.
238
- *
239
- * @example
240
- * type User = { profile: { name: string } };
241
- * type P = Paths<User>; // "profile" | "profile.name"
242
315
  */
243
- export declare type Paths<T, D extends unknown[] = []> = D['length'] extends MaxDepth ? never : T extends object ? {
244
- [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}`;
245
318
  }[keyof T & (string | number)] : never;
246
319
 
247
320
  /**
248
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.
249
323
  *
250
324
  * Works in tandem with `Paths<T>` to ensure that lensed atoms have
251
325
  * the correct inferred type for the member they point to.
252
326
  */
253
- 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;
254
-
255
- export declare interface PoolStats {
256
- acquired: number;
257
- released: number;
258
- rejected: {
259
- frozen: number;
260
- tooLarge: number;
261
- poolFull: number;
262
- };
263
- leaked: number;
264
- poolSize: number;
265
- }
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;
266
328
 
267
329
  /**
268
330
  * Readonly atom interface.
269
331
  */
270
332
  export declare interface ReadonlyAtom<T = unknown> {
333
+ /* Excluded from this release type: [BRAND] */
271
334
  /** The current value of the atom. */
272
335
  readonly value: T;
273
336
  /**
@@ -287,85 +350,125 @@ export declare interface ReadonlyAtom<T = unknown> {
287
350
  }
288
351
 
289
352
  /**
290
- * 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.
291
372
  */
292
373
  declare class Scheduler_2 {
293
- /** Queue buffer */
294
- _queueBuffer: [SchedulerJob[], SchedulerJob[]];
295
- _bufferIndex: number;
296
- _size: number;
297
- /** Epoch counter */
298
- _epoch: number;
299
- /** State flags */
300
- _isProcessing: boolean;
301
- _isFlushingSync: boolean;
302
- /** Batching state */
303
- _batchDepth: number;
304
- _batchQueue: SchedulerJob[];
305
- _batchQueueSize: number;
306
- /** Config */
307
- _maxFlushIterations: number;
308
- /** 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. */
309
395
  onOverflow: ((droppedCount: number) => void) | null;
310
- /** Bound run loop for microtask */
311
396
  private readonly _boundRunLoop;
397
+ /** Returns the total number of pending jobs (active + batched). */
312
398
  get queueSize(): number;
399
+ /** Returns true if the scheduler is currently within a `batch()` scope. */
313
400
  get isBatching(): boolean;
314
401
  /**
315
- * 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.
316
407
  */
317
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;
318
415
  /**
319
- * 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.
320
418
  */
321
- _flush(): void;
419
+ private _mergeBatchQueue;
322
420
  /**
323
- * Scheduler loop.
421
+ * Continuous loop that drains both main and batch queues.
422
+ * Processes until all queues are empty or max iterations reached.
324
423
  */
325
- private _runLoop;
326
- _flushSync(): void;
327
- _mergeBatchQueue(): void;
328
- _drainQueue(): void;
329
- _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. */
330
428
  private _handleFlushOverflow;
429
+ /** Enters a new batching depth. */
331
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
+ */
332
435
  endBatch(): void;
436
+ /** Configures the maximum safety iterations for the flush loop. */
333
437
  setMaxFlushIterations(max: number): void;
334
438
  }
335
439
 
336
- declare const scheduler_2: Scheduler_2;
337
- export { scheduler_2 as scheduler }
338
-
339
440
  /**
340
441
  * Scheduler configuration.
341
442
  */
342
- export declare const SCHEDULER_CONFIG: {
343
- readonly MAX_EXECUTIONS_PER_SECOND: 1000;
344
- readonly MAX_EXECUTIONS_PER_EFFECT: 100;
345
- readonly MAX_EXECUTIONS_PER_FLUSH: 10000;
346
- readonly MAX_FLUSH_ITERATIONS: 1000;
347
- readonly MIN_FLUSH_ITERATIONS: 10;
348
- readonly BATCH_QUEUE_SHRINK_THRESHOLD: 1000;
349
- };
350
-
351
- /** 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. */
352
453
  export declare class SchedulerError extends AtomError {
353
- name: string;
354
- constructor(message: string, cause?: Error | null);
454
+ readonly name = "SchedulerError";
455
+ constructor(message: string, cause?: unknown, recoverable?: boolean, code?: string);
355
456
  }
356
457
 
458
+ /** Union type representing any valid schedulable task. */
357
459
  declare type SchedulerJob = SchedulerJobFunction | SchedulerJobObject;
358
460
 
461
+ /** Represents a job that can be executed by the scheduler via a function interface. */
359
462
  declare interface SchedulerJobFunction {
360
463
  (): void;
361
- /** Next scheduled epoch */
362
- _nextEpoch?: number;
464
+ /** Internal tracking for deduplication within the same epoch. */
465
+ _nextEpoch?: number | undefined;
363
466
  }
364
467
 
365
468
  declare interface SchedulerJobObject {
366
469
  execute(): void;
367
- /** Next scheduled epoch */
368
- _nextEpoch?: number;
470
+ /** Internal tracking for deduplication within the same epoch. */
471
+ _nextEpoch?: number | undefined;
369
472
  }
370
473
 
371
474
  /**
@@ -375,14 +478,17 @@ declare interface SchedulerJobObject {
375
478
  export declare function setDeepValue(obj: unknown, keys: string[], index: number, value: unknown): unknown;
376
479
 
377
480
  /** Helper to convert numeric string to number for array indexing. */
378
- 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;
379
482
 
380
- export declare interface Subscriber {
483
+ declare interface Subscriber {
381
484
  execute(): void;
382
485
  }
383
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
+
384
490
  /**
385
- * Untracked execution.
491
+ * Executes a function without dependency tracking.
386
492
  *
387
493
  * @param fn - Function to execute.
388
494
  * @returns Result of `fn`.
@@ -392,12 +498,13 @@ export declare function untracked<T>(fn: () => T): T;
392
498
  /**
393
499
  * Writable atom interface.
394
500
  */
395
- export declare interface WritableAtom<T = unknown> extends ReadonlyAtom<T>, Disposable {
501
+ export declare interface WritableAtom<T = unknown> extends ReadonlyAtom<T>, Disposable_2 {
396
502
  value: T;
397
503
  /**
398
504
  * Cleans up the atom and releases resources.
399
505
  */
400
506
  dispose(): void;
507
+ [Symbol.dispose](): void;
401
508
  }
402
509
 
403
510
  export { }