@pumped-fn/lite-react 1.0.0 → 1.1.0
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/CHANGELOG.md +13 -0
- package/README.md +41 -0
- package/dist/index.cjs +52 -34
- package/dist/index.d.cts +26 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +26 -12
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +53 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @pumped-fn/lite-react
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1624845: feat(lite-react): add non-Suspense mode and resolve options for useAtom/useController
|
|
8
|
+
|
|
9
|
+
- Add `{ suspense: false }` option to `useAtom` returning `UseAtomState<T>` with `data`, `loading`, `error`, `controller`
|
|
10
|
+
- Add `{ resolve: boolean }` option to control auto-resolution behavior
|
|
11
|
+
- Suspense mode: `resolve` defaults to `true` (auto-resolves idle atoms)
|
|
12
|
+
- Non-Suspense mode: `resolve` defaults to `false` (no auto-resolve)
|
|
13
|
+
- Add `{ resolve: true }` option to `useController` for Suspense integration
|
|
14
|
+
- Export new types: `UseAtomSuspenseOptions`, `UseAtomManualOptions`, `UseAtomOptions`, `UseAtomState`, `UseControllerOptions`
|
|
15
|
+
|
|
3
16
|
## 1.0.0
|
|
4
17
|
|
|
5
18
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -91,6 +91,14 @@ ctrl.update(n => n + 1)
|
|
|
91
91
|
ctrl.invalidate()
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
With `{ resolve: true }` option, triggers Suspense if atom not resolved:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// Suspense ensures controller is resolved before render
|
|
98
|
+
const ctrl = useController(configAtom, { resolve: true })
|
|
99
|
+
ctrl.get() // safe - Suspense guarantees resolved state
|
|
100
|
+
```
|
|
101
|
+
|
|
94
102
|
### useAtom
|
|
95
103
|
|
|
96
104
|
Subscribe to atom value with Suspense integration.
|
|
@@ -109,6 +117,39 @@ function UserProfile() {
|
|
|
109
117
|
</ErrorBoundary>
|
|
110
118
|
```
|
|
111
119
|
|
|
120
|
+
#### Non-Suspense Mode
|
|
121
|
+
|
|
122
|
+
For manual loading/error state handling without Suspense:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
function UserProfile() {
|
|
126
|
+
const { data, loading, error, controller } = useAtom(userAtom, { suspense: false })
|
|
127
|
+
|
|
128
|
+
if (loading) return <div>Loading...</div>
|
|
129
|
+
if (error) return <div>Error: {error.message}</div>
|
|
130
|
+
if (!data) return <div>Not loaded</div>
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div>
|
|
134
|
+
<h1>{data.name}</h1>
|
|
135
|
+
<button onClick={() => controller.invalidate()}>Refresh</button>
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
With `{ resolve: true }`, auto-resolves on mount:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// Starts resolution automatically when component mounts
|
|
145
|
+
const { data, loading, error } = useAtom(userAtom, { suspense: false, resolve: true })
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Option | Effect |
|
|
149
|
+
|--------|--------|
|
|
150
|
+
| `{ suspense: false }` | Returns state object, no auto-resolve |
|
|
151
|
+
| `{ suspense: false, resolve: true }` | Returns state object, auto-resolves on mount |
|
|
152
|
+
|
|
112
153
|
### useSelect
|
|
113
154
|
|
|
114
155
|
Fine-grained selection — only re-renders when selected value changes.
|
package/dist/index.cjs
CHANGED
|
@@ -53,49 +53,67 @@ function useScope() {
|
|
|
53
53
|
if (!scope) throw new Error("useScope must be used within a ScopeProvider");
|
|
54
54
|
return scope;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
* Get a memoized controller for an atom.
|
|
58
|
-
*
|
|
59
|
-
* @param atom - The atom to create a controller for
|
|
60
|
-
* @returns A memoized Lite.Controller instance
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```tsx
|
|
64
|
-
* const ctrl = useController(counterAtom)
|
|
65
|
-
* ctrl.set(ctrl.get() + 1)
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
function useController(atom$1) {
|
|
56
|
+
function useController(atom$1, options) {
|
|
69
57
|
const scope = useScope();
|
|
70
|
-
|
|
58
|
+
const ctrl = (0, react.useMemo)(() => scope.controller(atom$1), [scope, atom$1]);
|
|
59
|
+
if (options?.resolve) {
|
|
60
|
+
if (ctrl.state === "idle" || ctrl.state === "resolving") throw getOrCreatePendingPromise(atom$1, ctrl);
|
|
61
|
+
if (ctrl.state === "failed") throw ctrl.get();
|
|
62
|
+
}
|
|
63
|
+
return ctrl;
|
|
71
64
|
}
|
|
72
|
-
|
|
73
|
-
* Subscribe to atom value with Suspense/ErrorBoundary integration.
|
|
74
|
-
* Auto-resolves atoms lazily and throws cached Promise for Suspense.
|
|
75
|
-
*
|
|
76
|
-
* @param atom - The atom to read
|
|
77
|
-
* @returns The current value of the atom
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```tsx
|
|
81
|
-
* function UserProfile() {
|
|
82
|
-
* const user = useAtom(userAtom)
|
|
83
|
-
* return <div>{user.name}</div>
|
|
84
|
-
* }
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
function useAtom(atom$1) {
|
|
65
|
+
function useAtom(atom$1, options) {
|
|
88
66
|
const ctrl = useController(atom$1);
|
|
89
67
|
const atomRef = (0, react.useRef)(atom$1);
|
|
90
68
|
atomRef.current = atom$1;
|
|
69
|
+
if (options?.suspense === false) return useAtomState(atom$1, ctrl, options.resolve ?? false);
|
|
70
|
+
const autoResolve = options?.resolve !== false;
|
|
91
71
|
const getSnapshot = (0, react.useCallback)(() => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
72
|
+
if (ctrl.state === "idle") {
|
|
73
|
+
if (autoResolve) throw getOrCreatePendingPromise(atomRef.current, ctrl);
|
|
74
|
+
throw new Error("Atom is not resolved. Set resolve: true or resolve the atom before rendering.");
|
|
75
|
+
}
|
|
76
|
+
if (ctrl.state === "resolving") throw getOrCreatePendingPromise(atomRef.current, ctrl);
|
|
77
|
+
if (ctrl.state === "failed") throw ctrl.get();
|
|
95
78
|
return ctrl.get();
|
|
96
|
-
}, [ctrl]);
|
|
79
|
+
}, [ctrl, autoResolve]);
|
|
97
80
|
return (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => ctrl.on("resolved", onStoreChange), [ctrl]), getSnapshot, getSnapshot);
|
|
98
81
|
}
|
|
82
|
+
function useAtomState(atom$1, ctrl, autoResolve) {
|
|
83
|
+
const stateCache = (0, react.useRef)(null);
|
|
84
|
+
(0, react.useEffect)(() => {
|
|
85
|
+
if (autoResolve && (ctrl.state === "idle" || ctrl.state === "resolving")) getOrCreatePendingPromise(atom$1, ctrl);
|
|
86
|
+
}, [
|
|
87
|
+
atom$1,
|
|
88
|
+
ctrl,
|
|
89
|
+
autoResolve
|
|
90
|
+
]);
|
|
91
|
+
const getSnapshot = (0, react.useCallback)(() => {
|
|
92
|
+
let data;
|
|
93
|
+
let error;
|
|
94
|
+
if (ctrl.state === "resolved") data = ctrl.get();
|
|
95
|
+
else if (ctrl.state === "failed") try {
|
|
96
|
+
ctrl.get();
|
|
97
|
+
} catch (e) {
|
|
98
|
+
error = e instanceof Error ? e : new Error(String(e));
|
|
99
|
+
}
|
|
100
|
+
if (stateCache.current && stateCache.current.ctrlState === ctrl.state && stateCache.current.data === data && stateCache.current.error === error) return stateCache.current.result;
|
|
101
|
+
const result = {
|
|
102
|
+
data,
|
|
103
|
+
loading: ctrl.state === "resolving",
|
|
104
|
+
error,
|
|
105
|
+
controller: ctrl
|
|
106
|
+
};
|
|
107
|
+
stateCache.current = {
|
|
108
|
+
ctrlState: ctrl.state,
|
|
109
|
+
data,
|
|
110
|
+
error,
|
|
111
|
+
result
|
|
112
|
+
};
|
|
113
|
+
return result;
|
|
114
|
+
}, [ctrl]);
|
|
115
|
+
return (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => ctrl.on("*", onStoreChange), [ctrl]), getSnapshot, getSnapshot);
|
|
116
|
+
}
|
|
99
117
|
/**
|
|
100
118
|
* Select a derived value from an atom with fine-grained reactivity.
|
|
101
119
|
* Only re-renders when the selected value changes per equality function.
|
package/dist/index.d.cts
CHANGED
|
@@ -28,6 +28,26 @@ declare function ScopeProvider({
|
|
|
28
28
|
}: ScopeProviderProps): react_jsx_runtime0.JSX.Element;
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/hooks.d.ts
|
|
31
|
+
interface UseAtomSuspenseOptions {
|
|
32
|
+
suspense?: true;
|
|
33
|
+
/** @default true */
|
|
34
|
+
resolve?: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface UseAtomManualOptions {
|
|
37
|
+
suspense: false;
|
|
38
|
+
/** @default false */
|
|
39
|
+
resolve?: boolean;
|
|
40
|
+
}
|
|
41
|
+
type UseAtomOptions = UseAtomSuspenseOptions | UseAtomManualOptions;
|
|
42
|
+
interface UseAtomState<T> {
|
|
43
|
+
data: T | undefined;
|
|
44
|
+
loading: boolean;
|
|
45
|
+
error: Error | undefined;
|
|
46
|
+
controller: Lite$1.Controller<T>;
|
|
47
|
+
}
|
|
48
|
+
interface UseControllerOptions {
|
|
49
|
+
resolve?: boolean;
|
|
50
|
+
}
|
|
31
51
|
/**
|
|
32
52
|
* Access the current Lite.Scope from context.
|
|
33
53
|
*
|
|
@@ -44,9 +64,6 @@ declare function useScope(): Lite$1.Scope;
|
|
|
44
64
|
/**
|
|
45
65
|
* Get a memoized controller for an atom.
|
|
46
66
|
*
|
|
47
|
-
* @param atom - The atom to create a controller for
|
|
48
|
-
* @returns A memoized Lite.Controller instance
|
|
49
|
-
*
|
|
50
67
|
* @example
|
|
51
68
|
* ```tsx
|
|
52
69
|
* const ctrl = useController(counterAtom)
|
|
@@ -54,22 +71,19 @@ declare function useScope(): Lite$1.Scope;
|
|
|
54
71
|
* ```
|
|
55
72
|
*/
|
|
56
73
|
declare function useController<T>(atom: Lite$1.Atom<T>): Lite$1.Controller<T>;
|
|
74
|
+
declare function useController<T>(atom: Lite$1.Atom<T>, options: UseControllerOptions): Lite$1.Controller<T>;
|
|
57
75
|
/**
|
|
58
76
|
* Subscribe to atom value with Suspense/ErrorBoundary integration.
|
|
59
|
-
* Auto-resolves atoms lazily and throws cached Promise for Suspense.
|
|
60
|
-
*
|
|
61
|
-
* @param atom - The atom to read
|
|
62
|
-
* @returns The current value of the atom
|
|
63
77
|
*
|
|
64
78
|
* @example
|
|
65
79
|
* ```tsx
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* return <div>{user.name}</div>
|
|
69
|
-
* }
|
|
80
|
+
* const user = useAtom(userAtom)
|
|
81
|
+
* const { data, loading, error } = useAtom(userAtom, { suspense: false })
|
|
70
82
|
* ```
|
|
71
83
|
*/
|
|
72
84
|
declare function useAtom<T>(atom: Lite$1.Atom<T>): T;
|
|
85
|
+
declare function useAtom<T>(atom: Lite$1.Atom<T>, options: UseAtomSuspenseOptions): T;
|
|
86
|
+
declare function useAtom<T>(atom: Lite$1.Atom<T>, options: UseAtomManualOptions): UseAtomState<T>;
|
|
73
87
|
/**
|
|
74
88
|
* Select a derived value from an atom with fine-grained reactivity.
|
|
75
89
|
* Only re-renders when the selected value changes per equality function.
|
|
@@ -86,5 +100,5 @@ declare function useAtom<T>(atom: Lite$1.Atom<T>): T;
|
|
|
86
100
|
*/
|
|
87
101
|
declare function useSelect<T, S>(atom: Lite$1.Atom<T>, selector: (value: T) => S, eq?: (a: S, b: S) => boolean): S;
|
|
88
102
|
//#endregion
|
|
89
|
-
export { type Lite, ScopeContext, ScopeProvider, type ScopeProviderProps, atom, createScope, flow, preset, useAtom, useController, useScope, useSelect };
|
|
103
|
+
export { type Lite, ScopeContext, ScopeProvider, type ScopeProviderProps, type UseAtomManualOptions, type UseAtomOptions, type UseAtomState, type UseAtomSuspenseOptions, type UseControllerOptions, atom, createScope, flow, preset, useAtom, useController, useScope, useSelect };
|
|
90
104
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/context.tsx","../src/hooks.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAMM,cAAY,MAAA,CAAA,QAAA,MAAA,CAAA;AALyB,UAOjC,kBAAA,CAFiD;EAEjD,KAAA,EACD,MAAA,CAAK,KADJ;EAeD,QAAA,EAbG,SAaU;;;;;;;;;ACtBqB;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/context.tsx","../src/hooks.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAMM,cAAY,MAAA,CAAA,QAAA,MAAA,CAAA;AALyB,UAOjC,kBAAA,CAFiD;EAEjD,KAAA,EACD,MAAA,CAAK,KADJ;EAeD,QAAA,EAbG,SAaU;;;;;;;;;ACtBqB;AAGX;AAMF;AAMqC,iBDO1D,aAAA,CCLa;EAAA,KAAA;EAAA;AAAA,CAAA,EDKsB,kBCLtB,CAAA,EDKwC,kBAAA,CAAA,GAAA,CAAA,OCLxC;;;UAdZ,sBAAA;;;;;ADHiC,UCSjC,oBAAA,CDJQ;EAER,QAAA,EAAA,KAAA;EAeD;EAAgB,OAAA,CAAA,EAAA,OAAA;;KCPpB,cAAA,GAAiB,sBDOsB,GCPG,oBDOH;UCLlC,YDKoD,CAAA,CAAA,CAAA,CAAA;EAAA,IAAA,ECJtD,CDIsD,GAAA,SAAA;;SCFrD;cACK,MAAA,CAAK,WAAW;AArBa;AAGX,UAqBtB,oBAAA,CAfoB;EAMzB,OAAA,CAAA,EAAA,OAAc;AAAgD;;;;;;AAMtC;AAGC;AA4BC;;;;;iBAAtB,QAAA,CAAA,CAiBqD,EAjBzC,MAAA,CAAK,KAiBoC;AAAA;;;;;;;AAC+B;;iBADpF,aA2BsB,CAAA,CAAA,CAAA,CAAA,IAAA,EA3BC,MAAA,CAAK,IA2BN,CA3BW,CA2BX,CAAA,CAAA,EA3BgB,MAAA,CAAK,UA2BrB,CA3BgC,CA2BhC,CAAA;iBA1BtB,aA0BgC,CAAA,CAAA,CAAA,CAAA,IAAA,EA1BT,MAAA,CAAK,IA0BI,CA1BC,CA0BD,CAAA,EAAA,OAAA,EA1Bc,oBA0Bd,CAAA,EA1BqC,MAAA,CAAK,UA0B1C,CA1BqD,CA0BrD,CAAA;;AAAC;;;;;;AACiC;;iBADlE,OAEsB,CAAA,CAAA,CAAA,CAAA,IAAA,EAFL,MAAA,CAAK,IAEA,CAFK,CAEL,CAAA,CAAA,EAFU,CAEV;iBADtB,OACwC,CAAA,CAAA,CAAA,CAAA,IAAA,EADvB,MAAA,CAAK,IACkB,CADb,CACa,CAAA,EAAA,OAAA,EADA,sBACA,CAAA,EADyB,CACzB;iBAAxC,OAA4E,CAAA,CAAA,CAAA,CAAA,IAAA,EAA3D,MAAA,CAAK,IAAsD,CAAjD,CAAiD,CAAA,EAAA,OAAA,EAApC,oBAAoC,CAAA,EAAb,YAAa,CAAA,CAAA,CAAA;;;AAAD;;;;;;;;;;;;iBA8G3E,sBACD,MAAA,CAAK,KAAK,sBACE,MAAM,YACf,MAAM,gBACd"}
|
package/dist/index.d.mts
CHANGED
|
@@ -28,6 +28,26 @@ declare function ScopeProvider({
|
|
|
28
28
|
}: ScopeProviderProps): react_jsx_runtime0.JSX.Element;
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/hooks.d.ts
|
|
31
|
+
interface UseAtomSuspenseOptions {
|
|
32
|
+
suspense?: true;
|
|
33
|
+
/** @default true */
|
|
34
|
+
resolve?: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface UseAtomManualOptions {
|
|
37
|
+
suspense: false;
|
|
38
|
+
/** @default false */
|
|
39
|
+
resolve?: boolean;
|
|
40
|
+
}
|
|
41
|
+
type UseAtomOptions = UseAtomSuspenseOptions | UseAtomManualOptions;
|
|
42
|
+
interface UseAtomState<T> {
|
|
43
|
+
data: T | undefined;
|
|
44
|
+
loading: boolean;
|
|
45
|
+
error: Error | undefined;
|
|
46
|
+
controller: Lite$1.Controller<T>;
|
|
47
|
+
}
|
|
48
|
+
interface UseControllerOptions {
|
|
49
|
+
resolve?: boolean;
|
|
50
|
+
}
|
|
31
51
|
/**
|
|
32
52
|
* Access the current Lite.Scope from context.
|
|
33
53
|
*
|
|
@@ -44,9 +64,6 @@ declare function useScope(): Lite$1.Scope;
|
|
|
44
64
|
/**
|
|
45
65
|
* Get a memoized controller for an atom.
|
|
46
66
|
*
|
|
47
|
-
* @param atom - The atom to create a controller for
|
|
48
|
-
* @returns A memoized Lite.Controller instance
|
|
49
|
-
*
|
|
50
67
|
* @example
|
|
51
68
|
* ```tsx
|
|
52
69
|
* const ctrl = useController(counterAtom)
|
|
@@ -54,22 +71,19 @@ declare function useScope(): Lite$1.Scope;
|
|
|
54
71
|
* ```
|
|
55
72
|
*/
|
|
56
73
|
declare function useController<T>(atom: Lite$1.Atom<T>): Lite$1.Controller<T>;
|
|
74
|
+
declare function useController<T>(atom: Lite$1.Atom<T>, options: UseControllerOptions): Lite$1.Controller<T>;
|
|
57
75
|
/**
|
|
58
76
|
* Subscribe to atom value with Suspense/ErrorBoundary integration.
|
|
59
|
-
* Auto-resolves atoms lazily and throws cached Promise for Suspense.
|
|
60
|
-
*
|
|
61
|
-
* @param atom - The atom to read
|
|
62
|
-
* @returns The current value of the atom
|
|
63
77
|
*
|
|
64
78
|
* @example
|
|
65
79
|
* ```tsx
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* return <div>{user.name}</div>
|
|
69
|
-
* }
|
|
80
|
+
* const user = useAtom(userAtom)
|
|
81
|
+
* const { data, loading, error } = useAtom(userAtom, { suspense: false })
|
|
70
82
|
* ```
|
|
71
83
|
*/
|
|
72
84
|
declare function useAtom<T>(atom: Lite$1.Atom<T>): T;
|
|
85
|
+
declare function useAtom<T>(atom: Lite$1.Atom<T>, options: UseAtomSuspenseOptions): T;
|
|
86
|
+
declare function useAtom<T>(atom: Lite$1.Atom<T>, options: UseAtomManualOptions): UseAtomState<T>;
|
|
73
87
|
/**
|
|
74
88
|
* Select a derived value from an atom with fine-grained reactivity.
|
|
75
89
|
* Only re-renders when the selected value changes per equality function.
|
|
@@ -86,5 +100,5 @@ declare function useAtom<T>(atom: Lite$1.Atom<T>): T;
|
|
|
86
100
|
*/
|
|
87
101
|
declare function useSelect<T, S>(atom: Lite$1.Atom<T>, selector: (value: T) => S, eq?: (a: S, b: S) => boolean): S;
|
|
88
102
|
//#endregion
|
|
89
|
-
export { type Lite, ScopeContext, ScopeProvider, type ScopeProviderProps, atom, createScope, flow, preset, useAtom, useController, useScope, useSelect };
|
|
103
|
+
export { type Lite, ScopeContext, ScopeProvider, type ScopeProviderProps, type UseAtomManualOptions, type UseAtomOptions, type UseAtomState, type UseAtomSuspenseOptions, type UseControllerOptions, atom, createScope, flow, preset, useAtom, useController, useScope, useSelect };
|
|
90
104
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/context.tsx","../src/hooks.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAMM,cAAY,MAAA,CAAA,QAAA,MAAA,CAAA;AALyB,UAOjC,kBAAA,CAFiD;EAEjD,KAAA,EACD,MAAA,CAAK,KADJ;EAeD,QAAA,EAbG,SAaU;;;;;;;;;ACtBqB;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/context.tsx","../src/hooks.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAMM,cAAY,MAAA,CAAA,QAAA,MAAA,CAAA;AALyB,UAOjC,kBAAA,CAFiD;EAEjD,KAAA,EACD,MAAA,CAAK,KADJ;EAeD,QAAA,EAbG,SAaU;;;;;;;;;ACtBqB;AAGX;AAMF;AAMqC,iBDO1D,aAAA,CCLa;EAAA,KAAA;EAAA;AAAA,CAAA,EDKsB,kBCLtB,CAAA,EDKwC,kBAAA,CAAA,GAAA,CAAA,OCLxC;;;UAdZ,sBAAA;;;;;ADHiC,UCSjC,oBAAA,CDJQ;EAER,QAAA,EAAA,KAAA;EAeD;EAAgB,OAAA,CAAA,EAAA,OAAA;;KCPpB,cAAA,GAAiB,sBDOsB,GCPG,oBDOH;UCLlC,YDKoD,CAAA,CAAA,CAAA,CAAA;EAAA,IAAA,ECJtD,CDIsD,GAAA,SAAA;;SCFrD;cACK,MAAA,CAAK,WAAW;AArBa;AAGX,UAqBtB,oBAAA,CAfoB;EAMzB,OAAA,CAAA,EAAA,OAAc;AAAgD;;;;;;AAMtC;AAGC;AA4BC;;;;;iBAAtB,QAAA,CAAA,CAiBqD,EAjBzC,MAAA,CAAK,KAiBoC;AAAA;;;;;;;AAC+B;;iBADpF,aA2BsB,CAAA,CAAA,CAAA,CAAA,IAAA,EA3BC,MAAA,CAAK,IA2BN,CA3BW,CA2BX,CAAA,CAAA,EA3BgB,MAAA,CAAK,UA2BrB,CA3BgC,CA2BhC,CAAA;iBA1BtB,aA0BgC,CAAA,CAAA,CAAA,CAAA,IAAA,EA1BT,MAAA,CAAK,IA0BI,CA1BC,CA0BD,CAAA,EAAA,OAAA,EA1Bc,oBA0Bd,CAAA,EA1BqC,MAAA,CAAK,UA0B1C,CA1BqD,CA0BrD,CAAA;;AAAC;;;;;;AACiC;;iBADlE,OAEsB,CAAA,CAAA,CAAA,CAAA,IAAA,EAFL,MAAA,CAAK,IAEA,CAFK,CAEL,CAAA,CAAA,EAFU,CAEV;iBADtB,OACwC,CAAA,CAAA,CAAA,CAAA,IAAA,EADvB,MAAA,CAAK,IACkB,CADb,CACa,CAAA,EAAA,OAAA,EADA,sBACA,CAAA,EADyB,CACzB;iBAAxC,OAA4E,CAAA,CAAA,CAAA,CAAA,IAAA,EAA3D,MAAA,CAAK,IAAsD,CAAjD,CAAiD,CAAA,EAAA,OAAA,EAApC,oBAAoC,CAAA,EAAb,YAAa,CAAA,CAAA,CAAA;;;AAAD;;;;;;;;;;;;iBA8G3E,sBACD,MAAA,CAAK,KAAK,sBACE,MAAM,YACf,MAAM,gBACd"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { atom, createScope, flow, preset } from "@pumped-fn/lite";
|
|
2
|
-
import { createContext, useCallback, useContext, useMemo, useRef, useSyncExternalStore } from "react";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/context.tsx
|
|
@@ -53,49 +53,67 @@ function useScope() {
|
|
|
53
53
|
if (!scope) throw new Error("useScope must be used within a ScopeProvider");
|
|
54
54
|
return scope;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
* Get a memoized controller for an atom.
|
|
58
|
-
*
|
|
59
|
-
* @param atom - The atom to create a controller for
|
|
60
|
-
* @returns A memoized Lite.Controller instance
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```tsx
|
|
64
|
-
* const ctrl = useController(counterAtom)
|
|
65
|
-
* ctrl.set(ctrl.get() + 1)
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
function useController(atom$1) {
|
|
56
|
+
function useController(atom$1, options) {
|
|
69
57
|
const scope = useScope();
|
|
70
|
-
|
|
58
|
+
const ctrl = useMemo(() => scope.controller(atom$1), [scope, atom$1]);
|
|
59
|
+
if (options?.resolve) {
|
|
60
|
+
if (ctrl.state === "idle" || ctrl.state === "resolving") throw getOrCreatePendingPromise(atom$1, ctrl);
|
|
61
|
+
if (ctrl.state === "failed") throw ctrl.get();
|
|
62
|
+
}
|
|
63
|
+
return ctrl;
|
|
71
64
|
}
|
|
72
|
-
|
|
73
|
-
* Subscribe to atom value with Suspense/ErrorBoundary integration.
|
|
74
|
-
* Auto-resolves atoms lazily and throws cached Promise for Suspense.
|
|
75
|
-
*
|
|
76
|
-
* @param atom - The atom to read
|
|
77
|
-
* @returns The current value of the atom
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```tsx
|
|
81
|
-
* function UserProfile() {
|
|
82
|
-
* const user = useAtom(userAtom)
|
|
83
|
-
* return <div>{user.name}</div>
|
|
84
|
-
* }
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
function useAtom(atom$1) {
|
|
65
|
+
function useAtom(atom$1, options) {
|
|
88
66
|
const ctrl = useController(atom$1);
|
|
89
67
|
const atomRef = useRef(atom$1);
|
|
90
68
|
atomRef.current = atom$1;
|
|
69
|
+
if (options?.suspense === false) return useAtomState(atom$1, ctrl, options.resolve ?? false);
|
|
70
|
+
const autoResolve = options?.resolve !== false;
|
|
91
71
|
const getSnapshot = useCallback(() => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
72
|
+
if (ctrl.state === "idle") {
|
|
73
|
+
if (autoResolve) throw getOrCreatePendingPromise(atomRef.current, ctrl);
|
|
74
|
+
throw new Error("Atom is not resolved. Set resolve: true or resolve the atom before rendering.");
|
|
75
|
+
}
|
|
76
|
+
if (ctrl.state === "resolving") throw getOrCreatePendingPromise(atomRef.current, ctrl);
|
|
77
|
+
if (ctrl.state === "failed") throw ctrl.get();
|
|
95
78
|
return ctrl.get();
|
|
96
|
-
}, [ctrl]);
|
|
79
|
+
}, [ctrl, autoResolve]);
|
|
97
80
|
return useSyncExternalStore(useCallback((onStoreChange) => ctrl.on("resolved", onStoreChange), [ctrl]), getSnapshot, getSnapshot);
|
|
98
81
|
}
|
|
82
|
+
function useAtomState(atom$1, ctrl, autoResolve) {
|
|
83
|
+
const stateCache = useRef(null);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (autoResolve && (ctrl.state === "idle" || ctrl.state === "resolving")) getOrCreatePendingPromise(atom$1, ctrl);
|
|
86
|
+
}, [
|
|
87
|
+
atom$1,
|
|
88
|
+
ctrl,
|
|
89
|
+
autoResolve
|
|
90
|
+
]);
|
|
91
|
+
const getSnapshot = useCallback(() => {
|
|
92
|
+
let data;
|
|
93
|
+
let error;
|
|
94
|
+
if (ctrl.state === "resolved") data = ctrl.get();
|
|
95
|
+
else if (ctrl.state === "failed") try {
|
|
96
|
+
ctrl.get();
|
|
97
|
+
} catch (e) {
|
|
98
|
+
error = e instanceof Error ? e : new Error(String(e));
|
|
99
|
+
}
|
|
100
|
+
if (stateCache.current && stateCache.current.ctrlState === ctrl.state && stateCache.current.data === data && stateCache.current.error === error) return stateCache.current.result;
|
|
101
|
+
const result = {
|
|
102
|
+
data,
|
|
103
|
+
loading: ctrl.state === "resolving",
|
|
104
|
+
error,
|
|
105
|
+
controller: ctrl
|
|
106
|
+
};
|
|
107
|
+
stateCache.current = {
|
|
108
|
+
ctrlState: ctrl.state,
|
|
109
|
+
data,
|
|
110
|
+
error,
|
|
111
|
+
result
|
|
112
|
+
};
|
|
113
|
+
return result;
|
|
114
|
+
}, [ctrl]);
|
|
115
|
+
return useSyncExternalStore(useCallback((onStoreChange) => ctrl.on("*", onStoreChange), [ctrl]), getSnapshot, getSnapshot);
|
|
116
|
+
}
|
|
99
117
|
/**
|
|
100
118
|
* Select a derived value from an atom with fine-grained reactivity.
|
|
101
119
|
* Only re-renders when the selected value changes per equality function.
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["atom"],"sources":["../src/context.tsx","../src/hooks.ts"],"sourcesContent":["import { createContext, type ReactNode } from 'react'\nimport { type Lite } from '@pumped-fn/lite'\n\n/**\n * React context for Lite.Scope.\n */\nconst ScopeContext = createContext<Lite.Scope | null>(null)\n\ninterface ScopeProviderProps {\n scope: Lite.Scope\n children: ReactNode\n}\n\n/**\n * Provider component for Lite.Scope.\n *\n * @example\n * ```tsx\n * <ScopeProvider scope={scope}>\n * <App />\n * </ScopeProvider>\n * ```\n */\nfunction ScopeProvider({ scope, children }: ScopeProviderProps) {\n return (\n <ScopeContext.Provider value={scope}>\n {children}\n </ScopeContext.Provider>\n )\n}\n\nexport { ScopeContext, ScopeProvider }\nexport type { ScopeProviderProps }\n","import { useCallback, useContext, useMemo, useRef, useSyncExternalStore } from 'react'\nimport { type Lite } from '@pumped-fn/lite'\nimport { ScopeContext } from './context'\n\nconst pendingPromises = new WeakMap<Lite.Atom<unknown>, Promise<unknown>>()\n\nfunction getOrCreatePendingPromise<T>(atom: Lite.Atom<T>, ctrl: Lite.Controller<T>): Promise<T> {\n let pending = pendingPromises.get(atom) as Promise<T> | undefined\n if (!pending) {\n pending = ctrl.resolve()\n pendingPromises.set(atom, pending)\n pending.finally(() => pendingPromises.delete(atom))\n }\n return pending\n}\n\n/**\n * Access the current Lite.Scope from context.\n *\n * @returns The current Lite.Scope instance from context\n * @throws When called outside of a ScopeProvider\n *\n * @example\n * ```tsx\n * const scope = useScope()\n * await scope.resolve(myAtom)\n * ```\n */\nfunction useScope(): Lite.Scope {\n const scope = useContext(ScopeContext)\n if (!scope) {\n throw new Error(\"useScope must be used within a ScopeProvider\")\n }\n return scope\n}\n\n/**\n * Get a memoized controller for an atom.\n *\n * @param atom - The atom to create a controller for\n * @returns A memoized Lite.Controller instance\n *\n * @example\n * ```tsx\n * const ctrl = useController(counterAtom)\n * ctrl.set(ctrl.get() + 1)\n * ```\n */\nfunction useController<T>(atom: Lite.Atom<T>): Lite.Controller<T> {\n const scope = useScope()\n return useMemo(() => scope.controller(atom), [scope, atom])\n}\n\n/**\n * Subscribe to atom value with Suspense/ErrorBoundary integration.\n * Auto-resolves atoms lazily and throws cached Promise for Suspense.\n *\n * @param atom - The atom to read\n * @returns The current value of the atom\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const user = useAtom(userAtom)\n * return <div>{user.name}</div>\n * }\n * ```\n */\nfunction useAtom<T>(atom: Lite.Atom<T>): T {\n const ctrl = useController(atom)\n const atomRef = useRef(atom)\n atomRef.current = atom\n\n const getSnapshot = useCallback((): T => {\n const state = ctrl.state\n if (state === 'idle' || state === 'resolving') {\n throw getOrCreatePendingPromise(atomRef.current, ctrl)\n }\n if (state === 'failed') {\n throw ctrl.get()\n }\n return ctrl.get()\n }, [ctrl])\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => ctrl.on('resolved', onStoreChange),\n [ctrl]\n )\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n\n/**\n * Select a derived value from an atom with fine-grained reactivity.\n * Only re-renders when the selected value changes per equality function.\n *\n * @param atom - The atom to select from\n * @param selector - Function to extract a derived value\n * @param eq - Optional equality function\n * @returns The selected value\n *\n * @example\n * ```tsx\n * const name = useSelect(userAtom, user => user.name)\n * ```\n */\nfunction useSelect<T, S>(\n atom: Lite.Atom<T>,\n selector: (value: T) => S,\n eq?: (a: S, b: S) => boolean\n): S {\n const scope = useScope()\n const ctrl = useController(atom)\n\n const atomRef = useRef(atom)\n atomRef.current = atom\n\n const selectorRef = useRef(selector)\n const eqRef = useRef(eq)\n selectorRef.current = selector\n eqRef.current = eq\n\n const handleRef = useRef<{\n scope: Lite.Scope\n atom: Lite.Atom<T>\n handle: Lite.SelectHandle<S>\n } | null>(null)\n\n const getOrCreateHandle = useCallback(() => {\n if (\n !handleRef.current ||\n handleRef.current.scope !== scope ||\n handleRef.current.atom !== atom\n ) {\n const handle = scope.select(atom, selectorRef.current, { eq: eqRef.current })\n handleRef.current = { scope, atom, handle }\n }\n return handleRef.current.handle\n }, [scope, atom])\n\n const getSnapshot = useCallback((): S => {\n const state = ctrl.state\n if (state === 'idle' || state === 'resolving') {\n throw getOrCreatePendingPromise(atomRef.current, ctrl)\n }\n if (state === 'failed') {\n throw ctrl.get()\n }\n return getOrCreateHandle().get()\n }, [ctrl, getOrCreateHandle])\n\n const subscribe = useCallback((onStoreChange: () => void) => {\n if (ctrl.state !== 'resolved') {\n return () => {}\n }\n return getOrCreateHandle().subscribe(onStoreChange)\n }, [ctrl, getOrCreateHandle])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n\nexport { useScope, useController, useAtom, useSelect }\n"],"mappings":";;;;;;;;AAMA,MAAM,eAAe,cAAiC,KAAK;;;;;;;;;;;AAiB3D,SAAS,cAAc,EAAE,OAAO,YAAgC;AAC9D,QACE,oBAAC,aAAa;EAAS,OAAO;EAC3B;GACqB;;;;;ACvB5B,MAAM,kCAAkB,IAAI,SAA+C;AAE3E,SAAS,0BAA6B,QAAoB,MAAsC;CAC9F,IAAI,UAAU,gBAAgB,IAAIA,OAAK;AACvC,KAAI,CAAC,SAAS;AACZ,YAAU,KAAK,SAAS;AACxB,kBAAgB,IAAIA,QAAM,QAAQ;AAClC,UAAQ,cAAc,gBAAgB,OAAOA,OAAK,CAAC;;AAErD,QAAO;;;;;;;;;;;;;;AAeT,SAAS,WAAuB;CAC9B,MAAM,QAAQ,WAAW,aAAa;AACtC,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,+CAA+C;AAEjE,QAAO;;;;;;;;;;;;;;AAeT,SAAS,cAAiB,QAAwC;CAChE,MAAM,QAAQ,UAAU;AACxB,QAAO,cAAc,MAAM,WAAWA,OAAK,EAAE,CAAC,OAAOA,OAAK,CAAC;;;;;;;;;;;;;;;;;AAkB7D,SAAS,QAAW,QAAuB;CACzC,MAAM,OAAO,cAAcA,OAAK;CAChC,MAAM,UAAU,OAAOA,OAAK;AAC5B,SAAQ,UAAUA;CAElB,MAAM,cAAc,kBAAqB;EACvC,MAAM,QAAQ,KAAK;AACnB,MAAI,UAAU,UAAU,UAAU,YAChC,OAAM,0BAA0B,QAAQ,SAAS,KAAK;AAExD,MAAI,UAAU,SACZ,OAAM,KAAK,KAAK;AAElB,SAAO,KAAK,KAAK;IAChB,CAAC,KAAK,CAAC;AAOV,QAAO,qBALW,aACf,kBAA8B,KAAK,GAAG,YAAY,cAAc,EACjE,CAAC,KAAK,CACP,EAEsC,aAAa,YAAY;;;;;;;;;;;;;;;;AAiBlE,SAAS,UACP,QACA,UACA,IACG;CACH,MAAM,QAAQ,UAAU;CACxB,MAAM,OAAO,cAAcA,OAAK;CAEhC,MAAM,UAAU,OAAOA,OAAK;AAC5B,SAAQ,UAAUA;CAElB,MAAM,cAAc,OAAO,SAAS;CACpC,MAAM,QAAQ,OAAO,GAAG;AACxB,aAAY,UAAU;AACtB,OAAM,UAAU;CAEhB,MAAM,YAAY,OAIR,KAAK;CAEf,MAAM,oBAAoB,kBAAkB;AAC1C,MACE,CAAC,UAAU,WACX,UAAU,QAAQ,UAAU,SAC5B,UAAU,QAAQ,SAASA,OAG3B,WAAU,UAAU;GAAE;GAAO;GAAM,QADpB,MAAM,OAAOA,QAAM,YAAY,SAAS,EAAE,IAAI,MAAM,SAAS,CAAC;GAClC;AAE7C,SAAO,UAAU,QAAQ;IACxB,CAAC,OAAOA,OAAK,CAAC;CAEjB,MAAM,cAAc,kBAAqB;EACvC,MAAM,QAAQ,KAAK;AACnB,MAAI,UAAU,UAAU,UAAU,YAChC,OAAM,0BAA0B,QAAQ,SAAS,KAAK;AAExD,MAAI,UAAU,SACZ,OAAM,KAAK,KAAK;AAElB,SAAO,mBAAmB,CAAC,KAAK;IAC/B,CAAC,MAAM,kBAAkB,CAAC;AAS7B,QAAO,qBAPW,aAAa,kBAA8B;AAC3D,MAAI,KAAK,UAAU,WACjB,cAAa;AAEf,SAAO,mBAAmB,CAAC,UAAU,cAAc;IAClD,CAAC,MAAM,kBAAkB,CAAC,EAEU,aAAa,YAAY"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["atom","data: T | undefined","error: Error | undefined","result: UseAtomState<T>"],"sources":["../src/context.tsx","../src/hooks.ts"],"sourcesContent":["import { createContext, type ReactNode } from 'react'\nimport { type Lite } from '@pumped-fn/lite'\n\n/**\n * React context for Lite.Scope.\n */\nconst ScopeContext = createContext<Lite.Scope | null>(null)\n\ninterface ScopeProviderProps {\n scope: Lite.Scope\n children: ReactNode\n}\n\n/**\n * Provider component for Lite.Scope.\n *\n * @example\n * ```tsx\n * <ScopeProvider scope={scope}>\n * <App />\n * </ScopeProvider>\n * ```\n */\nfunction ScopeProvider({ scope, children }: ScopeProviderProps) {\n return (\n <ScopeContext.Provider value={scope}>\n {children}\n </ScopeContext.Provider>\n )\n}\n\nexport { ScopeContext, ScopeProvider }\nexport type { ScopeProviderProps }\n","import { useCallback, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'\nimport { type Lite } from '@pumped-fn/lite'\nimport { ScopeContext } from './context'\n\ninterface UseAtomSuspenseOptions {\n suspense?: true\n /** @default true */\n resolve?: boolean\n}\n\ninterface UseAtomManualOptions {\n suspense: false\n /** @default false */\n resolve?: boolean\n}\n\ntype UseAtomOptions = UseAtomSuspenseOptions | UseAtomManualOptions\n\ninterface UseAtomState<T> {\n data: T | undefined\n loading: boolean\n error: Error | undefined\n controller: Lite.Controller<T>\n}\n\ninterface UseControllerOptions {\n resolve?: boolean\n}\n\nconst pendingPromises = new WeakMap<Lite.Atom<unknown>, Promise<unknown>>()\n\nfunction getOrCreatePendingPromise<T>(atom: Lite.Atom<T>, ctrl: Lite.Controller<T>): Promise<T> {\n let pending = pendingPromises.get(atom) as Promise<T> | undefined\n if (!pending) {\n pending = ctrl.resolve()\n pendingPromises.set(atom, pending)\n pending.finally(() => pendingPromises.delete(atom))\n }\n return pending\n}\n\n/**\n * Access the current Lite.Scope from context.\n *\n * @returns The current Lite.Scope instance from context\n * @throws When called outside of a ScopeProvider\n *\n * @example\n * ```tsx\n * const scope = useScope()\n * await scope.resolve(myAtom)\n * ```\n */\nfunction useScope(): Lite.Scope {\n const scope = useContext(ScopeContext)\n if (!scope) {\n throw new Error(\"useScope must be used within a ScopeProvider\")\n }\n return scope\n}\n\n/**\n * Get a memoized controller for an atom.\n *\n * @example\n * ```tsx\n * const ctrl = useController(counterAtom)\n * ctrl.set(ctrl.get() + 1)\n * ```\n */\nfunction useController<T>(atom: Lite.Atom<T>): Lite.Controller<T>\nfunction useController<T>(atom: Lite.Atom<T>, options: UseControllerOptions): Lite.Controller<T>\nfunction useController<T>(atom: Lite.Atom<T>, options?: UseControllerOptions): Lite.Controller<T> {\n const scope = useScope()\n const ctrl = useMemo(() => scope.controller(atom), [scope, atom])\n\n if (options?.resolve) {\n if (ctrl.state === 'idle' || ctrl.state === 'resolving') {\n throw getOrCreatePendingPromise(atom, ctrl)\n }\n if (ctrl.state === 'failed') {\n throw ctrl.get()\n }\n }\n\n return ctrl\n}\n\n/**\n * Subscribe to atom value with Suspense/ErrorBoundary integration.\n *\n * @example\n * ```tsx\n * const user = useAtom(userAtom)\n * const { data, loading, error } = useAtom(userAtom, { suspense: false })\n * ```\n */\nfunction useAtom<T>(atom: Lite.Atom<T>): T\nfunction useAtom<T>(atom: Lite.Atom<T>, options: UseAtomSuspenseOptions): T\nfunction useAtom<T>(atom: Lite.Atom<T>, options: UseAtomManualOptions): UseAtomState<T>\nfunction useAtom<T>(atom: Lite.Atom<T>, options?: UseAtomOptions): T | UseAtomState<T> {\n const ctrl = useController(atom)\n const atomRef = useRef(atom)\n atomRef.current = atom\n\n if (options?.suspense === false) {\n return useAtomState(atom, ctrl, options.resolve ?? false)\n }\n\n const autoResolve = options?.resolve !== false\n\n const getSnapshot = useCallback((): T => {\n if (ctrl.state === 'idle') {\n if (autoResolve) {\n throw getOrCreatePendingPromise(atomRef.current, ctrl)\n }\n throw new Error('Atom is not resolved. Set resolve: true or resolve the atom before rendering.')\n }\n if (ctrl.state === 'resolving') {\n throw getOrCreatePendingPromise(atomRef.current, ctrl)\n }\n if (ctrl.state === 'failed') {\n throw ctrl.get()\n }\n return ctrl.get()\n }, [ctrl, autoResolve])\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => ctrl.on('resolved', onStoreChange),\n [ctrl]\n )\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n\nfunction useAtomState<T>(\n atom: Lite.Atom<T>,\n ctrl: Lite.Controller<T>,\n autoResolve: boolean\n): UseAtomState<T> {\n const stateCache = useRef<{\n ctrlState: Lite.Controller<T>['state']\n data: T | undefined\n error: Error | undefined\n result: UseAtomState<T>\n } | null>(null)\n\n useEffect(() => {\n if (autoResolve && (ctrl.state === 'idle' || ctrl.state === 'resolving')) {\n getOrCreatePendingPromise(atom, ctrl)\n }\n }, [atom, ctrl, autoResolve])\n\n const getSnapshot = useCallback((): UseAtomState<T> => {\n let data: T | undefined\n let error: Error | undefined\n\n if (ctrl.state === 'resolved') {\n data = ctrl.get()\n } else if (ctrl.state === 'failed') {\n try {\n ctrl.get()\n } catch (e) {\n error = e instanceof Error ? e : new Error(String(e))\n }\n }\n\n if (\n stateCache.current &&\n stateCache.current.ctrlState === ctrl.state &&\n stateCache.current.data === data &&\n stateCache.current.error === error\n ) {\n return stateCache.current.result\n }\n\n const result: UseAtomState<T> = {\n data,\n loading: ctrl.state === 'resolving',\n error,\n controller: ctrl,\n }\n\n stateCache.current = { ctrlState: ctrl.state, data, error, result }\n return result\n }, [ctrl])\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => ctrl.on('*', onStoreChange),\n [ctrl]\n )\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n\n/**\n * Select a derived value from an atom with fine-grained reactivity.\n * Only re-renders when the selected value changes per equality function.\n *\n * @param atom - The atom to select from\n * @param selector - Function to extract a derived value\n * @param eq - Optional equality function\n * @returns The selected value\n *\n * @example\n * ```tsx\n * const name = useSelect(userAtom, user => user.name)\n * ```\n */\nfunction useSelect<T, S>(\n atom: Lite.Atom<T>,\n selector: (value: T) => S,\n eq?: (a: S, b: S) => boolean\n): S {\n const scope = useScope()\n const ctrl = useController(atom)\n\n const atomRef = useRef(atom)\n atomRef.current = atom\n\n const selectorRef = useRef(selector)\n const eqRef = useRef(eq)\n selectorRef.current = selector\n eqRef.current = eq\n\n const handleRef = useRef<{\n scope: Lite.Scope\n atom: Lite.Atom<T>\n handle: Lite.SelectHandle<S>\n } | null>(null)\n\n const getOrCreateHandle = useCallback(() => {\n if (\n !handleRef.current ||\n handleRef.current.scope !== scope ||\n handleRef.current.atom !== atom\n ) {\n const handle = scope.select(atom, selectorRef.current, { eq: eqRef.current })\n handleRef.current = { scope, atom, handle }\n }\n return handleRef.current.handle\n }, [scope, atom])\n\n const getSnapshot = useCallback((): S => {\n const state = ctrl.state\n if (state === 'idle' || state === 'resolving') {\n throw getOrCreatePendingPromise(atomRef.current, ctrl)\n }\n if (state === 'failed') {\n throw ctrl.get()\n }\n return getOrCreateHandle().get()\n }, [ctrl, getOrCreateHandle])\n\n const subscribe = useCallback((onStoreChange: () => void) => {\n if (ctrl.state !== 'resolved') {\n return () => {}\n }\n return getOrCreateHandle().subscribe(onStoreChange)\n }, [ctrl, getOrCreateHandle])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n\nexport { useScope, useController, useAtom, useSelect }\nexport type { UseAtomSuspenseOptions, UseAtomManualOptions, UseAtomOptions, UseAtomState, UseControllerOptions }\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pumped-fn/lite-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "React integration for @pumped-fn/lite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@testing-library/jest-dom": "^6.9.1",
|
|
33
33
|
"@testing-library/react": "^16.3.0",
|
|
34
|
-
"@types/react": "^18.3.
|
|
35
|
-
"jsdom": "^27.
|
|
34
|
+
"@types/react": "^18.3.27",
|
|
35
|
+
"jsdom": "^27.3.0",
|
|
36
36
|
"react": "^18.3.1",
|
|
37
|
-
"tsdown": "^0.
|
|
37
|
+
"tsdown": "^0.17.2",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
|
-
"vitest": "^4.0.
|
|
40
|
-
"@pumped-fn/lite": "1.
|
|
39
|
+
"vitest": "^4.0.15",
|
|
40
|
+
"@pumped-fn/lite": "1.10.0"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18"
|