stunk 2.8.1 → 3.0.0-beta.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.
- package/README.md +228 -32
- package/dist/chunk-3DOB632D.js +1 -0
- package/dist/chunk-KWDC37TK.js +1 -0
- package/dist/{core-DMY69lzg.d.cts → core-DsoxfUCH.d.cts} +6 -1
- package/dist/{core-DMY69lzg.d.ts → core-DsoxfUCH.d.ts} +6 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +484 -150
- package/dist/index.d.ts +484 -150
- package/dist/index.js +1 -1
- package/dist/middleware/index.cjs +1 -1
- package/dist/middleware/index.d.cts +77 -23
- package/dist/middleware/index.d.ts +77 -23
- package/dist/middleware/index.js +1 -1
- package/dist/mutation-BGXZyZXA.d.ts +214 -0
- package/dist/mutation-Txd2ni6w.d.cts +214 -0
- package/dist/query/index.cjs +1 -0
- package/dist/query/index.d.cts +107 -0
- package/dist/query/index.d.ts +107 -0
- package/dist/query/index.js +1 -0
- package/dist/use-react/index.cjs +1 -1
- package/dist/use-react/index.d.cts +131 -94
- package/dist/use-react/index.d.ts +131 -94
- package/dist/use-react/index.js +1 -1
- package/package.json +122 -121
package/dist/index.d.cts
CHANGED
|
@@ -4,51 +4,249 @@ type NamedMiddleware<T> = {
|
|
|
4
4
|
name?: string;
|
|
5
5
|
fn: Middleware<T>;
|
|
6
6
|
};
|
|
7
|
+
interface ChunkConfig<T> {
|
|
8
|
+
name?: string;
|
|
9
|
+
middleware?: (Middleware<T> | NamedMiddleware<T>)[];
|
|
10
|
+
/**
|
|
11
|
+
* When true, setting a value with keys not present in the initial
|
|
12
|
+
* shape throws an error in development. Useful for catching accidental
|
|
13
|
+
* shape mutations early. Has no effect in production. (default: false)
|
|
14
|
+
*/
|
|
15
|
+
strict?: boolean;
|
|
16
|
+
}
|
|
7
17
|
interface Chunk<T> {
|
|
8
18
|
/** Get the current value of the chunk. */
|
|
9
19
|
get: () => T;
|
|
20
|
+
/** Peek at the current value without tracking dependencies. */
|
|
21
|
+
peek: () => T;
|
|
10
22
|
/** Set a new value for the chunk & Update existing value efficiently. */
|
|
11
23
|
set: (newValueOrUpdater: T | ((currentValue: T) => T)) => void;
|
|
12
24
|
/** Subscribe to changes in the chunk. Returns an unsubscribe function. */
|
|
13
25
|
subscribe: (callback: Subscriber<T>) => () => void;
|
|
14
26
|
/** Create a derived chunk based on this chunk's value. */
|
|
15
|
-
derive: <D>(fn: (value: T) => D) =>
|
|
27
|
+
derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
|
|
16
28
|
/** Reset the chunk to its initial value. */
|
|
17
29
|
reset: () => void;
|
|
18
30
|
/** Destroy the chunk and all its subscribers. */
|
|
19
31
|
destroy: () => void;
|
|
20
32
|
}
|
|
33
|
+
interface ReadOnlyChunk<T> extends Omit<Chunk<T>, 'set' | 'reset'> {
|
|
34
|
+
derive: <D>(fn: (value: T) => D) => ReadOnlyChunk<D>;
|
|
35
|
+
}
|
|
21
36
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
37
|
+
* Groups multiple chunk updates into a single notification pass.
|
|
38
|
+
*
|
|
39
|
+
* Without batching, each `set()` call notifies subscribers immediately.
|
|
40
|
+
* Inside a `batch()`, all updates are collected and subscribers are notified
|
|
41
|
+
* once after the callback completes — even if multiple chunks were updated.
|
|
42
|
+
*
|
|
43
|
+
* Batches can be nested safely. Notifications only flush when the outermost
|
|
44
|
+
* batch completes.
|
|
45
|
+
*
|
|
46
|
+
* @param callback - A function containing one or more chunk `set()` calls.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const x = chunk(0);
|
|
50
|
+
* const y = chunk(0);
|
|
51
|
+
*
|
|
52
|
+
* batch(() => {
|
|
53
|
+
* x.set(1);
|
|
54
|
+
* y.set(2);
|
|
55
|
+
* });
|
|
56
|
+
* // Subscribers of x and y are each notified once, not twice.
|
|
24
57
|
*/
|
|
25
58
|
declare function batch(callback: () => void): void;
|
|
26
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Creates a reactive state unit — the core primitive of Stunk.
|
|
61
|
+
*
|
|
62
|
+
* A chunk holds a single value and notifies all subscribers whenever that
|
|
63
|
+
* value changes. Values are compared by reference (`===`) for primitives and
|
|
64
|
+
* by the result of middleware processing for objects — so setting the same
|
|
65
|
+
* value twice does not trigger subscribers.
|
|
66
|
+
*
|
|
67
|
+
* @param initialValue - The starting value. Cannot be `undefined`.
|
|
68
|
+
* @param config - Optional configuration for naming, middleware, and strict mode.
|
|
69
|
+
* @returns A `Chunk<T>` with `get()`, `set()`, `peek()`, `subscribe()`, `derive()`, `reset()`, and `destroy()`.
|
|
70
|
+
*/
|
|
71
|
+
declare function chunk<T>(initialValue: T, config?: ChunkConfig<T>): Chunk<T>;
|
|
27
72
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
73
|
+
interface ChunkMeta {
|
|
74
|
+
name: string;
|
|
75
|
+
id: number;
|
|
76
|
+
}
|
|
77
|
+
declare function isValidChunkValue(value: unknown): boolean;
|
|
78
|
+
declare function isChunk<T>(value: unknown): value is Chunk<T>;
|
|
79
|
+
declare function getChunkMeta<T>(chunk: Chunk<T>): ChunkMeta | undefined;
|
|
80
|
+
|
|
81
|
+
interface Computed<T> extends ReadOnlyChunk<T> {
|
|
82
|
+
/** Checks if the computed value needs to be recalculated due to dependency changes. */
|
|
83
|
+
isDirty: () => boolean;
|
|
84
|
+
/** Manually forces recalculation of the computed value from its dependencies. */
|
|
85
|
+
recompute: () => void;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates a derived value that automatically tracks its dependencies and
|
|
89
|
+
* recomputes lazily when any of them change.
|
|
90
|
+
*
|
|
91
|
+
* Dependencies are discovered automatically — any chunk whose `.get()` is
|
|
92
|
+
* called inside `computeFn` is tracked. Use `.peek()` inside the function
|
|
93
|
+
* to read a value without tracking it as a dependency.
|
|
94
|
+
*
|
|
95
|
+
* The computed value is cached and only recalculated when a dependency
|
|
96
|
+
* changes and the value is accessed (lazy) or when active subscribers exist
|
|
97
|
+
* (eager). Object values are compared with shallow equality to prevent
|
|
98
|
+
* unnecessary subscriber notifications.
|
|
99
|
+
*
|
|
100
|
+
* @param computeFn - A pure function that derives the computed value.
|
|
101
|
+
* @returns A read-only `Computed<T>` with `isDirty()`, `recompute()`, `derive()`, `subscribe()`, `peek()`, and `destroy()`.
|
|
102
|
+
*/
|
|
103
|
+
declare function computed<T>(computeFn: () => T): Computed<T>;
|
|
104
|
+
|
|
105
|
+
interface SelectOptions {
|
|
106
|
+
/**
|
|
107
|
+
* When `true`, uses shallow equality to compare selected values.
|
|
108
|
+
* Prevents unnecessary subscriber notifications when the selected
|
|
109
|
+
* object is a new reference but has the same property values.
|
|
110
|
+
*/
|
|
111
|
+
useShallowEqual?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Creates a read-only derived chunk that tracks a slice of a source chunk.
|
|
115
|
+
*
|
|
116
|
+
* Only notifies subscribers when the selected value actually changes —
|
|
117
|
+
* updates to unselected parts of the source are ignored.
|
|
118
|
+
*
|
|
119
|
+
* @param sourceChunk - The chunk to select from.
|
|
120
|
+
* @param selector - A function that extracts the desired slice.
|
|
121
|
+
* @param options.useShallowEqual - When `true`, uses shallow equality to
|
|
122
|
+
* compare selected values — prevents unnecessary updates for objects.
|
|
123
|
+
* @returns A `ReadOnlyChunk<S>` that updates only when the selected value changes.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const user = chunk({ name: 'Alice', age: 30 });
|
|
127
|
+
* const name = select(user, u => u.name);
|
|
128
|
+
* name.get(); // 'Alice'
|
|
129
|
+
*
|
|
130
|
+
* // age changes — name subscribers are NOT notified
|
|
131
|
+
* user.set({ name: 'Alice', age: 31 });
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* // Shallow equality — prevents updates when object values are the same
|
|
135
|
+
* const details = select(user, u => u.details, { useShallowEqual: true });
|
|
136
|
+
*/
|
|
137
|
+
declare function select<T, S>(sourceChunk: Chunk<T> | ReadOnlyChunk<T>, selector: (value: T) => S, options?: SelectOptions): ReadOnlyChunk<S>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Middleware that logs every value passed to `set()` to the console.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* const count = chunk(0, { middleware: [logger()] });
|
|
144
|
+
* count.set(5); // logs: "Setting value: 5"
|
|
145
|
+
*/
|
|
146
|
+
declare function logger<T>(): Middleware<T>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Middleware that throws if a numeric value is set below zero.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* const balance = chunk(100, { middleware: [nonNegativeValidator] });
|
|
153
|
+
* balance.set(-1); // throws: "Value must be non-negative!"
|
|
154
|
+
*/
|
|
155
|
+
declare const nonNegativeValidator: Middleware<number>;
|
|
156
|
+
|
|
157
|
+
interface ChunkWithHistory<T> extends Chunk<T> {
|
|
158
|
+
/** Reverts to the previous state (if available). */
|
|
159
|
+
undo: () => void;
|
|
160
|
+
/** Moves to the next state (if available). */
|
|
161
|
+
redo: () => void;
|
|
162
|
+
/** Returns true if there is a previous state to revert to. */
|
|
163
|
+
canUndo: () => boolean;
|
|
164
|
+
/** Returns true if there is a next state to move to. */
|
|
165
|
+
canRedo: () => boolean;
|
|
166
|
+
/** Returns an array of all the values in the history. */
|
|
167
|
+
getHistory: () => T[];
|
|
168
|
+
/** Clears the history, keeping only the current value. */
|
|
169
|
+
clearHistory: () => void;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Wraps a chunk with undo/redo history tracking.
|
|
173
|
+
*
|
|
174
|
+
* Every `set()` call is recorded. `undo()` and `redo()` move through the stack.
|
|
175
|
+
* Branching is supported — calling `set()` after `undo()` discards forward history.
|
|
176
|
+
*
|
|
177
|
+
* @param baseChunk - The chunk to wrap.
|
|
178
|
+
* @param options.maxHistory - Max entries to keep (default: 100).
|
|
179
|
+
* @param options.skipDuplicates - `true` skips strictly equal values.
|
|
180
|
+
* `'shallow'` also skips shallowly equal objects.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* const count = chunk(0);
|
|
184
|
+
* const tracked = history(count);
|
|
185
|
+
* tracked.set(1); tracked.set(2);
|
|
186
|
+
* tracked.undo(); // 1
|
|
187
|
+
* tracked.redo(); // 2
|
|
188
|
+
*/
|
|
189
|
+
declare function history<T>(baseChunk: Chunk<T>, options?: {
|
|
190
|
+
maxHistory?: number;
|
|
191
|
+
/**
|
|
192
|
+
* true — skip entries that are strictly equal (===) to the current value.
|
|
193
|
+
* 'shallow' — also skip entries that are shallowly equal to the current value.
|
|
194
|
+
*/
|
|
195
|
+
skipDuplicates?: boolean | "shallow";
|
|
196
|
+
}): ChunkWithHistory<T>;
|
|
197
|
+
|
|
198
|
+
interface PersistOptions<T> {
|
|
199
|
+
/** Storage key (required). */
|
|
200
|
+
key: string;
|
|
201
|
+
/** Storage engine (default: localStorage). */
|
|
202
|
+
storage?: Storage;
|
|
203
|
+
/** Serialize value to string (default: JSON.stringify). */
|
|
204
|
+
serialize?: (value: T) => string;
|
|
205
|
+
/** Deserialize string to value (default: JSON.parse). */
|
|
206
|
+
deserialize?: (value: string) => T;
|
|
207
|
+
/** Called on load/save errors and type mismatches. */
|
|
208
|
+
onError?: (error: Error, operation: 'load' | 'save') => void;
|
|
209
|
+
}
|
|
210
|
+
interface PersistedChunk<T> extends Chunk<T> {
|
|
211
|
+
/** Remove the persisted key from storage without destroying the chunk. */
|
|
212
|
+
clearStorage: () => void;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Wraps a chunk with automatic persistence to a storage engine.
|
|
216
|
+
*
|
|
217
|
+
* Loads any saved value on creation. Saves on every `set()`.
|
|
218
|
+
* Gracefully disabled in SSR when no storage is available.
|
|
219
|
+
*
|
|
220
|
+
* @param baseChunk - The chunk to wrap.
|
|
221
|
+
* @param options.key - Storage key (required).
|
|
222
|
+
* @param options.storage - Storage engine (default: `localStorage`).
|
|
223
|
+
* @param options.serialize - Custom serializer (default: `JSON.stringify`).
|
|
224
|
+
* @param options.deserialize - Custom deserializer (default: `JSON.parse`).
|
|
225
|
+
* @param options.onError - Called on load/save errors or type mismatches.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* const user = chunk({ name: 'Alice' });
|
|
229
|
+
* const persisted = persist(user, { key: 'user' });
|
|
230
|
+
* persisted.set({ name: 'Bob' }); // saved to localStorage
|
|
231
|
+
* persisted.clearStorage(); // removes the key
|
|
232
|
+
*/
|
|
233
|
+
declare function persist<T>(baseChunk: Chunk<T>, options: PersistOptions<T>): PersistedChunk<T>;
|
|
234
|
+
|
|
235
|
+
declare const index$1_history: typeof history;
|
|
236
|
+
declare const index$1_logger: typeof logger;
|
|
237
|
+
declare const index$1_nonNegativeValidator: typeof nonNegativeValidator;
|
|
238
|
+
declare const index$1_persist: typeof persist;
|
|
239
|
+
declare namespace index$1 {
|
|
240
|
+
export { index$1_history as history, index$1_logger as logger, index$1_nonNegativeValidator as nonNegativeValidator, index$1_persist as persist };
|
|
241
|
+
}
|
|
46
242
|
|
|
47
243
|
interface AsyncState<T, E extends Error> {
|
|
48
244
|
loading: boolean;
|
|
49
245
|
error: E | null;
|
|
50
246
|
data: T | null;
|
|
51
247
|
lastFetched?: number;
|
|
248
|
+
/** True when showing previous data while new data is loading (keepPreviousData: true) */
|
|
249
|
+
isPlaceholderData?: boolean;
|
|
52
250
|
}
|
|
53
251
|
interface PaginationState {
|
|
54
252
|
page: number;
|
|
@@ -59,82 +257,111 @@ interface PaginationState {
|
|
|
59
257
|
interface AsyncStateWithPagination<T, E extends Error> extends AsyncState<T, E> {
|
|
60
258
|
pagination?: PaginationState;
|
|
61
259
|
}
|
|
62
|
-
interface RefreshConfig {
|
|
63
|
-
/** Time in ms after which data becomes stale */
|
|
64
|
-
staleTime?: number;
|
|
65
|
-
/** Time in ms to cache data */
|
|
66
|
-
cacheTime?: number;
|
|
67
|
-
/** Auto-refresh interval in ms */
|
|
68
|
-
refetchInterval?: number;
|
|
69
|
-
}
|
|
70
|
-
interface PaginationConfig {
|
|
71
|
-
/** Initial page number (default: 1) */
|
|
72
|
-
initialPage?: number;
|
|
73
|
-
/** Items per page (default: 10) */
|
|
74
|
-
pageSize?: number;
|
|
75
|
-
/** Whether to accumulate pages (infinite scroll) or replace */
|
|
76
|
-
mode?: 'replace' | 'accumulate';
|
|
77
|
-
}
|
|
78
|
-
interface AsyncChunkOptExtended<T, E extends Error> extends AsyncChunkOpt<T, E> {
|
|
79
|
-
refresh?: RefreshConfig;
|
|
80
|
-
pagination?: PaginationConfig;
|
|
81
|
-
/** Enable/disable the fetcher */
|
|
82
|
-
enabled?: boolean;
|
|
83
|
-
}
|
|
84
260
|
interface FetcherResponse<T> {
|
|
85
261
|
data: T;
|
|
86
262
|
total?: number;
|
|
87
263
|
hasMore?: boolean;
|
|
88
264
|
}
|
|
265
|
+
interface AsyncChunkOptions<T, E extends Error = Error> {
|
|
266
|
+
/** Deduplication key — concurrent calls with the same key share one in-flight request */
|
|
267
|
+
key?: string;
|
|
268
|
+
/** Seed data shown before the first fetch completes */
|
|
269
|
+
initialData?: T | null;
|
|
270
|
+
/** Disable fetching until ready — pass a function for dynamic evaluation */
|
|
271
|
+
enabled?: boolean | (() => boolean);
|
|
272
|
+
/** Called after every successful fetch */
|
|
273
|
+
onSuccess?: (data: T) => void;
|
|
274
|
+
/** Called when all retries are exhausted */
|
|
275
|
+
onError?: (error: E) => void;
|
|
276
|
+
/** Number of retries on failure (default: 0) */
|
|
277
|
+
retryCount?: number;
|
|
278
|
+
/** Delay in ms between retries (default: 1000) */
|
|
279
|
+
retryDelay?: number;
|
|
280
|
+
/** Show previous data while refetching — prevents UI flicker on param changes (default: false) */
|
|
281
|
+
keepPreviousData?: boolean;
|
|
282
|
+
/** Time in ms before data is considered stale (default: 0) */
|
|
283
|
+
staleTime?: number;
|
|
284
|
+
/** Time in ms to cache data after last subscriber leaves (default: 300_000) */
|
|
285
|
+
cacheTime?: number;
|
|
286
|
+
/** Auto-refetch interval in ms */
|
|
287
|
+
refetchInterval?: number;
|
|
288
|
+
/** Refetch when window regains focus (default: false) */
|
|
289
|
+
refetchOnWindowFocus?: boolean;
|
|
290
|
+
pagination?: {
|
|
291
|
+
/** Initial page number (default: 1) */
|
|
292
|
+
initialPage?: number;
|
|
293
|
+
/** Items per page (default: 10) */
|
|
294
|
+
pageSize?: number;
|
|
295
|
+
/** Replace data on each page load, or accumulate for infinite scroll (default: 'replace') */
|
|
296
|
+
mode?: 'replace' | 'accumulate';
|
|
297
|
+
};
|
|
298
|
+
}
|
|
89
299
|
interface AsyncChunk<T, E extends Error = Error> extends Chunk<AsyncStateWithPagination<T, E>> {
|
|
90
|
-
/** Force
|
|
300
|
+
/** Force a fresh fetch, ignoring stale time */
|
|
91
301
|
reload: (params?: any) => Promise<void>;
|
|
92
|
-
/**
|
|
302
|
+
/** Fetch only if data is stale — respects staleTime */
|
|
93
303
|
refresh: (params?: any) => Promise<void>;
|
|
94
|
-
/**
|
|
304
|
+
/** Update data directly without a network request */
|
|
95
305
|
mutate: (mutator: (currentData: T | null) => T) => void;
|
|
96
|
-
/** Reset to initial state */
|
|
306
|
+
/** Reset to initial state and re-fetch */
|
|
97
307
|
reset: () => void;
|
|
98
|
-
/**
|
|
308
|
+
/** Safe cleanup — only tears down if no active subscribers remain */
|
|
99
309
|
cleanup: () => void;
|
|
310
|
+
/** Force cleanup regardless of subscriber count */
|
|
311
|
+
forceCleanup: () => void;
|
|
312
|
+
/** Clear all current params and refetch */
|
|
313
|
+
clearParams: () => void;
|
|
100
314
|
}
|
|
101
315
|
interface PaginatedAsyncChunk<T, E extends Error = Error> extends AsyncChunk<T, E> {
|
|
102
|
-
/** Load next page */
|
|
316
|
+
/** Load the next page */
|
|
103
317
|
nextPage: () => Promise<void>;
|
|
104
|
-
/** Load previous page */
|
|
318
|
+
/** Load the previous page */
|
|
105
319
|
prevPage: () => Promise<void>;
|
|
106
|
-
/**
|
|
320
|
+
/** Jump to a specific page */
|
|
107
321
|
goToPage: (page: number) => Promise<void>;
|
|
108
|
-
/** Reset pagination to
|
|
322
|
+
/** Reset pagination to page 1 and re-fetch */
|
|
109
323
|
resetPagination: () => Promise<void>;
|
|
110
324
|
}
|
|
111
|
-
declare function asyncChunk<T, E extends Error = Error>(fetcher: () => Promise<T | FetcherResponse<T>>, options?:
|
|
325
|
+
declare function asyncChunk<T, E extends Error = Error>(fetcher: () => Promise<T | FetcherResponse<T>>, options?: AsyncChunkOptions<T, E>): AsyncChunk<T, E> | PaginatedAsyncChunk<T, E>;
|
|
112
326
|
declare function asyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(fetcher: (params: P & {
|
|
113
327
|
page?: number;
|
|
114
328
|
pageSize?: number;
|
|
115
|
-
}) => Promise<T | FetcherResponse<T>>, options?:
|
|
329
|
+
}) => Promise<T | FetcherResponse<T>>, options?: AsyncChunkOptions<T, E>): (AsyncChunk<T, E> | PaginatedAsyncChunk<T, E>) & {
|
|
116
330
|
setParams: (params: Partial<P>) => void;
|
|
117
331
|
reload: (params?: Partial<P>) => Promise<void>;
|
|
118
332
|
refresh: (params?: Partial<P>) => Promise<void>;
|
|
119
333
|
};
|
|
120
334
|
|
|
121
|
-
|
|
122
|
-
/**
|
|
335
|
+
type InfiniteAsyncChunkOptions<T, E extends Error = Error> = Omit<AsyncChunkOptions<T[], E>, 'pagination'> & {
|
|
336
|
+
/** Items per page (default: 10) */
|
|
123
337
|
pageSize?: number;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/** Error callback */
|
|
133
|
-
onError?: (error: E) => void;
|
|
134
|
-
}
|
|
338
|
+
};
|
|
339
|
+
type InfiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}> = PaginatedAsyncChunk<T[], E> & {
|
|
340
|
+
setParams: (params: Partial<Record<keyof P, P[keyof P] | null>>) => void;
|
|
341
|
+
clearParams: () => void;
|
|
342
|
+
reload: (params?: Partial<P>) => Promise<void>;
|
|
343
|
+
refresh: (params?: Partial<P>) => Promise<void>;
|
|
344
|
+
forceCleanup: () => void;
|
|
345
|
+
};
|
|
135
346
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
347
|
+
* Creates an infinite scroll async chunk that accumulates pages.
|
|
348
|
+
*
|
|
349
|
+
* A convenience wrapper around `asyncChunk` with `pagination.mode: 'accumulate'`
|
|
350
|
+
* pre-configured. Each `nextPage()` appends to the existing data array.
|
|
351
|
+
*
|
|
352
|
+
* @param fetcher - Async function receiving `{ page, pageSize, ...params }`,
|
|
353
|
+
* returning `{ data: T[], hasMore?, total? }`.
|
|
354
|
+
* @param options.pageSize - Items per page (default: 10).
|
|
355
|
+
* @param options.key - Deduplication key.
|
|
356
|
+
* @param options.onSuccess - Called with the full accumulated array after each fetch.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* const posts = infiniteAsyncChunk(
|
|
360
|
+
* async ({ page, pageSize }) => fetchPosts({ page, pageSize }),
|
|
361
|
+
* { pageSize: 20 }
|
|
362
|
+
* );
|
|
363
|
+
* posts.reload(); // page 1
|
|
364
|
+
* posts.nextPage(); // page 2 appended
|
|
138
365
|
*/
|
|
139
366
|
declare function infiniteAsyncChunk<T, E extends Error = Error, P extends Record<string, any> = {}>(fetcher: (params: P & {
|
|
140
367
|
page: number;
|
|
@@ -143,101 +370,208 @@ declare function infiniteAsyncChunk<T, E extends Error = Error, P extends Record
|
|
|
143
370
|
data: T[];
|
|
144
371
|
hasMore?: boolean;
|
|
145
372
|
total?: number;
|
|
146
|
-
}>, options?: InfiniteAsyncChunkOptions<T, E>):
|
|
147
|
-
setParams: (params: Partial<P>) => void;
|
|
148
|
-
reload: (params?: Partial<P>) => Promise<void>;
|
|
149
|
-
refresh: (params?: Partial<P>) => Promise<void>;
|
|
150
|
-
};
|
|
373
|
+
}>, options?: InfiniteAsyncChunkOptions<T, E>): InfiniteAsyncChunk<T, E, P>;
|
|
151
374
|
|
|
152
|
-
type
|
|
153
|
-
type
|
|
154
|
-
[K in keyof T]:
|
|
375
|
+
type InferAsyncData<T> = T extends AsyncChunk<infer U, Error> ? U : never;
|
|
376
|
+
type CombinedData<T extends Record<string, AsyncChunk<any>>> = {
|
|
377
|
+
[K in keyof T]: InferAsyncData<T[K]> | null;
|
|
378
|
+
};
|
|
379
|
+
type CombinedState<T extends Record<string, AsyncChunk<any>>> = {
|
|
380
|
+
loading: boolean;
|
|
381
|
+
error: Error | null;
|
|
382
|
+
errors: Partial<{
|
|
383
|
+
[K in keyof T]: Error;
|
|
384
|
+
}>;
|
|
385
|
+
data: CombinedData<T>;
|
|
155
386
|
};
|
|
156
|
-
interface Computed<T> extends Chunk<T> {
|
|
157
|
-
/**
|
|
158
|
-
* Checks if the computed value needs to be recalculated due to dependency changes.
|
|
159
|
-
* @returns True if the computed value is dirty, false otherwise.
|
|
160
|
-
*/
|
|
161
|
-
isDirty: () => boolean;
|
|
162
|
-
/** Manually forces recalculation of the computed value from its dependencies. */
|
|
163
|
-
recompute: () => void;
|
|
164
|
-
}
|
|
165
|
-
declare function computed<TDeps extends Chunk<any>[], TResult>(dependencies: [...TDeps], computeFn: (...args: DependencyValues<TDeps>) => TResult): Computed<TResult>;
|
|
166
|
-
|
|
167
|
-
interface SelectOptions {
|
|
168
|
-
/**
|
|
169
|
-
* Configuration options for selector functions.
|
|
170
|
-
* @property {boolean} [useShallowEqual] - When true, performs a shallow equality check
|
|
171
|
-
* on the derived selector results to prevent unnecessary updates.
|
|
172
|
-
*/
|
|
173
|
-
useShallowEqual?: boolean;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Creates a derived read-only chunk based on a selector function.
|
|
177
|
-
* @param sourceChunk The source chunk to derive from.
|
|
178
|
-
* @param selector A function that extracts part of the source value.
|
|
179
|
-
* @param options Optional settings for shallow equality comparison.
|
|
180
|
-
* @returns A read-only derived chunk.
|
|
181
|
-
*/
|
|
182
|
-
declare function select<T, S>(sourceChunk: Chunk<T>, selector: (value: T) => S, options?: SelectOptions): Chunk<S>;
|
|
183
387
|
|
|
184
|
-
declare function isValidChunkValue(value: unknown): boolean;
|
|
185
|
-
declare function isChunk<T>(value: unknown): value is Chunk<T>;
|
|
186
|
-
declare function once<T>(fn: () => T): () => T;
|
|
187
388
|
/**
|
|
188
|
-
* Combines multiple async chunks into a single chunk.
|
|
189
|
-
*
|
|
389
|
+
* Combines multiple async chunks into a single unified state chunk.
|
|
390
|
+
*
|
|
391
|
+
* The result tracks `loading` (true if any chunk is loading), `error` (first
|
|
392
|
+
* error encountered), `errors` (per-chunk errors), and `data` (per-chunk data).
|
|
393
|
+
*
|
|
394
|
+
* @param chunks - A record of named `AsyncChunk` instances.
|
|
395
|
+
* @returns A `Chunk<CombinedState<T>>` that reflects the live state of all inputs.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* const combined = combineAsyncChunks({ user: userChunk, posts: postsChunk });
|
|
399
|
+
* combined.get(); // { loading, error, errors, data: { user, posts } }
|
|
190
400
|
*/
|
|
191
401
|
declare function combineAsyncChunks<T extends Record<string, AsyncChunk<any>>>(chunks: T): Chunk<CombinedState<T>>;
|
|
192
402
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
declare const nonNegativeValidator: Middleware<number>;
|
|
196
|
-
|
|
197
|
-
interface ChunkWithHistory<T> extends Chunk<T> {
|
|
198
|
-
/**
|
|
199
|
-
* Reverts to the previous state (if available).
|
|
200
|
-
*/
|
|
201
|
-
undo: () => void;
|
|
202
|
-
/**
|
|
203
|
-
* Moves to the next state (if available).
|
|
204
|
-
*/
|
|
205
|
-
redo: () => void;
|
|
206
|
-
/**
|
|
207
|
-
* Returns true if there is a previous state to revert to.
|
|
208
|
-
*/
|
|
209
|
-
canUndo: () => boolean;
|
|
403
|
+
interface GlobalQueryConfig {
|
|
210
404
|
/**
|
|
211
|
-
*
|
|
405
|
+
* Default configuration applied to all asyncChunk instances.
|
|
406
|
+
* Per-chunk options always override these defaults.
|
|
212
407
|
*/
|
|
213
|
-
|
|
408
|
+
query?: {
|
|
409
|
+
/** Time in ms before data is considered stale (default: 0) */
|
|
410
|
+
staleTime?: number;
|
|
411
|
+
/** Time in ms to cache data after last subscriber leaves (default: 300_000) */
|
|
412
|
+
cacheTime?: number;
|
|
413
|
+
/** Auto-refetch interval in ms */
|
|
414
|
+
refetchInterval?: number;
|
|
415
|
+
/** Refetch when window regains focus (default: false) */
|
|
416
|
+
refetchOnWindowFocus?: boolean;
|
|
417
|
+
/** Number of retries on failure (default: 0) */
|
|
418
|
+
retryCount?: number;
|
|
419
|
+
/** Delay in ms between retries (default: 1000) */
|
|
420
|
+
retryDelay?: number;
|
|
421
|
+
/** Global error handler — called when all retries are exhausted */
|
|
422
|
+
onError?: (error: Error) => void;
|
|
423
|
+
/** Global success handler — called after every successful fetch */
|
|
424
|
+
onSuccess?: (data: unknown) => void;
|
|
425
|
+
};
|
|
214
426
|
/**
|
|
215
|
-
*
|
|
427
|
+
* Default configuration applied to all mutation instances.
|
|
428
|
+
* Reserved for when mutation() ships — per-mutation options always override.
|
|
216
429
|
*/
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
430
|
+
mutation?: {
|
|
431
|
+
/** Global error handler for mutations */
|
|
432
|
+
onError?: (error: Error) => void;
|
|
433
|
+
/** Global success handler for mutations */
|
|
434
|
+
onSuccess?: (data: unknown) => void;
|
|
435
|
+
};
|
|
222
436
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
437
|
+
/**
|
|
438
|
+
* Configures global defaults for all `asyncChunk` and `mutation` instances.
|
|
439
|
+
*
|
|
440
|
+
* Call this once at app entry — before any `asyncChunk` is created.
|
|
441
|
+
* Per-chunk options always take precedence over these defaults.
|
|
442
|
+
*
|
|
443
|
+
* @param config.query - Defaults for all async chunks (staleTime, retryCount, onError, etc.)
|
|
444
|
+
* @param config.mutation - Defaults for all mutations (onError, onSuccess)
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* import { configureQuery } from "stunk/query";
|
|
448
|
+
*
|
|
449
|
+
* configureQuery({
|
|
450
|
+
* query: {
|
|
451
|
+
* staleTime: 30_000,
|
|
452
|
+
* retryCount: 3,
|
|
453
|
+
* refetchOnWindowFocus: true,
|
|
454
|
+
* onError: (err) => toast.error(err.message),
|
|
455
|
+
* },
|
|
456
|
+
* mutation: {
|
|
457
|
+
* onError: (err) => toast.error(err.message),
|
|
458
|
+
* onSuccess: () => toast.success("Done!"),
|
|
459
|
+
* },
|
|
460
|
+
* });
|
|
461
|
+
*/
|
|
462
|
+
declare function configureQuery(config: GlobalQueryConfig): void;
|
|
463
|
+
/**
|
|
464
|
+
* Returns the current global query config.
|
|
465
|
+
* Used internally by asyncChunk and mutation to read defaults.
|
|
466
|
+
*/
|
|
467
|
+
declare function getGlobalQueryConfig(): GlobalQueryConfig;
|
|
468
|
+
/**
|
|
469
|
+
* Resets the global config back to defaults.
|
|
470
|
+
* Primarily useful in tests to avoid config bleed between test cases.
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* afterEach(() => resetQueryConfig());
|
|
474
|
+
*/
|
|
475
|
+
declare function resetQueryConfig(): void;
|
|
226
476
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
477
|
+
type MutationFn<TData, TVariables> = (variables: TVariables) => Promise<TData>;
|
|
478
|
+
interface MutationOptions<TData, TError extends Error = Error, TVariables = void> {
|
|
479
|
+
/** Chunks to automatically reload after a successful mutation */
|
|
480
|
+
invalidates?: AsyncChunk<any, any>[];
|
|
481
|
+
/** Called after a successful mutation with the returned data and original variables */
|
|
482
|
+
onSuccess?: (data: TData, variables: TVariables) => void;
|
|
483
|
+
/** Called when the mutation fails with the error and original variables */
|
|
484
|
+
onError?: (error: TError, variables: TVariables) => void;
|
|
485
|
+
/** Called after every attempt — success or failure — useful for unconditional cleanup */
|
|
486
|
+
onSettled?: (data: TData | null, error: TError | null, variables: TVariables) => void;
|
|
487
|
+
}
|
|
488
|
+
interface MutationState<TData, TError extends Error = Error> {
|
|
489
|
+
/** True while the mutation is in progress */
|
|
490
|
+
loading: boolean;
|
|
491
|
+
/** The data returned from the last successful mutation, or null */
|
|
492
|
+
data: TData | null;
|
|
493
|
+
/** The error from the last failed mutation, or null */
|
|
494
|
+
error: TError | null;
|
|
495
|
+
/** True after a successful mutation — distinct from data since data can be null on success */
|
|
496
|
+
isSuccess: boolean;
|
|
497
|
+
}
|
|
498
|
+
interface MutationResult<TData, TError extends Error = Error> {
|
|
499
|
+
/** The returned data on success, or null on failure */
|
|
500
|
+
data: TData | null;
|
|
501
|
+
/** The error on failure, or null on success */
|
|
502
|
+
error: TError | null;
|
|
503
|
+
}
|
|
504
|
+
interface Mutation<TData, TError extends Error = Error, TVariables = void> {
|
|
505
|
+
/**
|
|
506
|
+
* Execute the mutation. Always resolves — never throws.
|
|
507
|
+
* Returns `{ data, error }` so you can await it or fire and forget safely.
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* // Fire and forget — safe
|
|
511
|
+
* createPost.mutate({ title: 'Hello' });
|
|
512
|
+
*
|
|
513
|
+
* // Await for local UI control — no try/catch needed
|
|
514
|
+
* const { data, error } = await createPost.mutate({ title: 'Hello' });
|
|
515
|
+
* if (!error) router.push('/posts');
|
|
516
|
+
*/
|
|
517
|
+
mutate: (...args: TVariables extends void ? [] : [variables: TVariables]) => Promise<MutationResult<TData, TError>>;
|
|
518
|
+
/** Returns the current mutation state */
|
|
519
|
+
get: () => MutationState<TData, TError>;
|
|
520
|
+
/** Subscribe to state changes. Returns an unsubscribe function. */
|
|
521
|
+
subscribe: (callback: (state: MutationState<TData, TError>) => void) => () => void;
|
|
522
|
+
/** Reset state back to initial — clears data, error, isSuccess */
|
|
523
|
+
reset: () => void;
|
|
232
524
|
}
|
|
233
|
-
|
|
525
|
+
/**
|
|
526
|
+
* Creates a reactive mutation for POST, PUT, DELETE, or any async side effect.
|
|
527
|
+
*
|
|
528
|
+
* Always returns a promise that resolves — never throws.
|
|
529
|
+
* On success, automatically reloads any chunks listed in `invalidates`.
|
|
530
|
+
*
|
|
531
|
+
* @param mutationFn - Async function that performs the side effect.
|
|
532
|
+
* @param options.invalidates - Chunks to reload after a successful mutation.
|
|
533
|
+
* @param options.onSuccess - Called with data and variables on success.
|
|
534
|
+
* @param options.onError - Called with error and variables on failure.
|
|
535
|
+
* @param options.onSettled - Called after every attempt regardless of outcome.
|
|
536
|
+
*
|
|
537
|
+
* @example
|
|
538
|
+
* const createPost = mutation(
|
|
539
|
+
* async (data: NewPost) => fetchAPI('/posts', { method: 'POST', body: data }),
|
|
540
|
+
* {
|
|
541
|
+
* invalidates: [postsChunk],
|
|
542
|
+
* onSuccess: (data) => toast.success('Post created!'),
|
|
543
|
+
* onError: (err) => toast.error(err.message),
|
|
544
|
+
* }
|
|
545
|
+
* );
|
|
546
|
+
*
|
|
547
|
+
* // Fire and forget
|
|
548
|
+
* createPost.mutate({ title: 'Hello' });
|
|
549
|
+
*
|
|
550
|
+
* // Await for local control
|
|
551
|
+
* const { data, error } = await createPost.mutate({ title: 'Hello' });
|
|
552
|
+
* if (!error) router.push('/posts');
|
|
553
|
+
*/
|
|
554
|
+
declare function mutation<TData, TError extends Error = Error, TVariables = void>(mutationFn: MutationFn<TData, TVariables>, options?: MutationOptions<TData, TError, TVariables>): Mutation<TData, TError, TVariables>;
|
|
234
555
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
556
|
+
type index_AsyncChunk<T, E extends Error = Error> = AsyncChunk<T, E>;
|
|
557
|
+
type index_AsyncState<T, E extends Error> = AsyncState<T, E>;
|
|
558
|
+
type index_AsyncStateWithPagination<T, E extends Error> = AsyncStateWithPagination<T, E>;
|
|
559
|
+
type index_GlobalQueryConfig = GlobalQueryConfig;
|
|
560
|
+
type index_Mutation<TData, TError extends Error = Error, TVariables = void> = Mutation<TData, TError, TVariables>;
|
|
561
|
+
type index_MutationFn<TData, TVariables> = MutationFn<TData, TVariables>;
|
|
562
|
+
type index_MutationOptions<TData, TError extends Error = Error, TVariables = void> = MutationOptions<TData, TError, TVariables>;
|
|
563
|
+
type index_MutationResult<TData, TError extends Error = Error> = MutationResult<TData, TError>;
|
|
564
|
+
type index_MutationState<TData, TError extends Error = Error> = MutationState<TData, TError>;
|
|
565
|
+
type index_PaginatedAsyncChunk<T, E extends Error = Error> = PaginatedAsyncChunk<T, E>;
|
|
566
|
+
declare const index_asyncChunk: typeof asyncChunk;
|
|
567
|
+
declare const index_combineAsyncChunks: typeof combineAsyncChunks;
|
|
568
|
+
declare const index_configureQuery: typeof configureQuery;
|
|
569
|
+
declare const index_getGlobalQueryConfig: typeof getGlobalQueryConfig;
|
|
570
|
+
declare const index_infiniteAsyncChunk: typeof infiniteAsyncChunk;
|
|
571
|
+
declare const index_mutation: typeof mutation;
|
|
572
|
+
declare const index_resetQueryConfig: typeof resetQueryConfig;
|
|
239
573
|
declare namespace index {
|
|
240
|
-
export {
|
|
574
|
+
export { type index_AsyncChunk as AsyncChunk, type index_AsyncState as AsyncState, type index_AsyncStateWithPagination as AsyncStateWithPagination, type index_GlobalQueryConfig as GlobalQueryConfig, type index_Mutation as Mutation, type index_MutationFn as MutationFn, type index_MutationOptions as MutationOptions, type index_MutationResult as MutationResult, type index_MutationState as MutationState, type index_PaginatedAsyncChunk as PaginatedAsyncChunk, index_asyncChunk as asyncChunk, index_combineAsyncChunks as combineAsyncChunks, index_configureQuery as configureQuery, index_getGlobalQueryConfig as getGlobalQueryConfig, index_infiniteAsyncChunk as infiniteAsyncChunk, index_mutation as mutation, index_resetQueryConfig as resetQueryConfig };
|
|
241
575
|
}
|
|
242
576
|
|
|
243
|
-
export { type
|
|
577
|
+
export { type Chunk, type Middleware, batch, chunk, computed, getChunkMeta, isChunk, isValidChunkValue, index$1 as middleware, index as query, select };
|