@cripty2001/utils 0.0.143 → 0.0.145

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.
@@ -30,8 +30,9 @@ export declare function useWhispr<T>(data: T | Whispr<T>): Whispr<T>;
30
30
  * Subscribe a callback to a Whispr inside a react component, properly handling unsubscription on unmount.
31
31
  * @param w The whispr to subscribe to
32
32
  * @param cb The callback to call on value change
33
+ * @param unsafe If true, the callback will be allowed to throw errors, that will then bubble up
33
34
  */
34
- export declare function useOnWhispr<T>(w: Whispr<T>, cb: (value: T) => void): void;
35
+ export declare function useOnWhispr<T>(w: Whispr<T>, unsafe: boolean | undefined, cb: (value: T) => void): void;
35
36
  /**
36
37
  * Return a reactive current timestamp (ms), updated at the given interval.
37
38
  * @returns The current timestamp
@@ -52,18 +53,61 @@ export declare function useDebounced<T>(value: T): T;
52
53
  */
53
54
  export declare function useSynced<T extends any>(def: T, value: T | undefined, setValue: ((value: T) => void) | undefined): [T, React.Dispatch<React.SetStateAction<T>>];
54
55
  /**
56
+ * Wraps an async function into a reactable data structure that tracks loading state, progress, and results.
55
57
  *
56
- * Wraps an async function into a reactable data.
58
+ * **Error Handling:** This function does NOT throw errors. Instead, errors are stored in the returned dispatcher's state.
59
+ * Check the dispatcher's `data` property to access the error state. The dispatcher's promise resolves successfully
60
+ * even when errors occur - errors are captured and stored in the reactive state for UI consumption.
57
61
  *
58
- * @param f The async function to call. It should return a promise that resolves to the data. It is not reactive
62
+ * @param f The async function to call. It should return a promise that resolves to the data. It is not reactive.
59
63
  * @param data The data to give to f. It must be stable, as anything in the dependency array of the useEffect and similars in the react ecosystem. If null, this function will act like an useEffect with an empty dependency array.
60
- * @param debouce Debounce time in ms. Default to 200ms. The async function will not be called if this time has not passed since the useAsync first invocation or value change. If another change happens during the wait, the first function call is never executed
61
- * @returns The dispatcher
64
+ * @param debouce Debounce time in ms. Default to 200ms. The async function will not be called if this time has not passed since the useAsync first invocation or value change. If another change happens during the wait, the first function call is never executed.
65
+ * @returns A Dispatcher object containing:
66
+ * - `data`: A Whispr<DispatcherStatePayload<O>> that contains the loading state, progress, and either the result data or error
67
+ * - `filtered`: A Whispr<O | null> that contains the result data when successful, or null when loading or on error
62
68
  *
63
69
  * @type I Input for the async function.
64
- * @type O Output for the async function
70
+ * @type O Output for the async function.
71
+ *
72
+ * @example
73
+ * const dispatcher = useAsync(async (userId) => {
74
+ * const response = await fetch(`/api/users/${userId}`);
75
+ * return response.json();
76
+ * }, userId);
77
+ *
78
+ * const state = useWhisprValue(dispatcher.data);
79
+ * // state can be: { loading: true, progress: 0 } | { loading: false, ok: true, data: T } | { loading: false, ok: false, error: Error }
80
+ *
81
+ * if (!state.loading && !state.ok) {
82
+ * console.error('Error:', state.error);
83
+ * }
65
84
  */
66
85
  export declare function useAsync<I, O>(f: (input: I, setProgress: (p: number) => void, signal: AbortSignal) => Promise<O>, data: I, debouce?: number): Dispatcher<I, O>;
86
+ /**
87
+ * Async version of useEffect with debouncing. Executes an async function as a side effect when data changes.
88
+ *
89
+ * **Error Handling:** This function THROWS errors. Unlike `useAsync`, errors are not stored in state but are thrown
90
+ * as promise rejections. Use this when you want errors to propagate (e.g., to error boundaries or try/catch blocks).
91
+ *
92
+ * @param f The async function to execute. It should return a promise. It is not reactive.
93
+ * @param data The data that triggers the effect. It must be stable, as anything in the dependency array of useEffect.
94
+ * If null, this function will act like useEffect with an empty dependency array.
95
+ * @param debounce Debounce time in ms. Default to 200ms. The async function will not be called if this time has not
96
+ * passed since the last data change. If another change happens during the wait, the first function call is aborted.
97
+ *
98
+ * @remarks This function returns void - it is purely for side effects, similar to useEffect.
99
+ * @remarks Errors thrown by the async function will cause the promise to reject. If you need to handle errors
100
+ * in the UI without throwing, use `useAsync` instead.
101
+ *
102
+ * @example
103
+ * useAsyncEffect(async (userId, setProgress, signal) => {
104
+ * const response = await fetch(`/api/users/${userId}`, { signal });
105
+ * if (!response.ok) throw new Error('Failed to fetch');
106
+ * const data = await response.json();
107
+ * // Do something with data
108
+ * }, userId, 300);
109
+ */
110
+ export declare function useAsyncEffect<I>(f: (input: I, setProgress: (p: number) => void, signal: AbortSignal) => Promise<void>, data: I, debounce?: number): void;
67
111
  /**
68
112
  * Format a timestamp into a relative time string (e.g. "5 minutes ago", "in 2 hours"), using the browser locale.
69
113
  * The refreshed time is reactive.
@@ -7,6 +7,7 @@ exports.useCurrentTimestamp = useCurrentTimestamp;
7
7
  exports.useDebounced = useDebounced;
8
8
  exports.useSynced = useSynced;
9
9
  exports.useAsync = useAsync;
10
+ exports.useAsyncEffect = useAsyncEffect;
10
11
  exports.useRelTime = useRelTime;
11
12
  exports.useSearcher = useSearcher;
12
13
  exports.useSafeRef = useSafeRef;
@@ -65,10 +66,11 @@ function useWhispr(data) {
65
66
  * Subscribe a callback to a Whispr inside a react component, properly handling unsubscription on unmount.
66
67
  * @param w The whispr to subscribe to
67
68
  * @param cb The callback to call on value change
69
+ * @param unsafe If true, the callback will be allowed to throw errors, that will then bubble up
68
70
  */
69
- function useOnWhispr(w, cb) {
71
+ function useOnWhispr(w, unsafe = false, cb) {
70
72
  (0, react_1.useEffect)(() => {
71
- const unsub = w.subscribe(cb);
73
+ const unsub = w.subscribe(cb, undefined, unsafe);
72
74
  return () => unsub();
73
75
  }, [w, cb]);
74
76
  }
@@ -136,16 +138,34 @@ function useSynced(def, value, setValue) {
136
138
  return [v, syncedSetter];
137
139
  }
138
140
  /**
141
+ * Wraps an async function into a reactable data structure that tracks loading state, progress, and results.
139
142
  *
140
- * Wraps an async function into a reactable data.
143
+ * **Error Handling:** This function does NOT throw errors. Instead, errors are stored in the returned dispatcher's state.
144
+ * Check the dispatcher's `data` property to access the error state. The dispatcher's promise resolves successfully
145
+ * even when errors occur - errors are captured and stored in the reactive state for UI consumption.
141
146
  *
142
- * @param f The async function to call. It should return a promise that resolves to the data. It is not reactive
147
+ * @param f The async function to call. It should return a promise that resolves to the data. It is not reactive.
143
148
  * @param data The data to give to f. It must be stable, as anything in the dependency array of the useEffect and similars in the react ecosystem. If null, this function will act like an useEffect with an empty dependency array.
144
- * @param debouce Debounce time in ms. Default to 200ms. The async function will not be called if this time has not passed since the useAsync first invocation or value change. If another change happens during the wait, the first function call is never executed
145
- * @returns The dispatcher
149
+ * @param debouce Debounce time in ms. Default to 200ms. The async function will not be called if this time has not passed since the useAsync first invocation or value change. If another change happens during the wait, the first function call is never executed.
150
+ * @returns A Dispatcher object containing:
151
+ * - `data`: A Whispr<DispatcherStatePayload<O>> that contains the loading state, progress, and either the result data or error
152
+ * - `filtered`: A Whispr<O | null> that contains the result data when successful, or null when loading or on error
146
153
  *
147
154
  * @type I Input for the async function.
148
- * @type O Output for the async function
155
+ * @type O Output for the async function.
156
+ *
157
+ * @example
158
+ * const dispatcher = useAsync(async (userId) => {
159
+ * const response = await fetch(`/api/users/${userId}`);
160
+ * return response.json();
161
+ * }, userId);
162
+ *
163
+ * const state = useWhisprValue(dispatcher.data);
164
+ * // state can be: { loading: true, progress: 0 } | { loading: false, ok: true, data: T } | { loading: false, ok: false, error: Error }
165
+ *
166
+ * if (!state.loading && !state.ok) {
167
+ * console.error('Error:', state.error);
168
+ * }
149
169
  */
150
170
  function useAsync(f, data, debouce = 200) {
151
171
  // Initing reactive input
@@ -160,6 +180,38 @@ function useAsync(f, data, debouce = 200) {
160
180
  // Returning dispatcher
161
181
  return dispatcher;
162
182
  }
183
+ /**
184
+ * Async version of useEffect with debouncing. Executes an async function as a side effect when data changes.
185
+ *
186
+ * **Error Handling:** This function THROWS errors. Unlike `useAsync`, errors are not stored in state but are thrown
187
+ * as promise rejections. Use this when you want errors to propagate (e.g., to error boundaries or try/catch blocks).
188
+ *
189
+ * @param f The async function to execute. It should return a promise. It is not reactive.
190
+ * @param data The data that triggers the effect. It must be stable, as anything in the dependency array of useEffect.
191
+ * If null, this function will act like useEffect with an empty dependency array.
192
+ * @param debounce Debounce time in ms. Default to 200ms. The async function will not be called if this time has not
193
+ * passed since the last data change. If another change happens during the wait, the first function call is aborted.
194
+ *
195
+ * @remarks This function returns void - it is purely for side effects, similar to useEffect.
196
+ * @remarks Errors thrown by the async function will cause the promise to reject. If you need to handle errors
197
+ * in the UI without throwing, use `useAsync` instead.
198
+ *
199
+ * @example
200
+ * useAsyncEffect(async (userId, setProgress, signal) => {
201
+ * const response = await fetch(`/api/users/${userId}`, { signal });
202
+ * if (!response.ok) throw new Error('Failed to fetch');
203
+ * const data = await response.json();
204
+ * // Do something with data
205
+ * }, userId, 300);
206
+ */
207
+ function useAsyncEffect(f, data, debounce = 200) {
208
+ const dispatcher = useAsync(f, data, debounce);
209
+ useOnWhispr(dispatcher.data, true, (data) => {
210
+ if (!data.loading && !data.ok) {
211
+ throw data.error;
212
+ }
213
+ });
214
+ }
163
215
  /**
164
216
  * Format a timestamp into a relative time string (e.g. "5 minutes ago", "in 2 hours"), using the browser locale.
165
217
  * The refreshed time is reactive.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cripty2001/utils",
3
- "version": "0.0.143",
3
+ "version": "0.0.145",
4
4
  "description": "Internal Set of utils. If you need them use them, otherwise go to the next package ;)",
5
5
  "homepage": "https://github.com/cripty2001/utils#readme",
6
6
  "bugs": {
@@ -22,7 +22,7 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@cripty2001/whispr": "^0.2.1",
25
+ "@cripty2001/whispr": "^0.2.3",
26
26
  "lodash": "^4.17.21"
27
27
  },
28
28
  "devDependencies": {