@estjs/signals 0.0.15-beta.1

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.
@@ -0,0 +1,1213 @@
1
+ /**
2
+ * Reactive node state flags
3
+ */
4
+ declare const enum ReactiveFlags {
5
+ /** No state flags */
6
+ NONE = 0,
7
+ /**
8
+ * Mutable flag - The node's value can change
9
+ *
10
+ * Signal and Computed are mutable, triggering propagation when their values change.
11
+ * Immutable nodes (like constants) don't set this flag.
12
+ */
13
+ MUTABLE = 1,// 0b00000001 = 1
14
+ /**
15
+ * Watching flag - The node is being watched by an Effect
16
+ *
17
+ * Set when an Effect depends on this node.
18
+ * Used to determine whether to add the Effect to the execution queue.
19
+ */
20
+ WATCHING = 2,// 0b00000010 = 2
21
+ /**
22
+ * Recursion check flag - Currently checking for circular dependencies
23
+ *
24
+ * Set during the checkDirty process to detect and handle circular dependencies.
25
+ * Prevents infinite recursion.
26
+ */
27
+ RECURSED_CHECK = 4,// 0b00000100 = 4
28
+ /**
29
+ * Recursed flag - Already in the recursion chain
30
+ *
31
+ * Marks that the node has already appeared in the current propagation path.
32
+ * Used to handle complex dependency graph structures.
33
+ */
34
+ RECURSED = 8,// 0b00001000 = 8
35
+ /**
36
+ * Dirty flag - The node's value has changed but hasn't propagated yet
37
+ *
38
+ * Set when a Signal value changes.
39
+ * Also set when Computed detects dependency changes.
40
+ * Indicates need to recompute or notify subscribers.
41
+ */
42
+ DIRTY = 16,// 0b00010000 = 16
43
+ /**
44
+ * Pending flag - The node may need updating
45
+ *
46
+ * Set during propagation, indicating the node's dependencies may have become dirty.
47
+ * Need to call checkDirty to confirm if update is actually needed.
48
+ */
49
+ PENDING = 32,// 0b00100000 = 32
50
+ /**
51
+ * Queued flag - Effect has been added to the execution queue
52
+ *
53
+ * Prevents the same Effect from being added to the queue multiple times.
54
+ * This flag is cleared before queue execution.
55
+ */
56
+ QUEUED = 64
57
+ }
58
+ declare const TriggerOpTypes: {
59
+ readonly SET: "SET";
60
+ readonly ADD: "ADD";
61
+ readonly DELETE: "DELETE";
62
+ readonly CLEAR: "CLEAR";
63
+ };
64
+ /**
65
+ * Internal flags used to mark and identify different types of reactive objects.
66
+ * These flags are attached as properties to objects to indicate their reactive characteristics.
67
+ */
68
+ declare enum SignalFlags {
69
+ /** Mark an object as reactive */
70
+ IS_REACTIVE = "_IS_REACTIVE",
71
+ /** Mark an object as readonly */
72
+ IS_READONLY = "_IS_READONLY",
73
+ /** Mark an object as shallow reactive (only top-level properties are reactive) */
74
+ IS_SHALLOW = "_IS_SHALLOW",
75
+ /** Used to access the raw (non-reactive) version of an object */
76
+ RAW = "_RAW",
77
+ /** Mark an object as a signal */
78
+ IS_SIGNAL = "_IS_SIGNAL",
79
+ /** Mark an object as a computed property */
80
+ IS_COMPUTED = "_IS_COMPUTED",
81
+ /** Mark an object as a ref */
82
+ IS_REF = "_IS_REF",
83
+ /** Mark an object as an effect */
84
+ IS_EFFECT = "_IS_EFFECT"
85
+ }
86
+
87
+ /**
88
+ * Link - Bidirectional connection in the dependency graph
89
+ *
90
+ * A Link connects two ReactiveNodes:
91
+ * - depNode: The dependency node (data source)
92
+ * - subNode: The subscriber node (data consumer)
93
+ *
94
+ * Links form doubly-linked lists in two directions:
95
+ * 1. Subscriber Chain: Connects all subscribers of the same dependency
96
+ * 2. Dependency Chain: Connects all dependencies of the same subscriber
97
+ *
98
+ * @example
99
+ * ```
100
+ * Signal A ←─┐
101
+ * ├─→ Effect X
102
+ * Signal B ←─┘
103
+ *
104
+ * Link1: A → X (A's subscriber chain, X's dependency chain)
105
+ * Link2: B → X (B's subscriber chain, X's dependency chain)
106
+ * ```
107
+ */
108
+ interface Link {
109
+ /**
110
+ * Version number
111
+ *
112
+ * Used to detect stale Links.
113
+ * The global version number increments each time dependency tracking starts.
114
+ * Links with old versions will be cleaned up.
115
+ *
116
+ */
117
+ version: number;
118
+ /**
119
+ * Dependency node - The data source being depended on
120
+ * Examples: Signal, Computed
121
+ */
122
+ depNode: ReactiveNode;
123
+ /**
124
+ * Subscriber node - The consumer of the data
125
+ * Examples: Effect, Computed
126
+ */
127
+ subNode: ReactiveNode;
128
+ /** Previous subscriber Link */
129
+ prevSubLink?: Link;
130
+ /** Next subscriber Link */
131
+ nextSubLink?: Link;
132
+ /** Previous dependency Link */
133
+ prevDepLink?: Link;
134
+ /** Next dependency Link */
135
+ nextDepLink?: Link;
136
+ }
137
+ /**
138
+ * Debugger event types for tracking reactive operations
139
+ */
140
+ type DebuggerEventType = 'get' | 'set' | 'add' | 'delete' | 'clear' | 'iterate';
141
+ /**
142
+ * Debugger event for tracking reactive operations
143
+ *
144
+ * This event is passed to onTrack and onTrigger callbacks to provide
145
+ * detailed information about reactive operations for debugging purposes.
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * effect(() => {
150
+ * console.log(signal.value);
151
+ * }, {
152
+ * onTrack(event) {
153
+ * console.log('Tracked:', event.type, event.key);
154
+ * },
155
+ * onTrigger(event) {
156
+ * console.log('Triggered:', event.type, event.key, event.newValue);
157
+ * }
158
+ * });
159
+ * ```
160
+ */
161
+ interface DebuggerEvent {
162
+ /** The effect or computed that is tracking/being triggered */
163
+ effect: ReactiveNode;
164
+ /** The reactive object being accessed or modified */
165
+ target: object;
166
+ /** The type of operation */
167
+ type: DebuggerEventType | string;
168
+ /** The property key being accessed or modified (optional) */
169
+ key?: any;
170
+ /** The new value being set (optional, only for trigger events) */
171
+ newValue?: any;
172
+ }
173
+ /**
174
+ * ReactiveNode - Reactive node interface
175
+ *
176
+ * All objects participating in the reactive system implement this interface.
177
+ * Includes Signal, Computed, Effect, Reactive objects, etc.
178
+ *
179
+ * Nodes form a dependency graph through Links:
180
+ * - depLink: List of nodes I depend on
181
+ * - subLink: List of nodes that depend on me
182
+ */
183
+ interface ReactiveNode {
184
+ /**
185
+ * Dependency chain head - The first node I depend on
186
+ *
187
+ * Traverse all dependencies through nextDepLink.
188
+ */
189
+ depLink?: Link;
190
+ /**
191
+ * Subscriber chain head - The first node that depends on me
192
+ *
193
+ * Traverse all subscribers through nextSubLink.
194
+ */
195
+ subLink?: Link;
196
+ /**
197
+ * Dependency chain tail - The last node I depend on
198
+ *
199
+ * Used for O(1) time complexity linked list append operations.
200
+ */
201
+ depLinkTail?: Link;
202
+ /**
203
+ * Subscriber chain tail - The last node that depends on me
204
+ *
205
+ * Used for O(1) time complexity linked list append operations.
206
+ */
207
+ subLinkTail?: Link;
208
+ /**
209
+ * State flags
210
+ * @see ReactiveFlags
211
+ */
212
+ flag: ReactiveFlags;
213
+ /**
214
+ * Optional debugging hook called when dependencies are tracked
215
+ */
216
+ onTrack?: (event: DebuggerEvent) => void;
217
+ /**
218
+ * Optional debugging hook called when reactive changes are triggered
219
+ */
220
+ onTrigger?: (event: DebuggerEvent) => void;
221
+ }
222
+ /**
223
+ * Execute function with tracking disabled
224
+ *
225
+ * During function execution, accessing Signals won't establish dependencies.
226
+ *
227
+ * @param fn - The function to execute
228
+ * @returns The function's return value
229
+ */
230
+ declare function untrack<T>(fn: () => T): T;
231
+ /**
232
+ * Trigger updates for subscribers of a reactive object property
233
+ *
234
+ * This function notifies all subscribers (effects/computed) that depend on a specific
235
+ * property of a reactive object that the property has changed.
236
+ *
237
+ * ## When is this called?
238
+ *
239
+ * - When setting a property: `reactiveObj.prop = value` → trigger(obj, 'SET', 'prop', value)
240
+ * - When adding a property: `reactiveObj.newProp = value` → trigger(obj, 'ADD', 'newProp', value)
241
+ * - When deleting a property: `delete reactiveObj.prop` → trigger(obj, 'DELETE', 'prop')
242
+ * - When clearing a collection: `reactiveArray.length = 0` → trigger(obj, 'CLEAR')
243
+ * - When mutating arrays: `reactiveArray.push(item)` → trigger(obj, 'SET', 'length', newLength)
244
+ *
245
+ * ## Operation Types
246
+ *
247
+ * - **SET**: Property value changed (most common)
248
+ * - **ADD**: New property added (affects iteration)
249
+ * - **DELETE**: Property removed (affects iteration)
250
+ * - **CLEAR**: Collection cleared (affects iteration)
251
+ *
252
+ * ## Iteration Dependencies
253
+
254
+ * @param target - The reactive object that changed
255
+ * @param type - The type of operation: 'SET' | 'ADD' | 'DELETE' | 'CLEAR'
256
+ * @param key - The property key that changed (optional for CLEAR operations)
257
+ * @param newValue - The new value (optional, used for debugging)
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * const state = reactive({ count: 0, items: [1, 2, 3] });
262
+ *
263
+ * effect(() => {
264
+ * console.log(state.count); // Depends on 'count'
265
+ * });
266
+ *
267
+ * effect(() => {
268
+ * console.log(state.items.length); // Depends on 'items' and iteration
269
+ * });
270
+ *
271
+ * // Triggers first effect only
272
+ * state.count = 1; // trigger(state, 'SET', 'count', 1)
273
+ *
274
+ * // Triggers second effect (changes length and iteration)
275
+ * state.items.push(4); // trigger(state.items, 'SET', 'length', 4)
276
+ * // trigger(state.items, 'ADD', '3', 4)
277
+ * ```
278
+ */
279
+ declare function trigger(target: object, type: string, key?: string | symbol | (string | symbol)[], newValue?: unknown): void;
280
+
281
+ /**
282
+ * Signal is a reactive primitive that holds a value and notifies subscribers when the value changes.
283
+ * It provides methods for reading, writing, and observing value changes.
284
+ *
285
+ * @template T - The type of value held by the Signal
286
+ */
287
+ interface Signal<T> {
288
+ /**
289
+ * The current value of the Signal. Reading this property tracks dependencies,
290
+ * and writing to it notifies subscribers of changes.
291
+ */
292
+ value: T;
293
+ /**
294
+ * Get the current value without establishing a dependency relationship.
295
+ * Useful when you need to read the value without tracking dependencies.
296
+ *
297
+ * @returns The current value
298
+ */
299
+ peek(): T;
300
+ /**
301
+ * Set a new value without notifying subscribers.
302
+ * Used for batching multiple updates together.
303
+ *
304
+ * @param value - The new value to set
305
+ */
306
+ set(value: T): void;
307
+ /**
308
+ * Update the value using a function that receives the current value.
309
+ * This is an atomic operation that only notifies subscribers once.
310
+ *
311
+ * @param updater - A function that receives the current value and returns the new value
312
+ */
313
+ update(updater: (prev: T) => T): void;
314
+ }
315
+ /**
316
+ * A more precise type for the value held by a signal.
317
+ * This type helps TypeScript understand the type of the unwrapped value.
318
+ *
319
+ * @template T - The type of value held by the signal
320
+ */
321
+ type SignalValue<T> = T extends Signal<infer V> ? V : never;
322
+ /**
323
+ * Extract the value type from a Signal
324
+ *
325
+ * @template T - The Signal type
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * import { signal, type SignalType } from '@estjs/signals';
330
+ *
331
+ * const count = signal(0);
332
+ * type CountValue = SignalType<typeof count>; // number
333
+ * ```
334
+ */
335
+ type SignalType<T> = T extends Signal<infer V> ? V : never;
336
+ /**
337
+ * Create a new signal with the given initial value.
338
+ * The signal will track all nested properties of object values.
339
+ *
340
+ * @template T - The type of value to store in the signal
341
+ * @param value - Initial value (defaults to undefined)
342
+ * @returns A new signal instance
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * const count = signal(0);
347
+ * const user = signal({ name: 'John' });
348
+ * const empty = signal(); // undefined
349
+ * ```
350
+ */
351
+ declare function signal<T>(value?: T): Signal<T>;
352
+ /**
353
+ * Create a new shallow signal with the given initial value.
354
+ * Only the top-level properties of object values are reactive.
355
+ *
356
+ * @template T - The type of value to store in the signal
357
+ * @param value - Initial value (defaults to undefined)
358
+ * @returns A new shallow signal instance
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * const state = shallowSignal({ nested: { value: 1 } });
363
+ * // Only state.nested is reactive, not state.nested.value
364
+ * ```
365
+ */
366
+ declare function shallowSignal<T>(value?: T): Signal<T>;
367
+ /**
368
+ * Type guard to check if a value is a Signal instance.
369
+ *
370
+ * @template T - The type of value held by the signal
371
+ * @param value - The value to check
372
+ * @returns true if the value is a Signal instance
373
+ */
374
+ declare function isSignal<T>(value: unknown): value is Signal<T>;
375
+
376
+ /**
377
+ * Computed getter function type
378
+ */
379
+ type ComputedGetter<T> = () => T;
380
+ /**
381
+ * Computed setter function type
382
+ */
383
+ type ComputedSetter<T> = (value: T) => void;
384
+ /**
385
+ * Computed options configuration
386
+ */
387
+ interface ComputedOptions<T> {
388
+ /** Getter function to compute the value */
389
+ get: ComputedGetter<T>;
390
+ /** Optional setter function to make the computed writable */
391
+ set?: ComputedSetter<T>;
392
+ /**
393
+ * Debug callback invoked when a dependency is tracked
394
+ * Only called in development mode
395
+ *
396
+ * @param event - Information about the tracked dependency
397
+ */
398
+ onTrack?: (event: DebuggerEvent) => void;
399
+ /**
400
+ * Debug callback invoked when the computed is triggered by a dependency change
401
+ * Only called in development mode
402
+ *
403
+ * @param event - Information about what triggered the recomputation
404
+ */
405
+ onTrigger?: (event: DebuggerEvent) => void;
406
+ }
407
+ /**
408
+ * Computed interface
409
+ */
410
+ interface Computed<T> {
411
+ readonly value: T;
412
+ peek(): T;
413
+ }
414
+ /**
415
+ * Extract the value type from a Computed
416
+ *
417
+ * @template T - The Computed type
418
+ *
419
+ * @example
420
+ * ```typescript
421
+ * import { computed, type ComputedType } from '@estjs/signals';
422
+ *
423
+ * const doubled = computed(() => count.value * 2);
424
+ * type DoubledValue = ComputedType<typeof doubled>; // number
425
+ * ```
426
+ */
427
+ type ComputedType<T> = T extends Computed<infer V> ? V : never;
428
+ /**
429
+ * Computed implementation class
430
+ *
431
+ * Implements both Computed and ReactiveNode interfaces.
432
+ * Features:
433
+ * - Lazy evaluation: only computes when accessed
434
+ * - Smart caching: returns cached value when dependencies haven't changed
435
+ * - Automatic tracking: automatically tracks dependencies during computation
436
+ *
437
+ * @template T - The type of the computed value
438
+ */
439
+ declare class ComputedImpl<T = any> implements Computed<T>, ReactiveNode {
440
+ depLink?: Link;
441
+ subLink?: Link;
442
+ depLinkTail?: Link;
443
+ subLinkTail?: Link;
444
+ flag: ReactiveFlags;
445
+ private readonly [SignalFlags.IS_COMPUTED];
446
+ readonly getter: ComputedGetter<T>;
447
+ readonly setter?: ComputedSetter<T>;
448
+ readonly onTrack?: (event: DebuggerEvent) => void;
449
+ readonly onTrigger?: (event: DebuggerEvent) => void;
450
+ private _value;
451
+ /**
452
+ * Create a Computed instance
453
+ *
454
+ * @param getter - The computation function
455
+ * @param setter - Optional setter function
456
+ * @param onTrack - Optional debug callback for dependency tracking
457
+ * @param onTrigger - Optional debug callback for triggers
458
+ */
459
+ constructor(getter: ComputedGetter<T>, setter?: ComputedSetter<T>, onTrack?: (event: DebuggerEvent) => void, onTrigger?: (event: DebuggerEvent) => void);
460
+ get value(): T;
461
+ /**
462
+ * Set value (only effective when setter is provided)
463
+ *
464
+ * @param newValue - The new value
465
+ */
466
+ set value(newValue: T);
467
+ /**
468
+ * Read value without tracking dependencies
469
+ *
470
+ * @returns Current value
471
+ */
472
+ peek(): T;
473
+ /**
474
+ * Recompute the value
475
+ *
476
+ * computation logic:
477
+ * 1. Start tracking dependencies
478
+ * 2. Execute getter function
479
+ * 3. Check if value changed using optimized comparison
480
+ * 4. If changed, update cache and notify subscribers
481
+ * 5. End tracking, clean up stale dependencies
482
+ * @private
483
+ */
484
+ private recompute;
485
+ /**
486
+ * Check if update is needed
487
+ *
488
+ * Internal use, called by reactive system.
489
+ *
490
+ * @returns true if value changed
491
+ */
492
+ shouldUpdate(): boolean;
493
+ }
494
+ /**
495
+ * Create a Computed value
496
+ *
497
+ * @param getterOrOptions - Computation function or configuration object
498
+ * @returns Computed instance
499
+ *
500
+ * @example
501
+ * ```typescript
502
+ * // Read-only computed
503
+ * const count = signal(0);
504
+ * const doubled = computed(() => count.value * 2);
505
+ *
506
+ * console.log(doubled.value); // 0
507
+ * count.value = 5;
508
+ * console.log(doubled.value); // 10
509
+ *
510
+ * // Writable computed
511
+ * const firstName = signal('John');
512
+ * const lastName = signal('Doe');
513
+ *
514
+ * const fullName = computed({
515
+ * get: () => `${firstName.value} ${lastName.value}`,
516
+ * set: (value) => {
517
+ * const [first, last] = value.split(' ');
518
+ * firstName.value = first;
519
+ * lastName.value = last;
520
+ * }
521
+ * });
522
+ *
523
+ * fullName.value = 'Jane Smith';
524
+ * console.log(firstName.value); // 'Jane'
525
+ * ```
526
+ */
527
+ declare function computed<T>(getterOrOptions: ComputedGetter<T> | ComputedOptions<T>): ComputedImpl<T>;
528
+ /**
529
+ * Type guard - Check if value is Computed
530
+ *
531
+ * @param value - The value to check
532
+ * @returns true if value is Computed
533
+ */
534
+ declare function isComputed<T>(value: unknown): value is Computed<T>;
535
+
536
+ /**
537
+ * Return the raw underlying value of a reactive proxy or signal.
538
+ * Recursively unwraps nested reactive objects and arrays.
539
+ *
540
+ * @param value - Reactive or signal value.
541
+ * @returns Raw value without any reactive wrapping.
542
+ */
543
+ declare function toRaw<T>(value: T): T;
544
+ /**
545
+ * Check if the target object is reactive.
546
+ *
547
+ * @param target - The object to check.
548
+ * @returns True if the object is reactive, false otherwise.
549
+ */
550
+ declare function isReactive(target: unknown): boolean;
551
+ /**
552
+ * Create a reactive proxy for the given target object. If the object is already reactive, return directly.
553
+ *
554
+ * @template T - The type of the object to make reactive
555
+ * @param target - The object to make reactive
556
+ * @returns The reactive proxy of the target object
557
+ *
558
+ * @example
559
+ * ```typescript
560
+ * const state = reactive({ count: 0, nested: { value: 1 } });
561
+ * // Both state.count and state.nested.value are reactive
562
+ * ```
563
+ */
564
+ declare function reactive<T extends object>(target: T): T;
565
+ /**
566
+ * Create a shallow reactive proxy for the given object. Only root-level properties are reactive.
567
+ *
568
+ * @template T - The type of the object to make shallow reactive
569
+ * @param target - The object to make shallow reactive
570
+ * @returns The shallow reactive proxy of the object
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * const state = shallowReactive({ count: 0, nested: { value: 1 } });
575
+ * // Only state.count is reactive, not state.nested.value
576
+ * ```
577
+ */
578
+ declare function shallowReactive<T extends object>(target: T): T;
579
+ /**
580
+ * Check if the target object is a shallow reactive proxy.
581
+ *
582
+ * @param value - The object to check.
583
+ * @returns True if the object is shallow reactive, false otherwise.
584
+ */
585
+ declare function isShallow(value: unknown): boolean;
586
+ /**
587
+ * Return a reactive proxy for the given value (if possible).
588
+ * If the given value is not an object, return the original value itself.
589
+ *
590
+ * @param value - The value that needs a reactive proxy created for it.
591
+ */
592
+ declare const toReactive: <T extends unknown>(value: T) => T;
593
+ /**
594
+ * Type alias representing a reactive object.
595
+ *
596
+ * @template T - The type of the original object.
597
+ */
598
+ type Reactive<T extends object> = T;
599
+
600
+ /**
601
+ * Represents a task (job) that can be scheduled for execution
602
+ */
603
+ type Job = () => void;
604
+ /**
605
+ * Represents a callback function that should be executed before the main task queue
606
+ */
607
+ type PreFlushCallback = () => void;
608
+ /**
609
+ * Represents the possible flush timing strategies for effects
610
+ *
611
+ * - 'pre': Execute before the main queue (useful for component updates)
612
+ * - 'post': Execute after the main queue (default behavior)
613
+ * - 'sync': Execute immediately and synchronously (use sparingly)
614
+ */
615
+ type FlushTiming = 'pre' | 'post' | 'sync';
616
+ /**
617
+ * Schedules a function to be executed in the next microtask
618
+ *
619
+ * If no function is provided, returns a Promise that resolves in the next microtask.
620
+ * This is useful for waiting until the DOM is update or deferring execution.
621
+ *
622
+ * @param fn - Optional function to execute
623
+ * @returns A Promise that resolves after the function execution
624
+ */
625
+ declare function nextTick(fn?: () => void): Promise<void>;
626
+ /**
627
+ * Adds a job to the main queue and ensures it will be executed
628
+ *
629
+ * Jobs are automatically deduplicated - the same job reference won't be added multiple times.
630
+ * This is useful for batching updates and avoiding redundant work.
631
+ * @param job - The job to enqueue
632
+ */
633
+ declare function queueJob(job: Job): void;
634
+ /**
635
+ * Adds a callback to be executed before the main queue processing
636
+ *
637
+ * Pre-flush callbacks are useful for setup work that needs to run before effects,
638
+ * such as computing derived values or preparing state.
639
+ * @param cb - The callback to execute before the main queue
640
+ */
641
+ declare function queuePreFlushCb(cb: PreFlushCallback): void;
642
+
643
+ /**
644
+ * Unwrap a Signal, Computed, or Reactive type to get the underlying value type
645
+ *
646
+ * @template T - The wrapped type
647
+ *
648
+ * @example
649
+ * ```typescript
650
+ * import type { Signal, Computed, Reactive, Unwrap } from '@estjs/signals';
651
+ *
652
+ * type Count = Unwrap<Signal<number>>; // number
653
+ * type User = Unwrap<Reactive<{ name: string }>>; // { name: string }
654
+ * type Double = Unwrap<Computed<number>>; // number
655
+ * ```
656
+ */
657
+ type Unwrap<T> = T extends Signal<infer V> ? V : T extends Computed<infer V> ? V : T extends Reactive<infer V extends object> ? V : T;
658
+ /**
659
+ * Effect function type
660
+ */
661
+ type EffectFunction<T = any> = () => T;
662
+ /**
663
+ * Effect scheduler function type
664
+ */
665
+ type EffectScheduler = (effect: EffectImpl) => void;
666
+ /**
667
+ * Effect options configuration
668
+ */
669
+ interface EffectOptions {
670
+ /**
671
+ * Custom scheduler for controlling when the effect runs
672
+ * Can be a function or a timing string ('sync', 'pre', 'post')
673
+ */
674
+ scheduler?: EffectScheduler | FlushTiming;
675
+ /**
676
+ * Alias for scheduler - controls when the effect runs
677
+ * Can be 'sync', 'pre', or 'post'
678
+ */
679
+ flush?: FlushTiming;
680
+ /**
681
+ * Callback invoked when the effect is stopped
682
+ * Useful for cleanup operations
683
+ */
684
+ onStop?: () => void;
685
+ /**
686
+ * Debug callback invoked when a dependency is tracked
687
+ * Only called in development mode
688
+ *
689
+ * @param event - Information about the tracked dependency
690
+ *
691
+ * @example
692
+ * ```typescript
693
+ * effect(() => {
694
+ * console.log(signal.value);
695
+ * }, {
696
+ * onTrack(event) {
697
+ * console.log('Tracked:', event.type, event.key);
698
+ * }
699
+ * });
700
+ * ```
701
+ */
702
+ onTrack?: (event: DebuggerEvent) => void;
703
+ /**
704
+ * Debug callback invoked when the effect is triggered by a dependency change
705
+ * Only called in development mode
706
+ *
707
+ * @param event - Information about what triggered the effect
708
+ *
709
+ * @example
710
+ * ```typescript
711
+ * effect(() => {
712
+ * console.log(signal.value);
713
+ * }, {
714
+ * onTrigger(event) {
715
+ * console.log('Triggered by:', event.type, event.key, event.newValue);
716
+ * }
717
+ * });
718
+ * ```
719
+ */
720
+ onTrigger?: (event: DebuggerEvent) => void;
721
+ }
722
+ /**
723
+ * Effect runner function with attached effect instance
724
+ */
725
+ interface EffectRunner<T = any> {
726
+ (): T;
727
+ effect: EffectImpl<T>;
728
+ stop: () => void;
729
+ }
730
+ /**
731
+ * Effect implementation class
732
+ *
733
+ * Implements the ReactiveNode interface, acting as a subscriber in the reactive system.
734
+ *
735
+ * Core features:
736
+ * - Automatically tracks dependent reactive values
737
+ * - Automatically re-executes when dependencies change
738
+ * - Supports custom scheduling strategies
739
+ * - Complete lifecycle management
740
+ *
741
+ * @template T - The return type of the effect function
742
+ */
743
+ declare class EffectImpl<T = any> implements ReactiveNode {
744
+ depLink?: Link;
745
+ subLink?: Link;
746
+ depLinkTail?: Link;
747
+ subLinkTail?: Link;
748
+ flag: ReactiveFlags;
749
+ private readonly [SignalFlags.IS_EFFECT];
750
+ readonly fn: EffectFunction<T>;
751
+ readonly scheduler?: EffectScheduler | FlushTiming;
752
+ readonly onStop?: () => void;
753
+ readonly onTrack?: (event: DebuggerEvent) => void;
754
+ readonly onTrigger?: (event: DebuggerEvent) => void;
755
+ readonly flash?: 'sync' | 'pre' | 'post';
756
+ private _active;
757
+ /**
758
+ * Create an Effect instance
759
+ *
760
+ * @param fn - The effect function
761
+ * @param options - Configuration options
762
+ */
763
+ constructor(fn: EffectFunction<T>, options?: EffectOptions);
764
+ /**
765
+ * Check if the Effect is active
766
+ */
767
+ get active(): boolean;
768
+ /**
769
+ * Check if the Effect is dirty (needs re-execution)
770
+
771
+ */
772
+ get dirty(): boolean;
773
+ /**
774
+ * Pause Effect execution
775
+ *
776
+ * When an effect is paused:
777
+ * - It stops responding to dependency changes
778
+ * - Notifications are ignored (see notify method)
779
+ * - DIRTY and PENDING flags are still set when dependencies change
780
+ * - The effect remains active and maintains its dependency links
781
+ *
782
+ * Use cases:
783
+ * - Temporarily disable effects during bulk updates
784
+ * - Prevent effects from running during initialization
785
+ * - Control when side effects should execute
786
+ *
787
+ * @example
788
+ * ```typescript
789
+ * const count = signal(0);
790
+ * const runner = effect(() => console.log(count.value));
791
+ *
792
+ * runner.effect.pause();
793
+ * count.value = 1; // Effect won't run
794
+ * count.value = 2; // Effect won't run
795
+ * runner.effect.resume(); // Effect runs once with latest value
796
+ * ```
797
+ */
798
+ pause(): void;
799
+ /**
800
+ * Resume Effect execution
801
+ *
802
+ * When an effect is resumed:
803
+ * - The PAUSED flag is cleared
804
+ * - If dependencies changed during pause (DIRTY or PENDING flags set),
805
+ * the effect executes immediately via notify()
806
+ * - If no changes occurred, the effect simply becomes active again
807
+ *
808
+ * State management:
809
+ * - Clears PAUSED flag atomically
810
+ * - Checks for accumulated DIRTY/PENDING flags
811
+ * - Triggers execution if needed
812
+ *
813
+ * @example
814
+ * ```typescript
815
+ * const count = signal(0);
816
+ * const runner = effect(() => console.log(count.value));
817
+ *
818
+ * runner.effect.pause();
819
+ * count.value = 1; // Queued
820
+ * count.value = 2; // Queued
821
+ * runner.effect.resume(); // Executes once with count.value = 2
822
+ * ```
823
+ */
824
+ resume(): void;
825
+ /**
826
+ * Execute the Effect function
827
+ *
828
+ * Core execution flow:
829
+ * 1. Check if active
830
+ * 2. Clear dirty flag
831
+ * 3. Start tracking dependencies
832
+ * 4. Execute user function
833
+ * 5. End tracking, clean up stale dependencies
834
+
835
+ * @returns The return value of the effect function
836
+ */
837
+ run(): T;
838
+ private _job?;
839
+ /**
840
+ * Get or create the job function for this effect
841
+ */
842
+ private getJob;
843
+ /**
844
+ * Notify that the Effect needs to execute
845
+ *
846
+ * Called by dependent reactive values.
847
+ * Decides whether to execute immediately or defer based on scheduling strategy.
848
+ */
849
+ notify(): void;
850
+ /**
851
+ * Stop the Effect
852
+ *
853
+ * After stopping:
854
+ * - No longer responds to dependency changes
855
+ * - Disconnects all dependency links
856
+ * - Clears cached job function
857
+ * - Calls onStop callback
858
+ * - Verifies complete cleanup in development mode
859
+ */
860
+ stop(): void;
861
+ }
862
+ /**
863
+ * Create and immediately execute an Effect
864
+ *
865
+ * @param fn - The effect function
866
+ * @param options - Configuration options
867
+ * @returns Effect runner
868
+ *
869
+ * @example
870
+ * ```typescript
871
+ * const count = signal(0);
872
+ *
873
+ * // Basic usage
874
+ * const runner = effect(() => {
875
+ * console.log('Count:', count.value);
876
+ * });
877
+ *
878
+ * count.value = 1; // Automatically executes, prints 'Count: 1'
879
+ *
880
+ * // Manual execution
881
+ * runner();
882
+ *
883
+ * // Stop
884
+ * runner.effect.stop();
885
+ *
886
+ * // Custom scheduling
887
+ * effect(() => {
888
+ * console.log(count.value);
889
+ * }, {
890
+ * scheduler: (eff) => {
891
+ * setTimeout(() => eff.run(), 100);
892
+ * }
893
+ * });
894
+ * ```
895
+ */
896
+ declare function effect<T = any>(fn: EffectFunction<T>, options?: EffectOptions): EffectRunner<T>;
897
+ /**
898
+ * Stop Effect execution
899
+ *
900
+ * @param runner - The effect runner
901
+ */
902
+ declare function stop(runner: EffectRunner): void;
903
+ /**
904
+ * Type guard - Check if value is an Effect
905
+ *
906
+ * @param value - The value to check
907
+ * @returns true if value is an Effect
908
+ */
909
+ declare function isEffect(value: any): value is EffectImpl;
910
+ /**
911
+ * Memoized effect function type
912
+ *
913
+ * @template T - State type
914
+ * @param prevState - State from previous execution
915
+ * @returns New state
916
+ */
917
+ type MemoEffectFn<T> = (prevState: T) => T;
918
+ /**
919
+ * Create a memoized Effect
920
+ *
921
+ * A memoized effect remembers the return value from the previous execution
922
+ * and passes it as a parameter on the next execution.
923
+ *
924
+ * Use cases:
925
+ * - Incremental DOM updates
926
+ * - Avoiding duplicate operations
927
+ * - State persistence
928
+ * - Difference detection
929
+ *
930
+ * @param fn - The memoized function
931
+ * @param initialState - Initial state
932
+ * @param options - Configuration options
933
+ * @returns Effect runner
934
+ *
935
+ * @example
936
+ * ```typescript
937
+ * const width = signal(100);
938
+ *
939
+ * // Only update DOM when width changes
940
+ * memoEffect(prev => {
941
+ * const current = width.value;
942
+ *
943
+ * if (current !== prev.width) {
944
+ * element.style.width = `${current}px`;
945
+ * prev.width = current;
946
+ * }
947
+ *
948
+ * return prev;
949
+ * }, { width: 0 });
950
+ * ```
951
+ */
952
+ declare function memoEffect<T>(fn: MemoEffectFn<T>, initialState: T, options?: EffectOptions): EffectRunner<void>;
953
+
954
+ /**
955
+ * Execute a function in batch mode
956
+ *
957
+ * Executes the function in a batch context, where all Signal changes
958
+ * are deferred and processed together after the batch ends.
959
+ *
960
+ * @param fn - The function to execute in batch mode
961
+ * @returns The return value of the function
962
+ *
963
+ * @example
964
+ * ```typescript
965
+ * const x = signal(0);
966
+ * const y = signal(0);
967
+ *
968
+ * effect(() => {
969
+ * console.log('Sum:', x.value + y.value);
970
+ * });
971
+ *
972
+ * // Without batch - Effect executes 2 times
973
+ * x.value = 1; // Effect executes
974
+ * y.value = 2; // Effect executes
975
+ *
976
+ * // With batch - Effect executes only 1 time
977
+ * batch(() => {
978
+ * x.value = 10;
979
+ * y.value = 20;
980
+ * }); // Effect executes once
981
+ * ```
982
+ */
983
+ declare function batch<T>(fn: () => T): T;
984
+ /**
985
+ * Start batch update
986
+ *
987
+ * Increases batch depth.
988
+ * During batch, Effects won't execute immediately.
989
+ */
990
+ declare function startBatch(): void;
991
+ /**
992
+ * End batch update
993
+ *
994
+ * Decreases batch depth.
995
+ * When depth reaches zero, flush all queued Effects and clean up.
996
+ *
997
+ * ## Cleanup Process
998
+ *
999
+ * When the outermost batch ends:
1000
+ * 1. Flush all queued jobs (effects execute)
1001
+ * 2. Job queue is automatically cleared by flushJobs()
1002
+ * 3. Temporary flags (QUEUED, DIRTY) are cleared by effect execution
1003
+ *
1004
+ */
1005
+ declare function endBatch(): void;
1006
+ /**
1007
+ * Check if currently in batch update mode
1008
+ *
1009
+ * @returns true if currently in batch
1010
+ */
1011
+ declare function isBatching(): boolean;
1012
+ /**
1013
+ * Get current batch depth
1014
+ *
1015
+ * Mainly used for debugging.
1016
+ *
1017
+ * @returns Current batch nesting depth
1018
+ */
1019
+ declare function getBatchDepth(): number;
1020
+
1021
+ /**
1022
+ * Represents a store's state object.
1023
+ * Must be a plain object containing the store's reactive state.
1024
+ */
1025
+ type State = Record<string, any>;
1026
+ /**
1027
+ * Represents a store's getters object.
1028
+ * Each getter is a function that receives the state and returns a computed value.
1029
+ */
1030
+ type Getters<S extends State> = Record<string, (state: S) => any>;
1031
+ /**
1032
+ * Represents a store's actions object.
1033
+ * Each action is a function that can modify the store's state.
1034
+ */
1035
+ type Actions = Record<string, (...args: any[]) => any>;
1036
+ /**
1037
+ * Configuration options for creating a store.
1038
+ *
1039
+ * @template S - The type of the store's state
1040
+ * @template G - The type of the store's getters
1041
+ * @template A - The type of the store's actions
1042
+ */
1043
+ interface StoreOptions<S extends State, G extends Getters<S>, A extends Actions> {
1044
+ /** The initial state of the store */
1045
+ state: S;
1046
+ /** Computed values derived from the state */
1047
+ getters?: G;
1048
+ /** Methods that can modify the store's state */
1049
+ actions?: A;
1050
+ }
1051
+ /**
1052
+ * Payload for patching store state.
1053
+ * Must be a partial object matching the store's state shape.
1054
+ */
1055
+ type PatchPayload<S> = Partial<S>;
1056
+ /**
1057
+ * Callback function for store subscriptions and action notifications.
1058
+ */
1059
+ type StoreCallback<S> = (state: S) => void;
1060
+ /**
1061
+ * Built-in actions available on all stores.
1062
+ *
1063
+ * @template S - The type of the store's state
1064
+ */
1065
+ interface StoreActions<S extends State> {
1066
+ /**
1067
+ * Updates multiple state properties at once.
1068
+ * Triggers a single update notification.
1069
+ *
1070
+ * @param payload - Object containing state updates
1071
+ */
1072
+ patch$: (payload: PatchPayload<S>) => void;
1073
+ /**
1074
+ * Subscribes to state changes.
1075
+ * The callback is called whenever the state changes.
1076
+ *
1077
+ * @param callback - Function to call on state changes
1078
+ */
1079
+ subscribe$: (callback: StoreCallback<S>) => void;
1080
+ /**
1081
+ * Unsubscribes from state changes.
1082
+ *
1083
+ * @param callback - The callback to remove
1084
+ */
1085
+ unsubscribe$: (callback: StoreCallback<S>) => void;
1086
+ /**
1087
+ * Subscribes to action executions.
1088
+ * The callback is called whenever an action is executed.
1089
+ *
1090
+ * @param callback - Function to call on action execution
1091
+ */
1092
+ onAction$: (callback: StoreCallback<S>) => void;
1093
+ /**
1094
+ * Resets the store state to its initial values.
1095
+ */
1096
+ reset$: () => void;
1097
+ }
1098
+ /**
1099
+ * Computed values from getters.
1100
+ *
1101
+ * @template G - The type of the store's getters
1102
+ */
1103
+ type GetterValues<G extends Getters<any>> = {
1104
+ [K in keyof G]: ReturnType<G[K]>;
1105
+ };
1106
+ /**
1107
+ * Store definition type that can be either a class or an options object.
1108
+ */
1109
+ type StoreDefinition<S extends State, G extends Getters<S>, A extends Actions> = (new () => S) | ({
1110
+ state: S;
1111
+ getters?: G;
1112
+ actions?: A;
1113
+ } & ThisType<S & GetterValues<G> & A & StoreActions<S>>);
1114
+ /**
1115
+ * Creates a new store with the given definition.
1116
+ * The store can be defined either as a class or as an options object.
1117
+ *
1118
+ * @template S - The type of the store's state
1119
+ * @template G - The type of the store's getters
1120
+ * @template A - The type of the store's actions
1121
+ * @param storeDefinition - The store definition (class or options)
1122
+ * @returns A function that creates a new store instance
1123
+ *
1124
+ * @example
1125
+ * ```ts
1126
+ * // Options-based store
1127
+ * const useCounter = createStore({
1128
+ * state: { count: 0 },
1129
+ * getters: {
1130
+ * double: state => state.count * 2
1131
+ * },
1132
+ * actions: {
1133
+ * increment() {
1134
+ * this.count++;
1135
+ * }
1136
+ * }
1137
+ * });
1138
+ *
1139
+ * // Class-based store
1140
+ * class Counter {
1141
+ * count = 0;
1142
+ *
1143
+ * get double() {
1144
+ * return this.count * 2;
1145
+ * }
1146
+ *
1147
+ * increment() {
1148
+ * this.count++;
1149
+ * }
1150
+ * }
1151
+ *
1152
+ * const useCounter = createStore(Counter);
1153
+ * ```
1154
+ */
1155
+ declare function createStore<S extends State, G extends Getters<S>, A extends Actions>(storeDefinition: StoreDefinition<S, G, A>): () => S & GetterValues<G> & A & StoreActions<S> & {
1156
+ state: S;
1157
+ };
1158
+
1159
+ /**
1160
+ * A Ref is a special type of Signal used primarily for DOM element references.
1161
+ * It provides methods to read, write, and observe changes to the value.
1162
+ *
1163
+ * @template T - The type of value held by the ref
1164
+ */
1165
+ interface Ref<T> extends Signal<T> {
1166
+ /**
1167
+ * The current value of the ref. Reading this property will track dependencies,
1168
+ * and writing to it will notify subscribers of changes.
1169
+ */
1170
+ value: T;
1171
+ }
1172
+ /**
1173
+ * Creates a new ref with the given initial value.
1174
+ * Unlike signals, refs don't create reactive proxies for object values.
1175
+ *
1176
+ * @template T - The type of value to store in the ref
1177
+ * @param value - The initial value
1178
+ * @returns A new ref instance
1179
+ *
1180
+ * @example
1181
+ * ```ts
1182
+ * const divRef = ref();
1183
+ * <div ref={divRef}></div>
1184
+ * ```
1185
+ */
1186
+ declare function ref<T>(value?: T): Ref<T>;
1187
+ /**
1188
+ * Type guard to check if a value is a Ref instance.
1189
+ *
1190
+ * @template T - The type of value held by the ref
1191
+ * @param value - The value to check
1192
+ * @returns True if the value is a Ref instance
1193
+ */
1194
+ declare function isRef<T>(value: unknown): value is Ref<T>;
1195
+
1196
+ interface WatchOptions {
1197
+ immediate?: boolean;
1198
+ deep?: boolean;
1199
+ }
1200
+ type WatchSource<T = any> = T | {
1201
+ value: T;
1202
+ } | (() => T);
1203
+ type WatchCallback<T = any> = (newValue: T, oldValue: T | undefined) => void;
1204
+ /**
1205
+ * Watch one or more reactive data sources and execute callback when sources change.
1206
+ * @param source - The source(s) to watch.
1207
+ * @param callback - The callback function to execute when source changes.
1208
+ * @param options - Configuration options like immediate and deep.
1209
+ * @returns A function to stop watching.
1210
+ */
1211
+ declare function watch<T = any>(source: WatchSource<T>, callback: WatchCallback<T>, options?: WatchOptions): () => void;
1212
+
1213
+ export { type Computed, type ComputedGetter, type ComputedOptions, type ComputedSetter, type ComputedType, type DebuggerEvent, type DebuggerEventType, type EffectFunction, type EffectOptions, type EffectRunner, type EffectScheduler, type FlushTiming, type Job, type MemoEffectFn, type PreFlushCallback, type Reactive, type Ref, type Signal, type SignalType, type SignalValue, type StoreActions, type StoreOptions, TriggerOpTypes, type Unwrap, batch, computed, createStore, effect, endBatch, getBatchDepth, isBatching, isComputed, isEffect, isReactive, isRef, isShallow, isSignal, memoEffect, nextTick, queueJob, queuePreFlushCb, reactive, ref, shallowReactive, shallowSignal, signal, startBatch, stop, toRaw, toReactive, trigger, untrack, watch };