@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.
- package/LICENSE +20 -0
- package/README.md +29 -0
- package/dist/signals.cjs.js +10 -0
- package/dist/signals.cjs.js.map +1 -0
- package/dist/signals.d.cts +1213 -0
- package/dist/signals.d.ts +1213 -0
- package/dist/signals.dev.cjs.js +1947 -0
- package/dist/signals.dev.esm.js +1916 -0
- package/dist/signals.esm.js +10 -0
- package/dist/signals.esm.js.map +1 -0
- package/package.json +62 -0
|
@@ -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 };
|