atomirx 0.0.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/README.md +1666 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +1440 -0
- package/coverage/coverage-final.json +14 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +131 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/core/atom.ts.html +889 -0
- package/coverage/src/core/batch.ts.html +223 -0
- package/coverage/src/core/define.ts.html +805 -0
- package/coverage/src/core/emitter.ts.html +919 -0
- package/coverage/src/core/equality.ts.html +631 -0
- package/coverage/src/core/hook.ts.html +460 -0
- package/coverage/src/core/index.html +281 -0
- package/coverage/src/core/isAtom.ts.html +100 -0
- package/coverage/src/core/isPromiseLike.ts.html +133 -0
- package/coverage/src/core/onCreateHook.ts.html +136 -0
- package/coverage/src/core/scheduleNotifyHook.ts.html +94 -0
- package/coverage/src/core/types.ts.html +523 -0
- package/coverage/src/core/withUse.ts.html +253 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/index.ts.html +106 -0
- package/dist/core/atom.d.ts +63 -0
- package/dist/core/atom.test.d.ts +1 -0
- package/dist/core/atomState.d.ts +104 -0
- package/dist/core/atomState.test.d.ts +1 -0
- package/dist/core/batch.d.ts +126 -0
- package/dist/core/batch.test.d.ts +1 -0
- package/dist/core/define.d.ts +173 -0
- package/dist/core/define.test.d.ts +1 -0
- package/dist/core/derived.d.ts +102 -0
- package/dist/core/derived.test.d.ts +1 -0
- package/dist/core/effect.d.ts +120 -0
- package/dist/core/effect.test.d.ts +1 -0
- package/dist/core/emitter.d.ts +237 -0
- package/dist/core/emitter.test.d.ts +1 -0
- package/dist/core/equality.d.ts +62 -0
- package/dist/core/equality.test.d.ts +1 -0
- package/dist/core/hook.d.ts +134 -0
- package/dist/core/hook.test.d.ts +1 -0
- package/dist/core/isAtom.d.ts +9 -0
- package/dist/core/isPromiseLike.d.ts +9 -0
- package/dist/core/isPromiseLike.test.d.ts +1 -0
- package/dist/core/onCreateHook.d.ts +79 -0
- package/dist/core/promiseCache.d.ts +134 -0
- package/dist/core/promiseCache.test.d.ts +1 -0
- package/dist/core/scheduleNotifyHook.d.ts +51 -0
- package/dist/core/select.d.ts +151 -0
- package/dist/core/selector.test.d.ts +1 -0
- package/dist/core/types.d.ts +279 -0
- package/dist/core/withUse.d.ts +38 -0
- package/dist/core/withUse.test.d.ts +1 -0
- package/dist/index-2ok7ilik.js +1217 -0
- package/dist/index-B_5SFzfl.cjs +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +20 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/react/index.cjs +30 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +823 -0
- package/dist/react/rx.d.ts +250 -0
- package/dist/react/rx.test.d.ts +1 -0
- package/dist/react/strictModeTest.d.ts +10 -0
- package/dist/react/useAction.d.ts +381 -0
- package/dist/react/useAction.test.d.ts +1 -0
- package/dist/react/useStable.d.ts +183 -0
- package/dist/react/useStable.test.d.ts +1 -0
- package/dist/react/useValue.d.ts +134 -0
- package/dist/react/useValue.test.d.ts +1 -0
- package/package.json +57 -0
- package/scripts/publish.js +198 -0
- package/src/core/atom.test.ts +369 -0
- package/src/core/atom.ts +189 -0
- package/src/core/atomState.test.ts +342 -0
- package/src/core/atomState.ts +256 -0
- package/src/core/batch.test.ts +257 -0
- package/src/core/batch.ts +172 -0
- package/src/core/define.test.ts +342 -0
- package/src/core/define.ts +243 -0
- package/src/core/derived.test.ts +381 -0
- package/src/core/derived.ts +339 -0
- package/src/core/effect.test.ts +196 -0
- package/src/core/effect.ts +184 -0
- package/src/core/emitter.test.ts +364 -0
- package/src/core/emitter.ts +392 -0
- package/src/core/equality.test.ts +392 -0
- package/src/core/equality.ts +182 -0
- package/src/core/hook.test.ts +227 -0
- package/src/core/hook.ts +177 -0
- package/src/core/isAtom.ts +27 -0
- package/src/core/isPromiseLike.test.ts +72 -0
- package/src/core/isPromiseLike.ts +16 -0
- package/src/core/onCreateHook.ts +92 -0
- package/src/core/promiseCache.test.ts +239 -0
- package/src/core/promiseCache.ts +279 -0
- package/src/core/scheduleNotifyHook.ts +53 -0
- package/src/core/select.ts +454 -0
- package/src/core/selector.test.ts +257 -0
- package/src/core/types.ts +311 -0
- package/src/core/withUse.test.ts +249 -0
- package/src/core/withUse.ts +56 -0
- package/src/index.test.ts +80 -0
- package/src/index.ts +51 -0
- package/src/react/index.ts +20 -0
- package/src/react/rx.test.tsx +416 -0
- package/src/react/rx.tsx +300 -0
- package/src/react/strictModeTest.tsx +71 -0
- package/src/react/useAction.test.ts +989 -0
- package/src/react/useAction.ts +605 -0
- package/src/react/useStable.test.ts +553 -0
- package/src/react/useStable.ts +288 -0
- package/src/react/useValue.test.ts +182 -0
- package/src/react/useValue.ts +261 -0
- package/tsconfig.json +9 -0
- package/v2.md +725 -0
- package/vite.config.ts +39 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { Listener, SingleOrMultipleListeners } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Event emitter interface for pub/sub pattern.
|
|
4
|
+
*
|
|
5
|
+
* @template T - The type of payload emitted to listeners (defaults to void)
|
|
6
|
+
*/
|
|
7
|
+
export interface Emitter<T = void> {
|
|
8
|
+
/**
|
|
9
|
+
* Subscribe to events with one or more listeners.
|
|
10
|
+
*
|
|
11
|
+
* @param listeners - Single listener or array of listeners
|
|
12
|
+
* @returns Unsubscribe function (idempotent - safe to call multiple times)
|
|
13
|
+
*/
|
|
14
|
+
on(listeners: SingleOrMultipleListeners<T>): VoidFunction;
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe with a mapping function that filters and transforms events.
|
|
17
|
+
*
|
|
18
|
+
* The map function receives the emitted value and returns either:
|
|
19
|
+
* - `{ value: TValue }` - Listener is called with the transformed value
|
|
20
|
+
* - `undefined` - Listener is NOT called (event filtered out)
|
|
21
|
+
*
|
|
22
|
+
* @template TValue - The transformed value type passed to listeners
|
|
23
|
+
* @param map - Transform function that can filter (return undefined) or map values
|
|
24
|
+
* @param listeners - Single listener or array of listeners for transformed values
|
|
25
|
+
* @returns Unsubscribe function
|
|
26
|
+
*
|
|
27
|
+
* @example Filter and transform
|
|
28
|
+
* ```ts
|
|
29
|
+
* const emitter = emitter<{ type: string; data: number }>();
|
|
30
|
+
*
|
|
31
|
+
* // Only listen to 'success' events, extract just the data
|
|
32
|
+
* emitter.on(
|
|
33
|
+
* (event) => event.type === 'success' ? { value: event.data } : undefined,
|
|
34
|
+
* (data) => console.log('Success data:', data)
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
on<TValue>(map: (value: T) => {
|
|
39
|
+
value: TValue;
|
|
40
|
+
} | undefined, listeners: SingleOrMultipleListeners<TValue>): VoidFunction;
|
|
41
|
+
/**
|
|
42
|
+
* Emit an event to all registered listeners.
|
|
43
|
+
*
|
|
44
|
+
* @param payload - The value to pass to all listeners
|
|
45
|
+
*/
|
|
46
|
+
emit(payload: T): void;
|
|
47
|
+
/**
|
|
48
|
+
* Emit an event to all registered listeners in LIFO (reverse) order.
|
|
49
|
+
* Useful for cleanup scenarios where resources should be released
|
|
50
|
+
* in reverse order of acquisition.
|
|
51
|
+
*
|
|
52
|
+
* @param payload - The value to pass to all listeners
|
|
53
|
+
*/
|
|
54
|
+
emitLifo(payload: T): void;
|
|
55
|
+
/**
|
|
56
|
+
* Remove all registered listeners.
|
|
57
|
+
*/
|
|
58
|
+
clear(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Emit an event to all listeners, then clear all listeners.
|
|
61
|
+
* Useful for one-time events like disposal.
|
|
62
|
+
*
|
|
63
|
+
* @param payload - The value to pass to all listeners
|
|
64
|
+
*/
|
|
65
|
+
emitAndClear(payload: T): void;
|
|
66
|
+
/**
|
|
67
|
+
* Emit an event to all listeners in LIFO (reverse) order, then clear.
|
|
68
|
+
* Useful for cleanup scenarios where resources should be released
|
|
69
|
+
* in reverse order of acquisition.
|
|
70
|
+
*
|
|
71
|
+
* @param payload - The value to pass to all listeners
|
|
72
|
+
*/
|
|
73
|
+
emitAndClearLifo(payload: T): void;
|
|
74
|
+
/**
|
|
75
|
+
* Emit to all listeners, clear, and "settle" the emitter.
|
|
76
|
+
*
|
|
77
|
+
* After settling:
|
|
78
|
+
* - Any new `on()` call immediately invokes the listener with the settled payload
|
|
79
|
+
* - Returns a no-op unsubscribe function
|
|
80
|
+
* - `emit()` and `emitAndClear()` become no-ops
|
|
81
|
+
*
|
|
82
|
+
* Useful for one-time events where late subscribers should still receive the value
|
|
83
|
+
* (similar to Promise behavior).
|
|
84
|
+
*
|
|
85
|
+
* @param payload - The final value to pass to all listeners
|
|
86
|
+
*/
|
|
87
|
+
settle(payload: T): void;
|
|
88
|
+
/** Number of registered listeners */
|
|
89
|
+
size(): number;
|
|
90
|
+
/** Whether the emitter has been settled */
|
|
91
|
+
settled(): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Iterate over all registered listeners.
|
|
94
|
+
* Used for batching to dedupe listeners across multiple atoms.
|
|
95
|
+
*/
|
|
96
|
+
forEach(callback: (listener: Listener<T>) => void): void;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Creates an event emitter for managing and notifying listeners.
|
|
100
|
+
*
|
|
101
|
+
* An emitter provides a simple pub/sub pattern for managing event listeners.
|
|
102
|
+
* It's used internally by atoms and effects to manage subscriptions and notifications.
|
|
103
|
+
*
|
|
104
|
+
* ## Key Features
|
|
105
|
+
*
|
|
106
|
+
* 1. **Subscribe/Unsubscribe**: Add listeners that will be notified when events are emitted
|
|
107
|
+
* 2. **Emit events**: Notify all registered listeners with a payload
|
|
108
|
+
* 3. **Mapped subscriptions**: Filter and transform events before they reach listeners
|
|
109
|
+
* 4. **LIFO emission**: Emit in reverse order for cleanup scenarios
|
|
110
|
+
* 5. **Settle pattern**: One-time events where late subscribers still receive the value
|
|
111
|
+
* 6. **Idempotent unsubscribe**: Safe to call unsubscribe multiple times
|
|
112
|
+
*
|
|
113
|
+
* ## Emission Methods
|
|
114
|
+
*
|
|
115
|
+
* | Method | Order | Clears | Settles | Use Case |
|
|
116
|
+
* |--------|-------|--------|---------|----------|
|
|
117
|
+
* | `emit()` | FIFO | No | No | Normal notifications |
|
|
118
|
+
* | `emitLifo()` | LIFO | No | No | Cleanup in reverse order |
|
|
119
|
+
* | `emitAndClear()` | FIFO | Yes | No | One-time broadcast |
|
|
120
|
+
* | `emitAndClearLifo()` | LIFO | Yes | No | One-time cleanup |
|
|
121
|
+
* | `settle()` | FIFO | Yes | Yes | Promise-like one-time events |
|
|
122
|
+
*
|
|
123
|
+
* ## Settle Behavior
|
|
124
|
+
*
|
|
125
|
+
* After `settle()` is called:
|
|
126
|
+
* - New `on()` calls immediately invoke the listener with the settled payload
|
|
127
|
+
* - Returns a no-op unsubscribe function
|
|
128
|
+
* - `emit()` and other methods become no-ops
|
|
129
|
+
*
|
|
130
|
+
* This is similar to how Promises work - late `.then()` calls still receive the value.
|
|
131
|
+
*
|
|
132
|
+
* @template T - The type of payload emitted to listeners (defaults to void)
|
|
133
|
+
* @param initialListeners - Optional array of listeners to start with
|
|
134
|
+
* @returns An Emitter instance
|
|
135
|
+
*
|
|
136
|
+
* @example Basic usage
|
|
137
|
+
* ```ts
|
|
138
|
+
* const events = emitter<string>();
|
|
139
|
+
*
|
|
140
|
+
* // Subscribe
|
|
141
|
+
* const unsubscribe = events.on((message) => {
|
|
142
|
+
* console.log('Received:', message);
|
|
143
|
+
* });
|
|
144
|
+
*
|
|
145
|
+
* // Emit
|
|
146
|
+
* events.emit('Hello'); // Logs: "Received: Hello"
|
|
147
|
+
*
|
|
148
|
+
* // Unsubscribe
|
|
149
|
+
* unsubscribe();
|
|
150
|
+
*
|
|
151
|
+
* events.emit('World'); // Nothing logged
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @example Multiple listeners
|
|
155
|
+
* ```ts
|
|
156
|
+
* const events = emitter<number>();
|
|
157
|
+
*
|
|
158
|
+
* events.on((n) => console.log('A:', n));
|
|
159
|
+
* events.on((n) => console.log('B:', n));
|
|
160
|
+
*
|
|
161
|
+
* events.emit(42);
|
|
162
|
+
* // Logs: "A: 42"
|
|
163
|
+
* // Logs: "B: 42"
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* @example Mapped subscriptions (filter and transform)
|
|
167
|
+
* ```ts
|
|
168
|
+
* const events = emitter<{ type: string; data: number }>();
|
|
169
|
+
*
|
|
170
|
+
* // Only listen to 'success' events, extract just the data
|
|
171
|
+
* events.on(
|
|
172
|
+
* (event) => event.type === 'success' ? { value: event.data } : undefined,
|
|
173
|
+
* (data) => console.log('Success data:', data)
|
|
174
|
+
* );
|
|
175
|
+
*
|
|
176
|
+
* events.emit({ type: 'error', data: 0 }); // Nothing logged (filtered)
|
|
177
|
+
* events.emit({ type: 'success', data: 42 }); // Logs: "Success data: 42"
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @example LIFO emission for cleanup
|
|
181
|
+
* ```ts
|
|
182
|
+
* const cleanup = emitter();
|
|
183
|
+
*
|
|
184
|
+
* cleanup.on(() => console.log('First registered, last to clean'));
|
|
185
|
+
* cleanup.on(() => console.log('Second registered, second to clean'));
|
|
186
|
+
* cleanup.on(() => console.log('Last registered, first to clean'));
|
|
187
|
+
*
|
|
188
|
+
* cleanup.emitLifo();
|
|
189
|
+
* // Logs in reverse order:
|
|
190
|
+
* // "Last registered, first to clean"
|
|
191
|
+
* // "Second registered, second to clean"
|
|
192
|
+
* // "First registered, last to clean"
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* @example Settle pattern (Promise-like)
|
|
196
|
+
* ```ts
|
|
197
|
+
* const ready = emitter<{ config: Config }>();
|
|
198
|
+
*
|
|
199
|
+
* // Early subscriber
|
|
200
|
+
* ready.on((payload) => console.log('Early:', payload.config));
|
|
201
|
+
*
|
|
202
|
+
* // Settle the emitter
|
|
203
|
+
* ready.settle({ config: loadedConfig });
|
|
204
|
+
* // Logs: "Early: ..."
|
|
205
|
+
*
|
|
206
|
+
* // Late subscriber - still receives the value immediately
|
|
207
|
+
* ready.on((payload) => console.log('Late:', payload.config));
|
|
208
|
+
* // Logs: "Late: ..." (immediately)
|
|
209
|
+
* ```
|
|
210
|
+
*
|
|
211
|
+
* @example Void emitter (no payload)
|
|
212
|
+
* ```ts
|
|
213
|
+
* const tick = emitter(); // emitter<void>
|
|
214
|
+
*
|
|
215
|
+
* tick.on(() => console.log('Tick!'));
|
|
216
|
+
*
|
|
217
|
+
* tick.emit(); // Logs: "Tick!"
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* @example Array of listeners
|
|
221
|
+
* ```ts
|
|
222
|
+
* const events = emitter<string>();
|
|
223
|
+
*
|
|
224
|
+
* // Subscribe multiple listeners at once
|
|
225
|
+
* const unsubscribe = events.on([
|
|
226
|
+
* (msg) => console.log('Logger 1:', msg),
|
|
227
|
+
* (msg) => console.log('Logger 2:', msg),
|
|
228
|
+
* ]);
|
|
229
|
+
*
|
|
230
|
+
* events.emit('Hello');
|
|
231
|
+
* // Logs: "Logger 1: Hello"
|
|
232
|
+
* // Logs: "Logger 2: Hello"
|
|
233
|
+
*
|
|
234
|
+
* unsubscribe(); // Removes both listeners
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export declare function emitter<T = void>(initialListeners?: Listener<T>[]): Emitter<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Equality, EqualityShorthand } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Strict equality (Object.is).
|
|
4
|
+
*/
|
|
5
|
+
export declare function strictEqual<T>(a: T, b: T): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Shallow equality for objects/arrays.
|
|
8
|
+
* Compares by reference for each top-level key/index.
|
|
9
|
+
*
|
|
10
|
+
* @param itemEqual - Optional comparator for each item/value (defaults to Object.is)
|
|
11
|
+
*/
|
|
12
|
+
export declare function shallowEqual<T>(a: T, b: T, itemEqual?: (a: unknown, b: unknown) => boolean): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 2-level shallow equality.
|
|
15
|
+
* Compares keys/length, then shallow compares each item/value.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* [{ id: 1, data: obj }] vs [{ id: 1, data: obj }] // true (same obj ref)
|
|
19
|
+
*/
|
|
20
|
+
export declare function shallow2Equal<T>(a: T, b: T): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* 3-level shallow equality.
|
|
23
|
+
* Compares keys/length, then shallow2 compares each item/value.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* [{ id: 1, nested: { data: obj } }] vs [{ id: 1, nested: { data: obj } }] // true
|
|
27
|
+
*/
|
|
28
|
+
export declare function shallow3Equal<T>(a: T, b: T): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Deep equality.
|
|
31
|
+
*/
|
|
32
|
+
export declare const deepEqual: (value: any, other: any) => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve equality strategy to a function.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveEquality<T>(e: Equality<T> | undefined): (a: T, b: T) => boolean;
|
|
37
|
+
export declare function equality(shorthand: EqualityShorthand): (a: unknown, b: unknown) => boolean;
|
|
38
|
+
export type StableFn<TArgs extends any[], TResult> = ((...args: TArgs) => TResult) & {
|
|
39
|
+
getOriginal: () => (...args: TArgs) => TResult;
|
|
40
|
+
getCurrent: () => (...args: TArgs) => TResult;
|
|
41
|
+
setCurrent: (newFn: (...args: TArgs) => TResult) => void;
|
|
42
|
+
};
|
|
43
|
+
export declare function createStableFn<TArgs extends any[], TResult>(fn: (...args: TArgs) => TResult): StableFn<TArgs, TResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Check if a value is a stable function wrapper.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isStableFn<TArgs extends any[], TResult>(value: unknown): value is StableFn<TArgs, TResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Stabilize a value with automatic function wrapper support.
|
|
50
|
+
*
|
|
51
|
+
* - Functions: Creates/updates stable wrapper (reference never changes)
|
|
52
|
+
* - Date objects: Compared by timestamp (uses deepEqual)
|
|
53
|
+
* - Other values: Returns previous if equal per equalityFn
|
|
54
|
+
*
|
|
55
|
+
* @param prev - Previous value container (or undefined for first call)
|
|
56
|
+
* @param next - New value
|
|
57
|
+
* @param equalityFn - Equality function for non-function/non-date values
|
|
58
|
+
* @returns Tuple of [stabilized value, wasStable]
|
|
59
|
+
*/
|
|
60
|
+
export declare function tryStabilize<T>(prev: {
|
|
61
|
+
value: T;
|
|
62
|
+
} | undefined, next: T, equalityFn: (a: T, b: T) => boolean): [T, boolean];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A setup function that returns a release function.
|
|
3
|
+
* Called by hook.use() to activate a hook, release restores previous value.
|
|
4
|
+
*/
|
|
5
|
+
export type HookSetup = () => VoidFunction;
|
|
6
|
+
/**
|
|
7
|
+
* A hook is a callable factory that creates setup functions,
|
|
8
|
+
* with direct access to current value via `.current`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const myHook = hook<string>("default");
|
|
13
|
+
*
|
|
14
|
+
* // Read current value (fast - direct property access)
|
|
15
|
+
* console.log(myHook.current); // "default"
|
|
16
|
+
*
|
|
17
|
+
* // Create a setup function (reducer receives previous value)
|
|
18
|
+
* const setup = myHook(() => "new value");
|
|
19
|
+
*
|
|
20
|
+
* // Use with hook.use()
|
|
21
|
+
* hook.use([myHook(() => "temp")], () => {
|
|
22
|
+
* console.log(myHook.current); // "temp"
|
|
23
|
+
* });
|
|
24
|
+
* console.log(myHook.current); // "default" (restored)
|
|
25
|
+
*
|
|
26
|
+
* // Compose with previous value
|
|
27
|
+
* myHook.override(prev => {
|
|
28
|
+
* console.log("Previous:", prev);
|
|
29
|
+
* return "next";
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export interface Hook<T> {
|
|
34
|
+
/**
|
|
35
|
+
* Creates a HookSetup that will set this hook using a reducer.
|
|
36
|
+
* The reducer receives the previous value and returns the next value.
|
|
37
|
+
*
|
|
38
|
+
* @param reducer - Function that receives previous value and returns next value
|
|
39
|
+
*
|
|
40
|
+
* @example Set new value (ignore previous)
|
|
41
|
+
* ```ts
|
|
42
|
+
* myHook(() => "new value")
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example Compose with previous
|
|
46
|
+
* ```ts
|
|
47
|
+
* myHook(prev => {
|
|
48
|
+
* prev?.(); // call previous handler
|
|
49
|
+
* return newHandler;
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
(reducer: (prev: T) => T): HookSetup;
|
|
54
|
+
/**
|
|
55
|
+
* Current value of the hook. Direct property access for fast reads.
|
|
56
|
+
*/
|
|
57
|
+
current: T;
|
|
58
|
+
/**
|
|
59
|
+
* Override the current value using a reducer.
|
|
60
|
+
* The reducer receives the previous value and returns the next value.
|
|
61
|
+
* Unlike the setup/release pattern, this is an immediate mutation.
|
|
62
|
+
*
|
|
63
|
+
* @param reducer - Function that receives previous value and returns next value
|
|
64
|
+
*
|
|
65
|
+
* @example Set new value (ignore previous)
|
|
66
|
+
* ```ts
|
|
67
|
+
* myHook.override(() => "new value")
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @example Compose with previous (middleware pattern)
|
|
71
|
+
* ```ts
|
|
72
|
+
* onCreateHook.override(prev => (info) => {
|
|
73
|
+
* prev?.(info); // call existing handler
|
|
74
|
+
* console.log("Created:", info.key);
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
override(reducer: (prev: T) => T): void;
|
|
79
|
+
/**
|
|
80
|
+
* Reset the hook to its initial value.
|
|
81
|
+
*/
|
|
82
|
+
reset(): void;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a new hook with an initial value.
|
|
86
|
+
*
|
|
87
|
+
* Hooks use the setup/release pattern for performance:
|
|
88
|
+
* - Reads are direct property access (fastest)
|
|
89
|
+
* - Writes use setup/release for proper nesting
|
|
90
|
+
*
|
|
91
|
+
* @param initial - Initial value for the hook
|
|
92
|
+
* @returns A Hook instance
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* // Create a hook
|
|
97
|
+
* const countHook = hook(0);
|
|
98
|
+
*
|
|
99
|
+
* // Read
|
|
100
|
+
* console.log(countHook.current); // 0
|
|
101
|
+
*
|
|
102
|
+
* // Use with hook.use() - reducer receives previous value
|
|
103
|
+
* hook.use([countHook(() => 5)], () => {
|
|
104
|
+
* console.log(countHook.current); // 5
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* // Compose with previous (middleware pattern)
|
|
108
|
+
* myHook.override(prev => (info) => {
|
|
109
|
+
* prev?.(info); // call existing
|
|
110
|
+
* newHandler(info);
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
declare function createHook<T>(initial: T): Hook<T>;
|
|
115
|
+
declare function createHook<T>(): Hook<T | undefined>;
|
|
116
|
+
/**
|
|
117
|
+
* Executes a function with multiple hooks temporarily set.
|
|
118
|
+
*
|
|
119
|
+
* @param setups - Array of HookSetup functions (from hook factories)
|
|
120
|
+
* @param fn - Function to execute with hooks active
|
|
121
|
+
* @returns The return value of fn
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* hook.use([trackHook(myTracker), debugHook(true)], () => {
|
|
126
|
+
* // hooks active here
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
declare function use<T>(setups: HookSetup[], fn: () => T): T;
|
|
131
|
+
export declare const hook: typeof createHook & {
|
|
132
|
+
use: typeof use;
|
|
133
|
+
};
|
|
134
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Atom, DerivedAtom } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Type guard to check if a value is an Atom.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isAtom<T>(value: unknown): value is Atom<T>;
|
|
6
|
+
/**
|
|
7
|
+
* Type guard to check if a value is a DerivedAtom.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isDerived<T>(value: unknown): value is DerivedAtom<T, boolean>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a value is a PromiseLike (has a .then method).
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* isPromiseLike(Promise.resolve(1)) // true
|
|
6
|
+
* isPromiseLike({ then: () => {} }) // true
|
|
7
|
+
* isPromiseLike(42) // false
|
|
8
|
+
*/
|
|
9
|
+
export declare function isPromiseLike<T>(value: unknown): value is PromiseLike<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { MutableAtomMeta, DerivedAtomMeta, MutableAtom, DerivedAtom, ModuleMeta } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Information provided when a mutable atom is created.
|
|
4
|
+
*/
|
|
5
|
+
export interface MutableAtomCreateInfo {
|
|
6
|
+
/** Discriminator for mutable atoms */
|
|
7
|
+
type: "mutable";
|
|
8
|
+
/** Optional key from atom options (for debugging/devtools) */
|
|
9
|
+
key: string | undefined;
|
|
10
|
+
/** Optional metadata from atom options */
|
|
11
|
+
meta: MutableAtomMeta | undefined;
|
|
12
|
+
/** The created mutable atom instance */
|
|
13
|
+
atom: MutableAtom<unknown>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Information provided when a derived atom is created.
|
|
17
|
+
*/
|
|
18
|
+
export interface DerivedAtomCreateInfo {
|
|
19
|
+
/** Discriminator for derived atoms */
|
|
20
|
+
type: "derived";
|
|
21
|
+
/** Optional key from derived options (for debugging/devtools) */
|
|
22
|
+
key: string | undefined;
|
|
23
|
+
/** Optional metadata from derived options */
|
|
24
|
+
meta: DerivedAtomMeta | undefined;
|
|
25
|
+
/** The created derived atom instance */
|
|
26
|
+
atom: DerivedAtom<unknown, boolean>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Union type for atom creation info (mutable or derived).
|
|
30
|
+
*/
|
|
31
|
+
export type AtomCreateInfo = MutableAtomCreateInfo | DerivedAtomCreateInfo;
|
|
32
|
+
/**
|
|
33
|
+
* Information provided when a module (via define()) is created.
|
|
34
|
+
*/
|
|
35
|
+
export interface ModuleCreateInfo {
|
|
36
|
+
/** Discriminator for modules */
|
|
37
|
+
type: "module";
|
|
38
|
+
/** Optional key from define options (for debugging/devtools) */
|
|
39
|
+
key: string | undefined;
|
|
40
|
+
/** Optional metadata from define options */
|
|
41
|
+
meta: ModuleMeta | undefined;
|
|
42
|
+
/** The created module instance */
|
|
43
|
+
module: unknown;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Global hook that fires whenever an atom or module is created.
|
|
47
|
+
*
|
|
48
|
+
* This is useful for:
|
|
49
|
+
* - **DevTools integration** - track all atoms/modules in the app
|
|
50
|
+
* - **Debugging** - log atom creation for troubleshooting
|
|
51
|
+
* - **Testing** - verify expected atoms are created
|
|
52
|
+
*
|
|
53
|
+
* @example Basic logging
|
|
54
|
+
* ```ts
|
|
55
|
+
* onCreateHook.current = (info) => {
|
|
56
|
+
* console.log(`Created ${info.type}: ${info.key ?? "anonymous"}`);
|
|
57
|
+
* };
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example DevTools integration
|
|
61
|
+
* ```ts
|
|
62
|
+
* const atoms = new Map();
|
|
63
|
+
* const modules = new Map();
|
|
64
|
+
*
|
|
65
|
+
* onCreateHook.current = (info) => {
|
|
66
|
+
* if (info.type === "module") {
|
|
67
|
+
* modules.set(info.key, info.module);
|
|
68
|
+
* } else {
|
|
69
|
+
* atoms.set(info.key, info.atom);
|
|
70
|
+
* }
|
|
71
|
+
* };
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example Cleanup (disable hook)
|
|
75
|
+
* ```ts
|
|
76
|
+
* onCreateHook.current = undefined;
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare const onCreateHook: import('./hook').Hook<((info: AtomCreateInfo | ModuleCreateInfo) => void) | undefined>;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Atom, AtomState, DerivedAtom } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Represents the state of a tracked Promise.
|
|
4
|
+
*/
|
|
5
|
+
export type PromiseState<T> = {
|
|
6
|
+
status: "pending";
|
|
7
|
+
promise: PromiseLike<T>;
|
|
8
|
+
} | {
|
|
9
|
+
status: "fulfilled";
|
|
10
|
+
value: T;
|
|
11
|
+
} | {
|
|
12
|
+
status: "rejected";
|
|
13
|
+
error: unknown;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Tracks a Promise and caches its state.
|
|
17
|
+
* If the Promise is already tracked, returns the existing state.
|
|
18
|
+
* Otherwise, starts tracking and returns the initial pending state.
|
|
19
|
+
*
|
|
20
|
+
* @param promise - The Promise to track
|
|
21
|
+
* @returns The current state of the Promise
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const promise = fetchData();
|
|
26
|
+
* const state = trackPromise(promise);
|
|
27
|
+
* // state.status === 'pending'
|
|
28
|
+
*
|
|
29
|
+
* await promise;
|
|
30
|
+
* const state2 = trackPromise(promise);
|
|
31
|
+
* // state2.status === 'fulfilled'
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function trackPromise<T>(promise: PromiseLike<T>): PromiseState<T>;
|
|
35
|
+
/**
|
|
36
|
+
* Gets the current state of a Promise without tracking it.
|
|
37
|
+
* Returns undefined if the Promise is not being tracked.
|
|
38
|
+
*
|
|
39
|
+
* @param promise - The Promise to check
|
|
40
|
+
* @returns The current state or undefined if not tracked
|
|
41
|
+
*/
|
|
42
|
+
export declare function getPromiseState<T>(promise: PromiseLike<T>): PromiseState<T> | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Checks if a Promise is being tracked.
|
|
45
|
+
*
|
|
46
|
+
* @param promise - The Promise to check
|
|
47
|
+
* @returns true if the Promise is tracked
|
|
48
|
+
*/
|
|
49
|
+
export declare function isTracked(promise: PromiseLike<unknown>): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Type guard to check if a value is a DerivedAtom.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isDerived<T>(value: unknown): value is DerivedAtom<T, boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Returns the current state of an atom as a discriminated union.
|
|
56
|
+
*
|
|
57
|
+
* For DerivedAtom:
|
|
58
|
+
* - Returns atom.state() directly (derived atoms track their own state)
|
|
59
|
+
*
|
|
60
|
+
* For MutableAtom:
|
|
61
|
+
* - If value is not a Promise: returns ready state
|
|
62
|
+
* - If value is a Promise: tracks and returns its state (ready/error/loading)
|
|
63
|
+
*
|
|
64
|
+
* @param atom - The atom to get state from
|
|
65
|
+
* @returns AtomState discriminated union (ready | error | loading)
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const state = getAtomState(myAtom$);
|
|
70
|
+
*
|
|
71
|
+
* switch (state.status) {
|
|
72
|
+
* case "ready":
|
|
73
|
+
* console.log(state.value); // T
|
|
74
|
+
* break;
|
|
75
|
+
* case "error":
|
|
76
|
+
* console.log(state.error);
|
|
77
|
+
* break;
|
|
78
|
+
* case "loading":
|
|
79
|
+
* console.log(state.promise);
|
|
80
|
+
* break;
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function getAtomState<T>(atom: Atom<T>): AtomState<Awaited<T>>;
|
|
85
|
+
/**
|
|
86
|
+
* Unwraps a value that may be a Promise.
|
|
87
|
+
* - If not a Promise, returns the value directly.
|
|
88
|
+
* - If a resolved Promise, returns the resolved value.
|
|
89
|
+
* - If a loading Promise, throws the Promise (for Suspense).
|
|
90
|
+
* - If a rejected Promise, throws the error.
|
|
91
|
+
*
|
|
92
|
+
* This follows the React Suspense pattern where throwing a Promise
|
|
93
|
+
* signals that the component should suspend until the Promise resolves.
|
|
94
|
+
*
|
|
95
|
+
* @param value - The value to unwrap (may be a Promise)
|
|
96
|
+
* @returns The unwrapped value
|
|
97
|
+
* @throws Promise if loading, Error if rejected
|
|
98
|
+
*/
|
|
99
|
+
export declare function unwrap<T>(value: T | PromiseLike<T>): T;
|
|
100
|
+
/**
|
|
101
|
+
* Checks if a value is a pending Promise.
|
|
102
|
+
*
|
|
103
|
+
* @param value - The value to check
|
|
104
|
+
* @returns true if value is a Promise in pending state
|
|
105
|
+
*/
|
|
106
|
+
export declare function isPending<T>(value: T | PromiseLike<T>): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Checks if a value is a fulfilled Promise.
|
|
109
|
+
*
|
|
110
|
+
* @param value - The value to check
|
|
111
|
+
* @returns true if value is a Promise in fulfilled state
|
|
112
|
+
*/
|
|
113
|
+
export declare function isFulfilled<T>(value: T | PromiseLike<T>): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Checks if a value is a rejected Promise.
|
|
116
|
+
*
|
|
117
|
+
* @param value - The value to check
|
|
118
|
+
* @returns true if value is a Promise in rejected state
|
|
119
|
+
*/
|
|
120
|
+
export declare function isRejected<T>(value: T | PromiseLike<T>): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Gets the resolved value of a Promise if fulfilled, otherwise undefined.
|
|
123
|
+
*
|
|
124
|
+
* @param value - The value to check
|
|
125
|
+
* @returns The resolved value or undefined
|
|
126
|
+
*/
|
|
127
|
+
export declare function getResolvedValue<T>(value: T | PromiseLike<T>): T | undefined;
|
|
128
|
+
/**
|
|
129
|
+
* Gets the error of a Promise if rejected, otherwise undefined.
|
|
130
|
+
*
|
|
131
|
+
* @param value - The value to check
|
|
132
|
+
* @returns The error or undefined
|
|
133
|
+
*/
|
|
134
|
+
export declare function getRejectedError<T>(value: T | PromiseLike<T>): unknown | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|