atomirx 0.0.1 → 0.0.4

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.
Files changed (53) hide show
  1. package/README.md +867 -160
  2. package/dist/core/atom.d.ts +83 -6
  3. package/dist/core/batch.d.ts +3 -3
  4. package/dist/core/derived.d.ts +55 -21
  5. package/dist/core/effect.d.ts +47 -51
  6. package/dist/core/getAtomState.d.ts +29 -0
  7. package/dist/core/promiseCache.d.ts +23 -32
  8. package/dist/core/select.d.ts +208 -29
  9. package/dist/core/types.d.ts +55 -19
  10. package/dist/core/withReady.d.ts +69 -0
  11. package/dist/index-CqO6BDwj.cjs +1 -0
  12. package/dist/index-D8RDOTB_.js +1319 -0
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.ts +9 -7
  15. package/dist/index.js +12 -10
  16. package/dist/react/index.cjs +10 -10
  17. package/dist/react/index.d.ts +2 -1
  18. package/dist/react/index.js +423 -379
  19. package/dist/react/rx.d.ts +114 -25
  20. package/dist/react/useAction.d.ts +5 -4
  21. package/dist/react/{useValue.d.ts → useSelector.d.ts} +56 -25
  22. package/dist/react/useSelector.test.d.ts +1 -0
  23. package/package.json +17 -1
  24. package/src/core/atom.test.ts +307 -43
  25. package/src/core/atom.ts +143 -21
  26. package/src/core/batch.test.ts +10 -10
  27. package/src/core/batch.ts +3 -3
  28. package/src/core/derived.test.ts +727 -72
  29. package/src/core/derived.ts +141 -73
  30. package/src/core/effect.test.ts +259 -39
  31. package/src/core/effect.ts +62 -85
  32. package/src/core/getAtomState.ts +69 -0
  33. package/src/core/promiseCache.test.ts +5 -3
  34. package/src/core/promiseCache.ts +76 -71
  35. package/src/core/select.ts +405 -130
  36. package/src/core/selector.test.ts +574 -32
  37. package/src/core/types.ts +54 -26
  38. package/src/core/withReady.test.ts +360 -0
  39. package/src/core/withReady.ts +127 -0
  40. package/src/core/withUse.ts +1 -1
  41. package/src/index.test.ts +4 -4
  42. package/src/index.ts +11 -6
  43. package/src/react/index.ts +2 -1
  44. package/src/react/rx.test.tsx +173 -18
  45. package/src/react/rx.tsx +274 -43
  46. package/src/react/useAction.test.ts +12 -14
  47. package/src/react/useAction.ts +11 -9
  48. package/src/react/{useValue.test.ts → useSelector.test.ts} +16 -16
  49. package/src/react/{useValue.ts → useSelector.ts} +64 -33
  50. package/v2.md +44 -44
  51. package/dist/index-2ok7ilik.js +0 -1217
  52. package/dist/index-B_5SFzfl.cjs +0 -1
  53. /package/dist/{react/useValue.test.d.ts → core/withReady.test.d.ts} +0 -0
@@ -1,9 +1,27 @@
1
- import { AtomOptions, MutableAtom } from './types';
1
+ import { AtomOptions, MutableAtom, Pipeable, Atom } from './types';
2
+ /**
3
+ * Context object passed to atom initializer functions.
4
+ * Provides utilities for cleanup and cancellation.
5
+ */
6
+ export interface AtomContext extends Pipeable {
7
+ /**
8
+ * AbortSignal that is aborted when the atom value changes (via set or reset).
9
+ * Use this to cancel pending async operations.
10
+ */
11
+ signal: AbortSignal;
12
+ /**
13
+ * Register a cleanup function that runs when the atom value changes or resets.
14
+ * Multiple cleanup functions can be registered; they run in FIFO order.
15
+ *
16
+ * @param cleanup - Function to run during cleanup
17
+ */
18
+ onCleanup(cleanup: VoidFunction): void;
19
+ }
2
20
  /**
3
21
  * Creates a mutable atom - a reactive state container that holds a single value.
4
22
  *
5
23
  * MutableAtom is a raw storage container. It stores values as-is, including Promises.
6
- * If you store a Promise, `.value` returns the Promise object itself.
24
+ * If you store a Promise, `.get()` returns the Promise object itself.
7
25
  *
8
26
  * Features:
9
27
  * - Raw storage: stores any value including Promises
@@ -17,14 +35,14 @@ import { AtomOptions, MutableAtom } from './types';
17
35
  * @param options - Configuration options
18
36
  * @param options.meta - Optional metadata for debugging/devtools
19
37
  * @param options.equals - Equality strategy for change detection (default: strict)
20
- * @returns A mutable atom with value, set/reset methods
38
+ * @returns A mutable atom with get, set/reset methods
21
39
  *
22
40
  * @example Synchronous value
23
41
  * ```ts
24
42
  * const count = atom(0);
25
43
  * count.set(1);
26
44
  * count.set(prev => prev + 1);
27
- * console.log(count.value); // 2
45
+ * console.log(count.get()); // 2
28
46
  * ```
29
47
  *
30
48
  * @example Lazy initialization
@@ -43,7 +61,7 @@ import { AtomOptions, MutableAtom } from './types';
43
61
  * @example Async value (stores Promise as-is)
44
62
  * ```ts
45
63
  * const posts = atom(fetchPosts());
46
- * posts.value; // Promise<Post[]>
64
+ * posts.get(); // Promise<Post[]>
47
65
  *
48
66
  * // Refetch - set a new Promise
49
67
  * posts.set(fetchPosts());
@@ -60,4 +78,63 @@ import { AtomOptions, MutableAtom } from './types';
60
78
  * state.set(prev => ({ ...prev })); // No notification (shallow equal)
61
79
  * ```
62
80
  */
63
- export declare function atom<T>(valueOrInit: T | (() => T), options?: AtomOptions<T>): MutableAtom<T>;
81
+ export declare function atom<T>(valueOrInit: T | ((context: AtomContext) => T), options?: AtomOptions<T>): MutableAtom<T>;
82
+ /**
83
+ * Type utility to expose an atom as read-only when exporting from a module.
84
+ *
85
+ * This function returns the same atom instance but with a narrowed type (`Atom<T>`)
86
+ * that hides mutable methods like `set()` and `reset()`. Use this to encapsulate
87
+ * state mutations within a module while allowing external consumers to only read
88
+ * and subscribe to changes.
89
+ *
90
+ * **Note:** This is a compile-time restriction only. At runtime, the atom is unchanged.
91
+ * Consumers with access to the original reference can still mutate it.
92
+ *
93
+ * @param atom - The atom (or record of atoms) to expose as read-only
94
+ * @returns The same atom(s) with a read-only type signature
95
+ *
96
+ * @example Single atom
97
+ * ```ts
98
+ * const myModule = define(() => {
99
+ * const count$ = atom(0); // Internal mutable atom
100
+ *
101
+ * return {
102
+ * // Expose as read-only - consumers can't call set() or reset()
103
+ * count$: readonly(count$),
104
+ * // Mutations only possible through explicit actions
105
+ * increment: () => count$.set(prev => prev + 1),
106
+ * decrement: () => count$.set(prev => prev - 1),
107
+ * };
108
+ * });
109
+ *
110
+ * // Usage:
111
+ * const { count$, increment } = myModule();
112
+ * count$.get(); // ✅ OK - reading is allowed
113
+ * count$.on(console.log); // ✅ OK - subscribing is allowed
114
+ * count$.set(5); // ❌ TypeScript error - set() not available on Atom<T>
115
+ * increment(); // ✅ OK - use exposed action instead
116
+ * ```
117
+ *
118
+ * @example Record of atoms
119
+ * ```ts
120
+ * const myModule = define(() => {
121
+ * const count$ = atom(0);
122
+ * const name$ = atom('');
123
+ *
124
+ * return {
125
+ * // Expose multiple atoms as read-only at once
126
+ * ...readonly({ count$, name$ }),
127
+ * setName: (name: string) => name$.set(name),
128
+ * };
129
+ * });
130
+ *
131
+ * // Usage:
132
+ * const { count$, name$, setName } = myModule();
133
+ * count$.get(); // ✅ Atom<number>
134
+ * name$.get(); // ✅ Atom<string>
135
+ * name$.set(''); // ❌ TypeScript error
136
+ * ```
137
+ */
138
+ export declare function readonly<T extends Atom<any> | Record<string, Atom<any>>>(atom: T): T extends Atom<infer V> ? Atom<V> : {
139
+ [K in keyof T]: T[K] extends Atom<infer V> ? Atom<V> : never;
140
+ };
@@ -54,7 +54,7 @@
54
54
  * ```ts
55
55
  * const counter = atom(0);
56
56
  *
57
- * counter.on(() => console.log("Counter:", counter.value));
57
+ * counter.on(() => console.log("Counter:", counter.get()));
58
58
  *
59
59
  * batch(() => {
60
60
  * counter.set(1);
@@ -70,7 +70,7 @@
70
70
  * const b = atom(0);
71
71
  *
72
72
  * // Same listener subscribed to both atoms
73
- * const listener = () => console.log("Changed!", a.value, b.value);
73
+ * const listener = () => console.log("Changed!", a.get(), b.get());
74
74
  * a.on(listener);
75
75
  * b.on(listener);
76
76
  *
@@ -98,7 +98,7 @@
98
98
  * ```ts
99
99
  * const result = batch(() => {
100
100
  * counter.set(10);
101
- * return counter.value * 2;
101
+ * return counter.get() * 2;
102
102
  * });
103
103
  * console.log(result); // 20
104
104
  * ```
@@ -1,19 +1,20 @@
1
- import { SelectContext } from './select';
1
+ import { ReactiveSelector, SelectContext } from './select';
2
2
  import { DerivedAtom, DerivedOptions } from './types';
3
+ import { WithReadySelectContext } from './withReady';
3
4
  /**
4
5
  * Context object passed to derived atom selector functions.
5
- * Provides utilities for reading atoms: `{ get, all, any, race, settled }`.
6
+ * Provides utilities for reading atoms: `{ read, all, any, race, settled }`.
6
7
  *
7
8
  * Currently identical to `SelectContext`, but defined separately to allow
8
9
  * future derived-specific extensions without breaking changes.
9
10
  */
10
- export interface DerivedContext extends SelectContext {
11
+ export interface DerivedContext extends SelectContext, WithReadySelectContext {
11
12
  }
12
13
  /**
13
14
  * Creates a derived (computed) atom from source atom(s).
14
15
  *
15
16
  * Derived atoms are **read-only** and automatically recompute when their
16
- * source atoms change. The `.value` property always returns a `Promise<T>`,
17
+ * source atoms change. The `.get()` method always returns a `Promise<T>`,
17
18
  * even for synchronous computations.
18
19
  *
19
20
  * ## IMPORTANT: Selector Must Return Synchronous Value
@@ -22,35 +23,68 @@ export interface DerivedContext extends SelectContext {
22
23
  *
23
24
  * ```ts
24
25
  * // ❌ WRONG - Don't use async function
25
- * derived(async ({ get }) => {
26
+ * derived(async ({ read }) => {
26
27
  * const data = await fetch('/api');
27
28
  * return data;
28
29
  * });
29
30
  *
30
31
  * // ❌ WRONG - Don't return a Promise
31
- * derived(({ get }) => fetch('/api').then(r => r.json()));
32
+ * derived(({ read }) => fetch('/api').then(r => r.json()));
32
33
  *
33
- * // ✅ CORRECT - Create async atom and read with get()
34
+ * // ✅ CORRECT - Create async atom and read with read()
34
35
  * const data$ = atom(fetch('/api').then(r => r.json()));
35
- * derived(({ get }) => get(data$)); // Suspends until resolved
36
+ * derived(({ read }) => read(data$)); // Suspends until resolved
36
37
  * ```
37
38
  *
39
+ * ## IMPORTANT: Do NOT Use try/catch - Use safe() Instead
40
+ *
41
+ * **Never wrap `read()` calls in try/catch blocks.** The `read()` function throws
42
+ * Promises when atoms are loading (Suspense pattern). A try/catch will catch
43
+ * these Promises and break the Suspense mechanism.
44
+ *
45
+ * ```ts
46
+ * // ❌ WRONG - Catches Suspense Promise, breaks loading state
47
+ * derived(({ read }) => {
48
+ * try {
49
+ * return read(asyncAtom$);
50
+ * } catch (e) {
51
+ * return 'fallback'; // This catches BOTH errors AND loading promises!
52
+ * }
53
+ * });
54
+ *
55
+ * // ✅ CORRECT - Use safe() to catch errors but preserve Suspense
56
+ * derived(({ read, safe }) => {
57
+ * const [err, data] = safe(() => {
58
+ * const raw = read(asyncAtom$); // Can throw Promise (Suspense)
59
+ * return JSON.parse(raw); // Can throw Error
60
+ * });
61
+ *
62
+ * if (err) return { error: err.message };
63
+ * return { data };
64
+ * });
65
+ * ```
66
+ *
67
+ * The `safe()` utility:
68
+ * - **Catches errors** and returns `[error, undefined]`
69
+ * - **Re-throws Promises** to preserve Suspense behavior
70
+ * - Returns `[undefined, result]` on success
71
+ *
38
72
  * ## Key Features
39
73
  *
40
- * 1. **Always async**: `.value` returns `Promise<T>`
74
+ * 1. **Always async**: `.get()` returns `Promise<T>`
41
75
  * 2. **Lazy computation**: Value is computed on first access
42
76
  * 3. **Automatic updates**: Recomputes when any source atom changes
43
77
  * 4. **Equality checking**: Only notifies if derived value changed
44
78
  * 5. **Fallback support**: Optional fallback for loading/error states
45
- * 6. **Suspense-like async**: `get()` throws promise if loading
79
+ * 6. **Suspense-like async**: `read()` throws promise if loading
46
80
  * 7. **Conditional dependencies**: Only subscribes to atoms accessed
47
81
  *
48
- * ## Suspense-Style get()
82
+ * ## Suspense-Style read()
49
83
  *
50
- * The `get()` function behaves like React Suspense:
51
- * - If source atom is **loading**: `get()` throws the promise
52
- * - If source atom has **error**: `get()` throws the error
53
- * - If source atom has **value**: `get()` returns the value
84
+ * The `read()` function behaves like React Suspense:
85
+ * - If source atom is **loading**: `read()` throws the promise
86
+ * - If source atom has **error**: `read()` throws the error
87
+ * - If source atom has **value**: `read()` returns the value
54
88
  *
55
89
  * @template T - Derived value type
56
90
  * @template F - Whether fallback is provided
@@ -62,9 +96,9 @@ export interface DerivedContext extends SelectContext {
62
96
  * @example Basic derived (no fallback)
63
97
  * ```ts
64
98
  * const count$ = atom(5);
65
- * const doubled$ = derived(({ get }) => get(count$) * 2);
99
+ * const doubled$ = derived(({ read }) => read(count$) * 2);
66
100
  *
67
- * await doubled$.value; // 10
101
+ * await doubled$.get(); // 10
68
102
  * doubled$.staleValue; // undefined (until first resolve) -> 10
69
103
  * doubled$.state(); // { status: "ready", value: 10 }
70
104
  * ```
@@ -72,7 +106,7 @@ export interface DerivedContext extends SelectContext {
72
106
  * @example With fallback
73
107
  * ```ts
74
108
  * const posts$ = atom(fetchPosts());
75
- * const count$ = derived(({ get }) => get(posts$).length, { fallback: 0 });
109
+ * const count$ = derived(({ read }) => read(posts$).length, { fallback: 0 });
76
110
  *
77
111
  * count$.staleValue; // 0 (during loading) -> 42 (after resolve)
78
112
  * count$.state(); // { status: "loading", promise } during loading
@@ -92,11 +126,11 @@ export interface DerivedContext extends SelectContext {
92
126
  *
93
127
  * @example Refresh
94
128
  * ```ts
95
- * const data$ = derived(({ get }) => get(source$));
129
+ * const data$ = derived(({ read }) => read(source$));
96
130
  * data$.refresh(); // Re-run computation
97
131
  * ```
98
132
  */
99
- export declare function derived<T>(fn: (ctx: DerivedContext) => T, options?: DerivedOptions<T>): DerivedAtom<T, false>;
100
- export declare function derived<T>(fn: (ctx: DerivedContext) => T, options: DerivedOptions<T> & {
133
+ export declare function derived<T>(fn: ReactiveSelector<T, DerivedContext>, options?: DerivedOptions<T>): DerivedAtom<T, false>;
134
+ export declare function derived<T>(fn: ReactiveSelector<T, DerivedContext>, options: DerivedOptions<T> & {
101
135
  fallback: T;
102
136
  }): DerivedAtom<T, true>;
@@ -1,10 +1,11 @@
1
- import { SelectContext } from './select';
1
+ import { ReactiveSelector, SelectContext } from './select';
2
2
  import { EffectOptions } from './types';
3
+ import { WithReadySelectContext } from './withReady';
3
4
  /**
4
5
  * Context object passed to effect functions.
5
- * Extends `SelectContext` with cleanup and error handling utilities.
6
+ * Extends `SelectContext` with cleanup utilities.
6
7
  */
7
- export interface EffectContext extends SelectContext {
8
+ export interface EffectContext extends SelectContext, WithReadySelectContext {
8
9
  /**
9
10
  * Register a cleanup function that runs before the next execution or on dispose.
10
11
  * Multiple cleanup functions can be registered; they run in FIFO order.
@@ -13,41 +14,21 @@ export interface EffectContext extends SelectContext {
13
14
  *
14
15
  * @example
15
16
  * ```ts
16
- * effect(({ get, onCleanup }) => {
17
+ * effect(({ read, onCleanup }) => {
17
18
  * const id = setInterval(() => console.log('tick'), 1000);
18
19
  * onCleanup(() => clearInterval(id));
19
20
  * });
20
21
  * ```
21
22
  */
22
23
  onCleanup: (cleanup: VoidFunction) => void;
23
- /**
24
- * Register an error handler for synchronous errors thrown in the effect.
25
- * If registered, prevents errors from propagating to `options.onError`.
26
- *
27
- * @param handler - Function to handle errors
28
- *
29
- * @example
30
- * ```ts
31
- * effect(({ get, onError }) => {
32
- * onError((e) => console.error('Effect failed:', e));
33
- * riskyOperation();
34
- * });
35
- * ```
36
- */
37
- onError: (handler: (error: unknown) => void) => void;
38
24
  }
39
- /**
40
- * Callback function for effects.
41
- * Receives the effect context with `{ get, all, any, race, settled, onCleanup, onError }` utilities.
42
- */
43
- export type EffectFn = (context: EffectContext) => void;
44
25
  /**
45
26
  * Creates a side-effect that runs when accessed atom(s) change.
46
27
  *
47
28
  * Effects are similar to derived atoms but for side-effects rather than computed values.
48
29
  * They inherit derived's behavior:
49
30
  * - **Suspense-like async**: Waits for async atoms to resolve before running
50
- * - **Conditional dependencies**: Only tracks atoms actually accessed via `get()`
31
+ * - **Conditional dependencies**: Only tracks atoms actually accessed via `read()`
51
32
  * - **Automatic cleanup**: Previous cleanup runs before next execution
52
33
  * - **Batched updates**: Atom updates within the effect are batched
53
34
  *
@@ -57,23 +38,23 @@ export type EffectFn = (context: EffectContext) => void;
57
38
  *
58
39
  * ```ts
59
40
  * // ❌ WRONG - Don't use async function
60
- * effect(async ({ get }) => {
41
+ * effect(async ({ read }) => {
61
42
  * const data = await fetch('/api');
62
43
  * console.log(data);
63
44
  * });
64
45
  *
65
- * // ✅ CORRECT - Create async atom and read with get()
46
+ * // ✅ CORRECT - Create async atom and read with read()
66
47
  * const data$ = atom(fetch('/api').then(r => r.json()));
67
- * effect(({ get }) => {
68
- * console.log(get(data$)); // Suspends until resolved
48
+ * effect(({ read }) => {
49
+ * console.log(read(data$)); // Suspends until resolved
69
50
  * });
70
51
  * ```
71
52
  *
72
53
  * ## Basic Usage
73
54
  *
74
55
  * ```ts
75
- * const dispose = effect(({ get }) => {
76
- * localStorage.setItem('count', String(get(countAtom)));
56
+ * const dispose = effect(({ read }) => {
57
+ * localStorage.setItem('count', String(read(countAtom)));
77
58
  * });
78
59
  * ```
79
60
  *
@@ -82,39 +63,54 @@ export type EffectFn = (context: EffectContext) => void;
82
63
  * Use `onCleanup` to register cleanup functions that run before the next execution or on dispose:
83
64
  *
84
65
  * ```ts
85
- * const dispose = effect(({ get, onCleanup }) => {
86
- * const interval = get(intervalAtom);
66
+ * const dispose = effect(({ read, onCleanup }) => {
67
+ * const interval = read(intervalAtom);
87
68
  * const id = setInterval(() => console.log('tick'), interval);
88
69
  * onCleanup(() => clearInterval(id));
89
70
  * });
90
71
  * ```
91
72
  *
92
- * ## Error Handling
73
+ * ## IMPORTANT: Do NOT Use try/catch - Use safe() Instead
93
74
  *
94
- * Use `onError` callback to handle errors within the effect, or `options.onError` for unhandled errors:
75
+ * **Never wrap `read()` calls in try/catch blocks.** The `read()` function throws
76
+ * Promises when atoms are loading (Suspense pattern). A try/catch will catch
77
+ * these Promises and break the Suspense mechanism.
95
78
  *
96
79
  * ```ts
97
- * // Callback-based error handling
98
- * const dispose = effect(({ get, onError }) => {
99
- * onError((e) => console.error('Effect failed:', e));
100
- * const data = get(dataAtom);
101
- * riskyOperation(data);
80
+ * // ❌ WRONG - Catches Suspense Promise, breaks loading state
81
+ * effect(({ read }) => {
82
+ * try {
83
+ * const data = read(asyncAtom$);
84
+ * riskyOperation(data);
85
+ * } catch (e) {
86
+ * console.error(e); // Catches BOTH errors AND loading promises!
87
+ * }
102
88
  * });
103
89
  *
104
- * // Option-based error handling (for unhandled errors)
105
- * const dispose = effect(
106
- * ({ get }) => {
107
- * const data = get(dataAtom);
108
- * riskyOperation(data);
109
- * },
110
- * { onError: (e) => console.error('Effect failed:', e) }
111
- * );
90
+ * // ✅ CORRECT - Use safe() to catch errors but preserve Suspense
91
+ * effect(({ read, safe }) => {
92
+ * const [err, data] = safe(() => {
93
+ * const raw = read(asyncAtom$); // Can throw Promise (Suspense)
94
+ * return riskyOperation(raw); // Can throw Error
95
+ * });
96
+ *
97
+ * if (err) {
98
+ * console.error('Operation failed:', err);
99
+ * return;
100
+ * }
101
+ * // Use data safely
102
+ * });
112
103
  * ```
113
104
  *
114
- * @param fn - Effect callback receiving context with `{ get, all, any, race, settled, onCleanup, onError }`.
105
+ * The `safe()` utility:
106
+ * - **Catches errors** and returns `[error, undefined]`
107
+ * - **Re-throws Promises** to preserve Suspense behavior
108
+ * - Returns `[undefined, result]` on success
109
+ *
110
+ * @param fn - Effect callback receiving context with `{ read, all, any, race, settled, safe, onCleanup }`.
115
111
  * Must be synchronous (not async).
116
- * @param options - Optional configuration (key, onError for unhandled errors)
112
+ * @param options - Optional configuration (key)
117
113
  * @returns Dispose function to stop the effect and run final cleanup
118
114
  * @throws Error if effect function returns a Promise
119
115
  */
120
- export declare function effect(fn: EffectFn, options?: EffectOptions): VoidFunction;
116
+ export declare function effect(fn: ReactiveSelector<void, EffectContext>, _options?: EffectOptions): VoidFunction;
@@ -0,0 +1,29 @@
1
+ import { Atom, AtomState } from './types';
2
+ /**
3
+ * Returns the current state of an atom as a discriminated union.
4
+ *
5
+ * For any atom (mutable or derived):
6
+ * - If value is not a Promise: returns ready state
7
+ * - If value is a Promise: tracks and returns its state (ready/error/loading)
8
+ *
9
+ * @param atom - The atom to get state from
10
+ * @returns AtomState discriminated union (ready | error | loading)
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const state = getAtomState(myAtom$);
15
+ *
16
+ * switch (state.status) {
17
+ * case "ready":
18
+ * console.log(state.value); // T
19
+ * break;
20
+ * case "error":
21
+ * console.log(state.error);
22
+ * break;
23
+ * case "loading":
24
+ * console.log(state.promise);
25
+ * break;
26
+ * }
27
+ * ```
28
+ */
29
+ export declare function getAtomState<T>(atom: Atom<T>): AtomState<Awaited<T>>;
@@ -1,4 +1,26 @@
1
- import { Atom, AtomState, DerivedAtom } from './types';
1
+ import { DerivedAtom } from './types';
2
+ /**
3
+ * Metadata attached to combined promises for comparison.
4
+ */
5
+ export interface CombinedPromiseMeta {
6
+ type: "all" | "race" | "allSettled";
7
+ promises: Promise<unknown>[];
8
+ }
9
+ /**
10
+ * Gets the metadata for a combined promise, if any.
11
+ * Used internally by promisesEqual for comparison.
12
+ */
13
+ export declare function getCombinedPromiseMetadata(promise: PromiseLike<unknown>): CombinedPromiseMeta | undefined;
14
+ /**
15
+ * Create a combined promise with metadata for comparison.
16
+ * If only one promise, returns it directly (no metadata needed).
17
+ */
18
+ export declare function createCombinedPromise(type: "all" | "race" | "allSettled", promises: Promise<unknown>[]): PromiseLike<unknown>;
19
+ /**
20
+ * Compare two promises, considering combined promise metadata.
21
+ * Returns true if promises are considered equal.
22
+ */
23
+ export declare function promisesEqual(a: PromiseLike<unknown> | undefined, b: PromiseLike<unknown> | undefined): boolean;
2
24
  /**
3
25
  * Represents the state of a tracked Promise.
4
26
  */
@@ -51,37 +73,6 @@ export declare function isTracked(promise: PromiseLike<unknown>): boolean;
51
73
  * Type guard to check if a value is a DerivedAtom.
52
74
  */
53
75
  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
76
  /**
86
77
  * Unwraps a value that may be a Promise.
87
78
  * - If not a Promise, returns the value directly.