atomirx 0.0.2 → 0.0.5

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 (65) hide show
  1. package/README.md +868 -161
  2. package/coverage/src/core/onCreateHook.ts.html +72 -70
  3. package/dist/core/atom.d.ts +83 -6
  4. package/dist/core/batch.d.ts +3 -3
  5. package/dist/core/derived.d.ts +69 -22
  6. package/dist/core/effect.d.ts +52 -52
  7. package/dist/core/getAtomState.d.ts +29 -0
  8. package/dist/core/hook.d.ts +1 -1
  9. package/dist/core/onCreateHook.d.ts +37 -23
  10. package/dist/core/onErrorHook.d.ts +49 -0
  11. package/dist/core/promiseCache.d.ts +23 -32
  12. package/dist/core/select.d.ts +208 -29
  13. package/dist/core/types.d.ts +107 -22
  14. package/dist/core/withReady.d.ts +115 -0
  15. package/dist/core/withReady.test.d.ts +1 -0
  16. package/dist/index-CBVj1kSj.js +1350 -0
  17. package/dist/index-Cxk9v0um.cjs +1 -0
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.ts +12 -8
  20. package/dist/index.js +18 -15
  21. package/dist/react/index.cjs +10 -10
  22. package/dist/react/index.d.ts +2 -1
  23. package/dist/react/index.js +422 -377
  24. package/dist/react/rx.d.ts +114 -25
  25. package/dist/react/useAction.d.ts +5 -4
  26. package/dist/react/{useValue.d.ts → useSelector.d.ts} +56 -25
  27. package/dist/react/useSelector.test.d.ts +1 -0
  28. package/package.json +1 -1
  29. package/src/core/atom.test.ts +307 -43
  30. package/src/core/atom.ts +144 -22
  31. package/src/core/batch.test.ts +10 -10
  32. package/src/core/batch.ts +3 -3
  33. package/src/core/define.test.ts +12 -11
  34. package/src/core/define.ts +1 -1
  35. package/src/core/derived.test.ts +906 -72
  36. package/src/core/derived.ts +192 -81
  37. package/src/core/effect.test.ts +651 -45
  38. package/src/core/effect.ts +102 -98
  39. package/src/core/getAtomState.ts +69 -0
  40. package/src/core/hook.test.ts +5 -5
  41. package/src/core/hook.ts +1 -1
  42. package/src/core/onCreateHook.ts +38 -23
  43. package/src/core/onErrorHook.test.ts +350 -0
  44. package/src/core/onErrorHook.ts +52 -0
  45. package/src/core/promiseCache.test.ts +5 -3
  46. package/src/core/promiseCache.ts +76 -71
  47. package/src/core/select.ts +405 -130
  48. package/src/core/selector.test.ts +574 -32
  49. package/src/core/types.ts +107 -29
  50. package/src/core/withReady.test.ts +534 -0
  51. package/src/core/withReady.ts +191 -0
  52. package/src/core/withUse.ts +1 -1
  53. package/src/index.test.ts +4 -4
  54. package/src/index.ts +21 -7
  55. package/src/react/index.ts +2 -1
  56. package/src/react/rx.test.tsx +173 -18
  57. package/src/react/rx.tsx +274 -43
  58. package/src/react/useAction.test.ts +12 -14
  59. package/src/react/useAction.ts +11 -9
  60. package/src/react/{useValue.test.ts → useSelector.test.ts} +16 -16
  61. package/src/react/{useValue.ts → useSelector.ts} +64 -33
  62. package/v2.md +44 -44
  63. package/dist/index-2ok7ilik.js +0 -1217
  64. package/dist/index-B_5SFzfl.cjs +0 -1
  65. /package/dist/{react/useValue.test.d.ts → core/onErrorHook.test.d.ts} +0 -0
package/src/core/types.ts CHANGED
@@ -28,8 +28,8 @@ export const SYMBOL_DERIVED = Symbol.for("atomirx.derived");
28
28
  * @example
29
29
  * ```ts
30
30
  * const enhanced = atom(0)
31
- * .use(source => ({ ...source, double: () => source.value * 2 }))
32
- * .use(source => ({ ...source, triple: () => source.value * 3 }));
31
+ * .use(source => ({ ...source, double: () => source.get() * 2 }))
32
+ * .use(source => ({ ...source, triple: () => source.get() * 3 }));
33
33
  * ```
34
34
  */
35
35
  export interface Pipeable {
@@ -47,9 +47,8 @@ export interface Pipeable {
47
47
  /**
48
48
  * Optional metadata for atoms.
49
49
  */
50
- export interface AtomMeta {
50
+ export interface AtomMeta extends AtomirxMeta {
51
51
  key?: string;
52
- [key: string]: unknown;
53
52
  }
54
53
 
55
54
  /**
@@ -61,10 +60,13 @@ export interface AtomMeta {
61
60
  export interface Atom<T> {
62
61
  /** Symbol marker to identify atom instances */
63
62
  readonly [SYMBOL_ATOM]: true;
64
- /** The current value */
65
- readonly value: T;
63
+
66
64
  /** Optional metadata for the atom */
67
65
  readonly meta?: AtomMeta;
66
+
67
+ /** Get the current value */
68
+ get(): T;
69
+
68
70
  /**
69
71
  * Subscribe to value changes.
70
72
  * @param listener - Callback invoked when value changes
@@ -91,7 +93,7 @@ export interface Atom<T> {
91
93
  *
92
94
  * // Async value (stores Promise as-is)
93
95
  * const posts = atom(fetchPosts());
94
- * posts.value; // Promise<Post[]>
96
+ * posts.get(); // Promise<Post[]>
95
97
  * posts.set(fetchPosts()); // Store new Promise
96
98
  * ```
97
99
  */
@@ -132,7 +134,7 @@ export interface MutableAtom<T> extends Atom<T>, Pipeable {
132
134
  * A derived (computed) atom that always returns Promise<T> for its value.
133
135
  *
134
136
  * DerivedAtom computes its value from other atoms. The computation is
135
- * re-run whenever dependencies change. The `.value` always returns a Promise,
137
+ * re-run whenever dependencies change. The `.get()` always returns a Promise,
136
138
  * even for synchronous computations.
137
139
  *
138
140
  * @template T - The resolved type of the computed value
@@ -141,13 +143,13 @@ export interface MutableAtom<T> extends Atom<T>, Pipeable {
141
143
  * @example
142
144
  * ```ts
143
145
  * // Without fallback
144
- * const double$ = derived(({ get }) => get(count$) * 2);
145
- * await double$.value; // number
146
+ * const double$ = derived(({ read }) => read(count$) * 2);
147
+ * await double$.get(); // number
146
148
  * double$.staleValue; // number | undefined
147
149
  * double$.state(); // { status: "ready", value: 10 }
148
150
  *
149
151
  * // With fallback - during loading
150
- * const double$ = derived(({ get }) => get(count$) * 2, { fallback: 0 });
152
+ * const double$ = derived(({ read }) => read(count$) * 2, { fallback: 0 });
151
153
  * double$.staleValue; // number (guaranteed)
152
154
  * double$.state(); // { status: "loading", promise } during loading
153
155
  * ```
@@ -199,9 +201,48 @@ export type AtomValue<A> =
199
201
  * @template T - The type of the atom's value
200
202
  */
201
203
  export type AtomState<T> =
202
- | { status: "ready"; value: T }
203
- | { status: "error"; error: unknown }
204
- | { status: "loading"; promise: Promise<T> };
204
+ | { status: "ready"; value: T; error?: undefined; promise?: undefined }
205
+ | { status: "error"; error: unknown; value?: undefined; promise?: undefined }
206
+ | {
207
+ status: "loading";
208
+ promise: Promise<T>;
209
+ value?: undefined;
210
+ error?: undefined;
211
+ };
212
+
213
+ /**
214
+ * Result type for SelectContext.state() - simplified AtomState without promise.
215
+ *
216
+ * All properties (`status`, `value`, `error`) are always present:
217
+ * - `value` is `T` when ready, `undefined` otherwise
218
+ * - `error` is the error when errored, `undefined` otherwise
219
+ *
220
+ * This enables easy destructuring without type narrowing:
221
+ * ```ts
222
+ * const { status, value, error } = state(atom$);
223
+ * ```
224
+ *
225
+ * Equality comparisons work correctly (no promise reference issues).
226
+ */
227
+ export type SelectStateResult<T> =
228
+ | { status: "ready"; value: T; error: undefined }
229
+ | { status: "error"; value: undefined; error: unknown }
230
+ | { status: "loading"; value: undefined; error: undefined };
231
+
232
+ /**
233
+ * Result type for race() and any() - includes winning key.
234
+ *
235
+ * @template K - The key type (string literal union)
236
+ * @template V - The value type
237
+ */
238
+ export type KeyedResult<K extends string, V> = {
239
+ /** The key that won the race/any */
240
+ key: K;
241
+ /** The resolved value */
242
+ value: V;
243
+ };
244
+
245
+ export type AtomPlugin = <T extends Atom<any>>(atom: T) => T | void;
205
246
 
206
247
  /**
207
248
  * Result type for settled operations.
@@ -236,16 +277,65 @@ export interface DerivedOptions<T> {
236
277
  meta?: DerivedAtomMeta;
237
278
  /** Equality strategy for change detection (default: "strict") */
238
279
  equals?: Equality<T>;
280
+ /**
281
+ * Callback invoked when the derived computation throws an error.
282
+ * This is called for actual errors, NOT for Promise throws (Suspense).
283
+ *
284
+ * @param error - The error thrown during computation
285
+ *
286
+ * @example
287
+ * ```ts
288
+ * const data$ = derived(
289
+ * ({ read }) => {
290
+ * const raw = read(source$);
291
+ * return JSON.parse(raw); // May throw SyntaxError
292
+ * },
293
+ * {
294
+ * onError: (error) => {
295
+ * console.error('Derived computation failed:', error);
296
+ * reportToSentry(error);
297
+ * }
298
+ * }
299
+ * );
300
+ * ```
301
+ */
302
+ onError?: (error: unknown) => void;
239
303
  }
240
304
 
241
305
  /**
242
306
  * Configuration options for effects.
243
307
  */
244
308
  export interface EffectOptions {
245
- /** Optional key for debugging */
309
+ meta?: EffectMeta;
310
+ /**
311
+ * Callback invoked when the effect computation throws an error.
312
+ * This is called for actual errors, NOT for Promise throws (Suspense).
313
+ *
314
+ * @param error - The error thrown during effect execution
315
+ *
316
+ * @example
317
+ * ```ts
318
+ * effect(
319
+ * ({ read }) => {
320
+ * const data = read(source$);
321
+ * riskyOperation(data); // May throw
322
+ * },
323
+ * {
324
+ * onError: (error) => {
325
+ * console.error('Effect failed:', error);
326
+ * showErrorNotification(error);
327
+ * }
328
+ * }
329
+ * );
330
+ * ```
331
+ */
332
+ onError?: (error: unknown) => void;
333
+ }
334
+
335
+ export interface AtomirxMeta {}
336
+
337
+ export interface EffectMeta extends AtomirxMeta {
246
338
  key?: string;
247
- /** Error handler for uncaught errors in the effect */
248
- onError?: (error: Error) => void;
249
339
  }
250
340
 
251
341
  /**
@@ -297,15 +387,3 @@ export interface ModuleMeta {}
297
387
  export type Listener<T> = (value: T) => void;
298
388
 
299
389
  export type SingleOrMultipleListeners<T> = Listener<T> | Listener<T>[];
300
-
301
- /**
302
- * Type guard to check if a value is an Atom.
303
- */
304
- export declare function isAtom<T>(value: unknown): value is Atom<T>;
305
-
306
- /**
307
- * Type guard to check if a value is a DerivedAtom.
308
- */
309
- export declare function isDerived<T>(
310
- value: unknown
311
- ): value is DerivedAtom<T, boolean>;