atomirx 0.0.8 → 0.1.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.
Files changed (138) hide show
  1. package/README.md +198 -2234
  2. package/bin/cli.js +90 -0
  3. package/dist/core/derived.d.ts +2 -2
  4. package/dist/core/effect.d.ts +3 -2
  5. package/dist/core/onCreateHook.d.ts +15 -2
  6. package/dist/core/onErrorHook.d.ts +4 -1
  7. package/dist/core/pool.d.ts +78 -0
  8. package/dist/core/pool.test.d.ts +1 -0
  9. package/dist/core/select-boolean.test.d.ts +1 -0
  10. package/dist/core/select-pool.test.d.ts +1 -0
  11. package/dist/core/select.d.ts +278 -86
  12. package/dist/core/types.d.ts +233 -1
  13. package/dist/core/withAbort.d.ts +95 -0
  14. package/dist/core/withReady.d.ts +3 -3
  15. package/dist/devtools/constants.d.ts +41 -0
  16. package/dist/devtools/index.cjs +1 -0
  17. package/dist/devtools/index.d.ts +29 -0
  18. package/dist/devtools/index.js +429 -0
  19. package/dist/devtools/registry.d.ts +98 -0
  20. package/dist/devtools/registry.test.d.ts +1 -0
  21. package/dist/devtools/setup.d.ts +61 -0
  22. package/dist/devtools/types.d.ts +311 -0
  23. package/dist/index-BZEnfIcB.cjs +1 -0
  24. package/dist/index-BbPZhsDl.js +1653 -0
  25. package/dist/index.cjs +1 -1
  26. package/dist/index.d.ts +4 -3
  27. package/dist/index.js +18 -14
  28. package/dist/onDispatchHook-C8yLzr-o.cjs +1 -0
  29. package/dist/onDispatchHook-SKbiIUaJ.js +5 -0
  30. package/dist/onErrorHook-BGGy3tqK.js +38 -0
  31. package/dist/onErrorHook-DHBASmYw.cjs +1 -0
  32. package/dist/react/index.cjs +1 -1
  33. package/dist/react/index.js +191 -151
  34. package/dist/react/onDispatchHook.d.ts +106 -0
  35. package/dist/react/useAction.d.ts +4 -1
  36. package/dist/react-devtools/DevToolsPanel.d.ts +93 -0
  37. package/dist/react-devtools/EntityDetails.d.ts +10 -0
  38. package/dist/react-devtools/EntityList.d.ts +15 -0
  39. package/dist/react-devtools/LogList.d.ts +12 -0
  40. package/dist/react-devtools/hooks.d.ts +50 -0
  41. package/dist/react-devtools/index.cjs +1 -0
  42. package/dist/react-devtools/index.d.ts +31 -0
  43. package/dist/react-devtools/index.js +1589 -0
  44. package/dist/react-devtools/styles.d.ts +148 -0
  45. package/package.json +26 -2
  46. package/skills/atomirx/SKILL.md +456 -0
  47. package/skills/atomirx/references/async-patterns.md +188 -0
  48. package/skills/atomirx/references/atom-patterns.md +238 -0
  49. package/skills/atomirx/references/deferred-loading.md +191 -0
  50. package/skills/atomirx/references/derived-patterns.md +428 -0
  51. package/skills/atomirx/references/effect-patterns.md +426 -0
  52. package/skills/atomirx/references/error-handling.md +140 -0
  53. package/skills/atomirx/references/hooks.md +322 -0
  54. package/skills/atomirx/references/pool-patterns.md +229 -0
  55. package/skills/atomirx/references/react-integration.md +411 -0
  56. package/skills/atomirx/references/rules.md +407 -0
  57. package/skills/atomirx/references/select-context.md +309 -0
  58. package/skills/atomirx/references/service-template.md +172 -0
  59. package/skills/atomirx/references/store-template.md +205 -0
  60. package/skills/atomirx/references/testing-patterns.md +431 -0
  61. package/coverage/base.css +0 -224
  62. package/coverage/block-navigation.js +0 -87
  63. package/coverage/clover.xml +0 -1440
  64. package/coverage/coverage-final.json +0 -14
  65. package/coverage/favicon.png +0 -0
  66. package/coverage/index.html +0 -131
  67. package/coverage/prettify.css +0 -1
  68. package/coverage/prettify.js +0 -2
  69. package/coverage/sort-arrow-sprite.png +0 -0
  70. package/coverage/sorter.js +0 -210
  71. package/coverage/src/core/atom.ts.html +0 -889
  72. package/coverage/src/core/batch.ts.html +0 -223
  73. package/coverage/src/core/define.ts.html +0 -805
  74. package/coverage/src/core/emitter.ts.html +0 -919
  75. package/coverage/src/core/equality.ts.html +0 -631
  76. package/coverage/src/core/hook.ts.html +0 -460
  77. package/coverage/src/core/index.html +0 -281
  78. package/coverage/src/core/isAtom.ts.html +0 -100
  79. package/coverage/src/core/isPromiseLike.ts.html +0 -133
  80. package/coverage/src/core/onCreateHook.ts.html +0 -138
  81. package/coverage/src/core/scheduleNotifyHook.ts.html +0 -94
  82. package/coverage/src/core/types.ts.html +0 -523
  83. package/coverage/src/core/withUse.ts.html +0 -253
  84. package/coverage/src/index.html +0 -116
  85. package/coverage/src/index.ts.html +0 -106
  86. package/dist/index-CBVj1kSj.js +0 -1350
  87. package/dist/index-Cxk9v0um.cjs +0 -1
  88. package/scripts/publish.js +0 -198
  89. package/src/core/atom.test.ts +0 -633
  90. package/src/core/atom.ts +0 -311
  91. package/src/core/atomState.test.ts +0 -342
  92. package/src/core/atomState.ts +0 -256
  93. package/src/core/batch.test.ts +0 -257
  94. package/src/core/batch.ts +0 -172
  95. package/src/core/define.test.ts +0 -343
  96. package/src/core/define.ts +0 -243
  97. package/src/core/derived.test.ts +0 -1215
  98. package/src/core/derived.ts +0 -450
  99. package/src/core/effect.test.ts +0 -802
  100. package/src/core/effect.ts +0 -188
  101. package/src/core/emitter.test.ts +0 -364
  102. package/src/core/emitter.ts +0 -392
  103. package/src/core/equality.test.ts +0 -392
  104. package/src/core/equality.ts +0 -182
  105. package/src/core/getAtomState.ts +0 -69
  106. package/src/core/hook.test.ts +0 -227
  107. package/src/core/hook.ts +0 -177
  108. package/src/core/isAtom.ts +0 -27
  109. package/src/core/isPromiseLike.test.ts +0 -72
  110. package/src/core/isPromiseLike.ts +0 -16
  111. package/src/core/onCreateHook.ts +0 -107
  112. package/src/core/onErrorHook.test.ts +0 -350
  113. package/src/core/onErrorHook.ts +0 -52
  114. package/src/core/promiseCache.test.ts +0 -241
  115. package/src/core/promiseCache.ts +0 -284
  116. package/src/core/scheduleNotifyHook.ts +0 -53
  117. package/src/core/select.ts +0 -729
  118. package/src/core/selector.test.ts +0 -799
  119. package/src/core/types.ts +0 -389
  120. package/src/core/withReady.test.ts +0 -534
  121. package/src/core/withReady.ts +0 -191
  122. package/src/core/withUse.test.ts +0 -249
  123. package/src/core/withUse.ts +0 -56
  124. package/src/index.test.ts +0 -80
  125. package/src/index.ts +0 -65
  126. package/src/react/index.ts +0 -21
  127. package/src/react/rx.test.tsx +0 -571
  128. package/src/react/rx.tsx +0 -531
  129. package/src/react/strictModeTest.tsx +0 -71
  130. package/src/react/useAction.test.ts +0 -987
  131. package/src/react/useAction.ts +0 -607
  132. package/src/react/useSelector.test.ts +0 -182
  133. package/src/react/useSelector.ts +0 -292
  134. package/src/react/useStable.test.ts +0 -553
  135. package/src/react/useStable.ts +0 -288
  136. package/tsconfig.json +0 -9
  137. package/v2.md +0 -725
  138. package/vite.config.ts +0 -42
@@ -1,227 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { hook } from "./hook";
3
-
4
- describe("hook", () => {
5
- describe("createHook", () => {
6
- it("should create a hook with initial value", () => {
7
- const myHook = hook(42);
8
- expect(myHook.current).toBe(42);
9
- });
10
-
11
- it("should create a hook with undefined when no initial value", () => {
12
- const myHook = hook<string>();
13
- expect(myHook.current).toBeUndefined();
14
- });
15
-
16
- it("should create a hook with object value", () => {
17
- const obj = { name: "test" };
18
- const myHook = hook(obj);
19
- expect(myHook.current).toBe(obj);
20
- });
21
-
22
- it("should create a hook with function value", () => {
23
- const fn = () => 42;
24
- const myHook = hook(fn);
25
- expect(myHook.current).toBe(fn);
26
- });
27
- });
28
-
29
- describe("hook.override", () => {
30
- it("should override the current value using reducer", () => {
31
- const myHook = hook(0);
32
- expect(myHook.current).toBe(0);
33
-
34
- myHook.override(() => 100);
35
- expect(myHook.current).toBe(100);
36
- });
37
-
38
- it("should allow multiple overrides", () => {
39
- const myHook = hook("initial");
40
- myHook.override(() => "first");
41
- expect(myHook.current).toBe("first");
42
-
43
- myHook.override(() => "second");
44
- expect(myHook.current).toBe("second");
45
- });
46
-
47
- it("should receive previous value in reducer", () => {
48
- const myHook = hook(10);
49
- myHook.override((prev) => prev + 5);
50
- expect(myHook.current).toBe(15);
51
-
52
- myHook.override((prev) => prev * 2);
53
- expect(myHook.current).toBe(30);
54
- });
55
-
56
- it("should support composing handlers (middleware pattern)", () => {
57
- const calls: string[] = [];
58
- const myHook = hook<((msg: string) => void) | undefined>(undefined);
59
-
60
- // First handler
61
- myHook.override(() => (msg) => calls.push(`first: ${msg}`));
62
-
63
- // Compose with second handler
64
- myHook.override((prev) => (msg) => {
65
- prev?.(msg);
66
- calls.push(`second: ${msg}`);
67
- });
68
-
69
- myHook.current?.("hello");
70
- expect(calls).toEqual(["first: hello", "second: hello"]);
71
- });
72
- });
73
-
74
- describe("hook setup/release pattern", () => {
75
- it("should create a setup function that returns a release function", () => {
76
- const myHook = hook(0);
77
- const setup = myHook(() => 10);
78
-
79
- expect(typeof setup).toBe("function");
80
-
81
- const release = setup();
82
- expect(typeof release).toBe("function");
83
- expect(myHook.current).toBe(10);
84
-
85
- release();
86
- expect(myHook.current).toBe(0);
87
- });
88
-
89
- it("should support nested setup/release", () => {
90
- const myHook = hook(0);
91
-
92
- const setup1 = myHook(() => 1);
93
- const release1 = setup1();
94
- expect(myHook.current).toBe(1);
95
-
96
- const setup2 = myHook(() => 2);
97
- const release2 = setup2();
98
- expect(myHook.current).toBe(2);
99
-
100
- release2();
101
- expect(myHook.current).toBe(1);
102
-
103
- release1();
104
- expect(myHook.current).toBe(0);
105
- });
106
-
107
- it("should receive previous value in setup reducer", () => {
108
- const myHook = hook(10);
109
-
110
- const setup = myHook((prev) => prev + 5);
111
- const release = setup();
112
- expect(myHook.current).toBe(15);
113
-
114
- release();
115
- expect(myHook.current).toBe(10);
116
- });
117
- });
118
-
119
- describe("hook.use", () => {
120
- it("should temporarily set hook value during function execution", () => {
121
- const myHook = hook(0);
122
-
123
- const result = hook.use([myHook(() => 42)], () => {
124
- expect(myHook.current).toBe(42);
125
- return "done";
126
- });
127
-
128
- expect(result).toBe("done");
129
- expect(myHook.current).toBe(0);
130
- });
131
-
132
- it("should support multiple hooks", () => {
133
- const hookA = hook("a");
134
- const hookB = hook("b");
135
-
136
- hook.use([hookA(() => "A"), hookB(() => "B")], () => {
137
- expect(hookA.current).toBe("A");
138
- expect(hookB.current).toBe("B");
139
- });
140
-
141
- expect(hookA.current).toBe("a");
142
- expect(hookB.current).toBe("b");
143
- });
144
-
145
- it("should restore values even if function throws", () => {
146
- const myHook = hook(0);
147
-
148
- expect(() => {
149
- hook.use([myHook(() => 42)], () => {
150
- expect(myHook.current).toBe(42);
151
- throw new Error("test error");
152
- });
153
- }).toThrow("test error");
154
-
155
- expect(myHook.current).toBe(0);
156
- });
157
-
158
- it("should support nested hook.use calls", () => {
159
- const myHook = hook(0);
160
-
161
- hook.use([myHook(() => 1)], () => {
162
- expect(myHook.current).toBe(1);
163
-
164
- hook.use([myHook(() => 2)], () => {
165
- expect(myHook.current).toBe(2);
166
- });
167
-
168
- expect(myHook.current).toBe(1);
169
- });
170
-
171
- expect(myHook.current).toBe(0);
172
- });
173
-
174
- it("should release hooks in reverse order", () => {
175
- const order: string[] = [];
176
- const hookA = hook<string | undefined>();
177
- const hookB = hook<string | undefined>();
178
-
179
- // Create custom setups that track release order using override/reset
180
- const setupA = () => {
181
- hookA.override(() => "A");
182
- return () => {
183
- order.push("release A");
184
- hookA.reset();
185
- };
186
- };
187
-
188
- const setupB = () => {
189
- hookB.override(() => "B");
190
- return () => {
191
- order.push("release B");
192
- hookB.reset();
193
- };
194
- };
195
-
196
- hook.use([setupA, setupB], () => {
197
- expect(hookA.current).toBe("A");
198
- expect(hookB.current).toBe("B");
199
- });
200
-
201
- expect(order).toEqual(["release B", "release A"]);
202
- });
203
-
204
- it("should work with empty setups array", () => {
205
- const result = hook.use([], () => "result");
206
- expect(result).toBe("result");
207
- });
208
-
209
- it("should return the function result", () => {
210
- const myHook = hook(0);
211
- const result = hook.use([myHook(() => 1)], () => {
212
- return { value: myHook.current };
213
- });
214
- expect(result).toEqual({ value: 1 });
215
- });
216
-
217
- it("should support reducer composition in hook.use", () => {
218
- const myHook = hook(10);
219
-
220
- hook.use([myHook((prev) => prev + 5)], () => {
221
- expect(myHook.current).toBe(15);
222
- });
223
-
224
- expect(myHook.current).toBe(10);
225
- });
226
- });
227
- });
package/src/core/hook.ts DELETED
@@ -1,177 +0,0 @@
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
- /**
8
- * A hook is a callable factory that creates setup functions,
9
- * with direct access to current value via `.current`.
10
- *
11
- * @example
12
- * ```ts
13
- * const myHook = hook<string>("default");
14
- *
15
- * // Read current value (fast - direct property access)
16
- * console.log(myHook.current); // "default"
17
- *
18
- * // Create a setup function (reducer receives previous value)
19
- * const setup = myHook(() => "new value");
20
- *
21
- * // Use with hook.use()
22
- * hook.use([myHook(() => "temp")], () => {
23
- * console.log(myHook.current); // "temp"
24
- * });
25
- * console.log(myHook.current); // "default" (restored)
26
- *
27
- * // Compose with previous value
28
- * myHook.override(prev => {
29
- * console.log("Previous:", prev);
30
- * return "next";
31
- * });
32
- * ```
33
- */
34
- export interface Hook<T> {
35
- /**
36
- * Creates a HookSetup that will set this hook using a reducer.
37
- * The reducer receives the previous value and returns the next value.
38
- *
39
- * @param reducer - Function that receives previous value and returns next value
40
- *
41
- * @example Set new value (ignore previous)
42
- * ```ts
43
- * myHook(() => "new value")
44
- * ```
45
- *
46
- * @example Compose with previous
47
- * ```ts
48
- * myHook(prev => {
49
- * prev?.(); // call previous handler
50
- * return newHandler;
51
- * })
52
- * ```
53
- */
54
- (reducer: (prev: T) => T): HookSetup;
55
-
56
- /**
57
- * Current value of the hook. Direct property access for fast reads.
58
- */
59
- readonly current: T;
60
-
61
- /**
62
- * Override the current value using a reducer.
63
- * The reducer receives the previous value and returns the next value.
64
- * Unlike the setup/release pattern, this is an immediate mutation.
65
- *
66
- * @param reducer - Function that receives previous value and returns next value
67
- *
68
- * @example Set new value (ignore previous)
69
- * ```ts
70
- * myHook.override(() => "new value")
71
- * ```
72
- *
73
- * @example Compose with previous (middleware pattern)
74
- * ```ts
75
- * onCreateHook.override(prev => (info) => {
76
- * prev?.(info); // call existing handler
77
- * console.log("Created:", info.key);
78
- * });
79
- * ```
80
- */
81
- override(reducer: (prev: T) => T): void;
82
-
83
- /**
84
- * Reset the hook to its initial value.
85
- */
86
- reset(): void;
87
- }
88
-
89
- /**
90
- * Creates a new hook with an initial value.
91
- *
92
- * Hooks use the setup/release pattern for performance:
93
- * - Reads are direct property access (fastest)
94
- * - Writes use setup/release for proper nesting
95
- *
96
- * @param initial - Initial value for the hook
97
- * @returns A Hook instance
98
- *
99
- * @example
100
- * ```ts
101
- * // Create a hook
102
- * const countHook = hook(0);
103
- *
104
- * // Read
105
- * console.log(countHook.current); // 0
106
- *
107
- * // Use with hook.use() - reducer receives previous value
108
- * hook.use([countHook(() => 5)], () => {
109
- * console.log(countHook.current); // 5
110
- * });
111
- *
112
- * // Compose with previous (middleware pattern)
113
- * myHook.override(prev => (info) => {
114
- * prev?.(info); // call existing
115
- * newHandler(info);
116
- * });
117
- * ```
118
- */
119
- function createHook<T>(initial: T): Hook<T>;
120
- function createHook<T>(): Hook<T | undefined>;
121
- function createHook<T>(initial?: T): Hook<T | undefined> {
122
- // The hook function creates a setup that returns a release
123
- const h = Object.assign(
124
- (reducer: (prev: T | undefined) => T | undefined): HookSetup => {
125
- return () => {
126
- const prev = h.current;
127
- h.current = reducer(prev);
128
- return () => {
129
- h.current = prev;
130
- };
131
- };
132
- },
133
- {
134
- current: initial,
135
- // Override method for direct mutation using reducer
136
- override: (reducer: (prev: T | undefined) => T | undefined) => {
137
- h.current = reducer(h.current);
138
- },
139
- reset: () => {
140
- h.current = initial;
141
- },
142
- }
143
- );
144
-
145
- return h as Hook<T | undefined>;
146
- }
147
-
148
- /**
149
- * Executes a function with multiple hooks temporarily set.
150
- *
151
- * @param setups - Array of HookSetup functions (from hook factories)
152
- * @param fn - Function to execute with hooks active
153
- * @returns The return value of fn
154
- *
155
- * @example
156
- * ```ts
157
- * hook.use([trackHook(myTracker), debugHook(true)], () => {
158
- * // hooks active here
159
- * });
160
- * ```
161
- */
162
- function use<T>(setups: HookSetup[], fn: () => T): T {
163
- const releases: VoidFunction[] = [];
164
- for (const setup of setups) {
165
- releases.push(setup());
166
- }
167
- try {
168
- return fn();
169
- } finally {
170
- for (const release of releases.reverse()) {
171
- release();
172
- }
173
- }
174
- }
175
-
176
- // Combine into namespace
177
- export const hook = Object.assign(createHook, { use });
@@ -1,27 +0,0 @@
1
- import { Atom, DerivedAtom, SYMBOL_ATOM, SYMBOL_DERIVED } from "./types";
2
-
3
- /**
4
- * Type guard to check if a value is an Atom.
5
- */
6
- export function isAtom<T>(value: unknown): value is Atom<T> {
7
- return (
8
- value !== null &&
9
- typeof value === "object" &&
10
- SYMBOL_ATOM in value &&
11
- (value as Atom<T>)[SYMBOL_ATOM] === true
12
- );
13
- }
14
-
15
- /**
16
- * Type guard to check if a value is a DerivedAtom.
17
- */
18
- export function isDerived<T>(
19
- value: unknown
20
- ): value is DerivedAtom<T, boolean> {
21
- return (
22
- value !== null &&
23
- typeof value === "object" &&
24
- SYMBOL_DERIVED in value &&
25
- (value as DerivedAtom<T, boolean>)[SYMBOL_DERIVED] === true
26
- );
27
- }
@@ -1,72 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { isPromiseLike } from "./isPromiseLike";
3
-
4
- describe("isPromiseLike", () => {
5
- describe("returns true for PromiseLike values", () => {
6
- it("should return true for native Promise", () => {
7
- expect(isPromiseLike(Promise.resolve(1))).toBe(true);
8
- });
9
-
10
- it("should return true for rejected Promise", () => {
11
- const rejected = Promise.reject(new Error("test"));
12
- rejected.catch(() => {}); // Prevent unhandled rejection
13
- expect(isPromiseLike(rejected)).toBe(true);
14
- });
15
-
16
- it("should return true for object with then method", () => {
17
- const thenable = { then: () => {} };
18
- expect(isPromiseLike(thenable)).toBe(true);
19
- });
20
-
21
- it("should return true for object with then method that takes callbacks", () => {
22
- const thenable = {
23
- then: (resolve: (v: number) => void, _reject: (e: Error) => void) => {
24
- resolve(42);
25
- },
26
- };
27
- expect(isPromiseLike(thenable)).toBe(true);
28
- });
29
- });
30
-
31
- describe("returns false for non-PromiseLike values", () => {
32
- it("should return false for null", () => {
33
- expect(isPromiseLike(null)).toBe(false);
34
- });
35
-
36
- it("should return false for undefined", () => {
37
- expect(isPromiseLike(undefined)).toBe(false);
38
- });
39
-
40
- it("should return false for number", () => {
41
- expect(isPromiseLike(42)).toBe(false);
42
- });
43
-
44
- it("should return false for string", () => {
45
- expect(isPromiseLike("hello")).toBe(false);
46
- });
47
-
48
- it("should return false for boolean", () => {
49
- expect(isPromiseLike(true)).toBe(false);
50
- });
51
-
52
- it("should return false for plain object without then", () => {
53
- expect(isPromiseLike({ value: 1 })).toBe(false);
54
- });
55
-
56
- it("should return false for array", () => {
57
- expect(isPromiseLike([1, 2, 3])).toBe(false);
58
- });
59
-
60
- it("should return false for function", () => {
61
- expect(isPromiseLike(() => {})).toBe(false);
62
- });
63
-
64
- it("should return false for object with non-function then property", () => {
65
- expect(isPromiseLike({ then: "not a function" })).toBe(false);
66
- });
67
-
68
- it("should return false for object with then as number", () => {
69
- expect(isPromiseLike({ then: 123 })).toBe(false);
70
- });
71
- });
72
- });
@@ -1,16 +0,0 @@
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 function isPromiseLike<T>(value: unknown): value is PromiseLike<T> {
10
- return (
11
- value !== null &&
12
- typeof value === "object" &&
13
- "then" in value &&
14
- typeof (value as any).then === "function"
15
- );
16
- }
@@ -1,107 +0,0 @@
1
- import { Effect } from "./effect";
2
- import { hook } from "./hook";
3
- import {
4
- MutableAtomMeta,
5
- DerivedAtomMeta,
6
- MutableAtom,
7
- DerivedAtom,
8
- ModuleMeta,
9
- EffectMeta,
10
- } from "./types";
11
-
12
- /**
13
- * Information provided when a mutable atom is created.
14
- */
15
- export interface MutableInfo {
16
- /** Discriminator for mutable atoms */
17
- type: "mutable";
18
- /** Optional key from atom options (for debugging/devtools) */
19
- key: string | undefined;
20
- /** Optional metadata from atom options */
21
- meta: MutableAtomMeta | undefined;
22
- /** The created mutable atom instance */
23
- instance: MutableAtom<unknown>;
24
- }
25
-
26
- /**
27
- * Information provided when a derived atom is created.
28
- */
29
- export interface DerivedInfo {
30
- /** Discriminator for derived atoms */
31
- type: "derived";
32
- /** Optional key from derived options (for debugging/devtools) */
33
- key: string | undefined;
34
- /** Optional metadata from derived options */
35
- meta: DerivedAtomMeta | undefined;
36
- /** The created derived atom instance */
37
- instance: DerivedAtom<unknown, boolean>;
38
- }
39
-
40
- /**
41
- * Information provided when an effect is created.
42
- */
43
- export interface EffectInfo {
44
- /** Discriminator for effects */
45
- type: "effect";
46
- /** Optional key from effect options (for debugging/devtools) */
47
- key: string | undefined;
48
- /** Optional metadata from effect options */
49
- meta: EffectMeta | undefined;
50
- /** The created effect instance */
51
- instance: Effect;
52
- }
53
-
54
- /**
55
- * Union type for atom/derived/effect creation info.
56
- */
57
- export type CreateInfo = MutableInfo | DerivedInfo | EffectInfo;
58
-
59
- /**
60
- * Information provided when a module (via define()) is created.
61
- */
62
- export interface ModuleInfo {
63
- /** Discriminator for modules */
64
- type: "module";
65
- /** Optional key from define options (for debugging/devtools) */
66
- key: string | undefined;
67
- /** Optional metadata from define options */
68
- meta: ModuleMeta | undefined;
69
- /** The created module instance */
70
- instance: unknown;
71
- }
72
-
73
- /**
74
- * Global hook that fires whenever an atom or module is created.
75
- *
76
- * This is useful for:
77
- * - **DevTools integration** - track all atoms/modules in the app
78
- * - **Debugging** - log atom creation for troubleshooting
79
- * - **Testing** - verify expected atoms are created
80
- *
81
- * **IMPORTANT**: Always use `.override()` to preserve the hook chain.
82
- * Direct assignment to `.current` will break existing handlers.
83
- *
84
- * @example Basic logging
85
- * ```ts
86
- * onCreateHook.override((prev) => (info) => {
87
- * prev?.(info); // call existing handlers first
88
- * console.log(`Created ${info.type}: ${info.key ?? "anonymous"}`);
89
- * });
90
- * ```
91
- *
92
- * @example DevTools integration
93
- * ```ts
94
- * const registry = new Map();
95
- *
96
- * onCreateHook.override((prev) => (info) => {
97
- * prev?.(info); // preserve chain
98
- * registry.set(info.key, info.instance);
99
- * });
100
- * ```
101
- *
102
- * @example Reset to default (disable all handlers)
103
- * ```ts
104
- * onCreateHook.reset();
105
- * ```
106
- */
107
- export const onCreateHook = hook<(info: CreateInfo | ModuleInfo) => void>();