@effector-tanstack-query/core 0.3.0 → 0.5.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/dist/createBaseQuery.cjs +23 -0
- package/dist/createBaseQuery.cjs.map +1 -1
- package/dist/createBaseQuery.d.cts +23 -1
- package/dist/createBaseQuery.d.ts +23 -1
- package/dist/createBaseQuery.js +23 -0
- package/dist/createBaseQuery.js.map +1 -1
- package/dist/createCacheAction.cjs +60 -0
- package/dist/createCacheAction.cjs.map +1 -0
- package/dist/createCacheAction.d.cts +66 -0
- package/dist/createCacheAction.d.ts +66 -0
- package/dist/createCacheAction.js +56 -0
- package/dist/createCacheAction.js.map +1 -0
- package/dist/createInfiniteQuery.cjs +2 -1
- package/dist/createInfiniteQuery.cjs.map +1 -1
- package/dist/createInfiniteQuery.js +2 -1
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createQueries.cjs +262 -0
- package/dist/createQueries.cjs.map +1 -0
- package/dist/createQueries.d.cts +32 -0
- package/dist/createQueries.d.ts +32 -0
- package/dist/createQueries.js +260 -0
- package/dist/createQueries.js.map +1 -0
- package/dist/createQuery.cjs +2 -1
- package/dist/createQuery.cjs.map +1 -1
- package/dist/createQuery.js +2 -1
- package/dist/createQuery.js.map +1 -1
- package/dist/index.cjs +18 -0
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/types.d.cts +140 -1
- package/dist/types.d.ts +140 -1
- package/package.json +4 -3
- package/src/createBaseQuery.ts +67 -1
- package/src/createCacheAction.ts +172 -0
- package/src/createInfiniteQuery.ts +1 -0
- package/src/createQueries.ts +442 -0
- package/src/createQuery.ts +1 -0
- package/src/index.ts +16 -0
- package/src/types.ts +175 -0
package/dist/types.d.cts
CHANGED
|
@@ -118,6 +118,30 @@ interface QueryResult<TData, TError = Error> {
|
|
|
118
118
|
* `$queryClient` and honors `fork({ values: [[$queryClient, qc]] })`.
|
|
119
119
|
*/
|
|
120
120
|
$queryClient: Store<QueryClient | null>;
|
|
121
|
+
/**
|
|
122
|
+
* Lifecycle events for `sample`-driven reactions to fetch completion.
|
|
123
|
+
*
|
|
124
|
+
* - `success` fires with the (post-`select`) data on every newly-finished
|
|
125
|
+
* successful fetch — fresh fetch, refetch, reactive key change, or a
|
|
126
|
+
* cross-scope `setQueryData`.
|
|
127
|
+
* - `failure` fires with the error on every failed fetch.
|
|
128
|
+
*
|
|
129
|
+
* Neither fires for the baseline state observed on `mounted()` (e.g.
|
|
130
|
+
* SSR-hydrated cache data) — the events track *new* fetches, not the
|
|
131
|
+
* initial observation. Each fork scope tracks its own baseline.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* sample({ clock: userQuery.finished.success, target: loadSettings })
|
|
135
|
+
* sample({
|
|
136
|
+
* clock: userQuery.finished.failure,
|
|
137
|
+
* fn: (err) => `Failed: ${err.message}`,
|
|
138
|
+
* target: showToast,
|
|
139
|
+
* })
|
|
140
|
+
*/
|
|
141
|
+
finished: {
|
|
142
|
+
success: Event<TData>;
|
|
143
|
+
failure: Event<TError>;
|
|
144
|
+
};
|
|
121
145
|
}
|
|
122
146
|
interface CreateInfiniteQueryOptions<TQueryFnData = unknown, TError = Error, TPageParam = unknown, TData = InfiniteData<TQueryFnData, TPageParam>, TQueryKey extends EffectorQueryKey = EffectorQueryKey> extends Omit<InfiniteQueryObserverOptions<TQueryFnData, TError, TData, ResolvedQueryKey<TQueryKey>, TPageParam>, 'queryKey' | 'enabled' | 'refetchInterval'> {
|
|
123
147
|
queryKey: TQueryKey;
|
|
@@ -158,6 +182,11 @@ interface InfiniteQueryResult<TData, TError = Error, TPageParam = unknown> {
|
|
|
158
182
|
$observer: Store<InfiniteQueryObserver<any, TError, TData, QueryKey, TPageParam> | null>;
|
|
159
183
|
/** See {@link QueryResult.$queryClient}. */
|
|
160
184
|
$queryClient: Store<QueryClient | null>;
|
|
185
|
+
/** See {@link QueryResult.finished}. */
|
|
186
|
+
finished: {
|
|
187
|
+
success: Event<TData>;
|
|
188
|
+
failure: Event<TError>;
|
|
189
|
+
};
|
|
161
190
|
}
|
|
162
191
|
type CreateMutationOptions<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> = MutationObserverOptions<TData, TError, TVariables, TOnMutateResult> & {
|
|
163
192
|
/** See {@link CreateQueryOptions.name}. */
|
|
@@ -229,5 +258,115 @@ interface MutationResult<TData = unknown, TError = Error, TVariables = void> {
|
|
|
229
258
|
}>;
|
|
230
259
|
};
|
|
231
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Per-item snapshot inside a `createQueries` family. Parallel to the
|
|
263
|
+
* factory's `source` array — one entry per item, in source order.
|
|
264
|
+
*/
|
|
265
|
+
interface QueryItemState<TItem, TData, TError = Error> {
|
|
266
|
+
/** The source item this entry was derived from. */
|
|
267
|
+
source: TItem;
|
|
268
|
+
data: TData | undefined;
|
|
269
|
+
error: TError | null;
|
|
270
|
+
status: QueryStatus;
|
|
271
|
+
isPending: boolean;
|
|
272
|
+
isFetching: boolean;
|
|
273
|
+
isSuccess: boolean;
|
|
274
|
+
isError: boolean;
|
|
275
|
+
isPlaceholderData: boolean;
|
|
276
|
+
fetchStatus: FetchStatus;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Per-item query options produced by `createQueries({ query })`. A pure
|
|
280
|
+
* function of one source item — no effector stores, no closures over
|
|
281
|
+
* mutable state. Reactivity comes from the source store; whenever it
|
|
282
|
+
* updates, this callback re-runs to compute fresh options.
|
|
283
|
+
*
|
|
284
|
+
* `enabled` is a plain boolean (not a `Store`) for the same reason —
|
|
285
|
+
* it's derived from the item, so reactivity is already covered.
|
|
286
|
+
*/
|
|
287
|
+
interface CreateQueriesItemOptions<TQueryFnData, TError, TData, TQueryKey extends ReadonlyArray<unknown>> extends Omit<QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>, 'queryKey' | 'enabled'> {
|
|
288
|
+
queryKey: TQueryKey;
|
|
289
|
+
enabled?: boolean;
|
|
290
|
+
}
|
|
291
|
+
interface CreateQueriesOptions<TItem, TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends ReadonlyArray<unknown> = ReadonlyArray<unknown>> {
|
|
292
|
+
/**
|
|
293
|
+
* Stable name used to derive the SID of the result `$items` store so
|
|
294
|
+
* `serialize(scope)` round-trips it for SSR. Without a name, the
|
|
295
|
+
* QueryClient cache still hydrates via `dehydrate`/`hydrate`, but the
|
|
296
|
+
* `$items` snapshot is silently dropped from `serialize(scope)`.
|
|
297
|
+
*/
|
|
298
|
+
name?: string;
|
|
299
|
+
/**
|
|
300
|
+
* Reactive list of items. Each item becomes one parallel query in the
|
|
301
|
+
* family. Adding / removing items updates the family (spawn / dispose
|
|
302
|
+
* observer). Order is preserved in `$items`.
|
|
303
|
+
*
|
|
304
|
+
* Duplicates in `source` deduplicate observers (same `queryKey` hash)
|
|
305
|
+
* but produce separate `$items` entries — one per occurrence.
|
|
306
|
+
*/
|
|
307
|
+
source: Store<ReadonlyArray<TItem>>;
|
|
308
|
+
/**
|
|
309
|
+
* Per-item options builder. MUST be pure — same item in, same options
|
|
310
|
+
* out. Reactivity is driven by the source store; this callback fires
|
|
311
|
+
* synchronously when source updates.
|
|
312
|
+
*/
|
|
313
|
+
query: (item: TItem) => CreateQueriesItemOptions<TQueryFnData, TError, TData, TQueryKey>;
|
|
314
|
+
/** Shared QueryObserver defaults applied on top of `query(item)`. */
|
|
315
|
+
staleTime?: number;
|
|
316
|
+
gcTime?: number;
|
|
317
|
+
retry?: QueryObserverOptions<TQueryFnData, TError, TData>['retry'];
|
|
318
|
+
retryDelay?: QueryObserverOptions<TQueryFnData, TError, TData>['retryDelay'];
|
|
319
|
+
refetchOnMount?: QueryObserverOptions<TQueryFnData, TError, TData>['refetchOnMount'];
|
|
320
|
+
refetchOnReconnect?: QueryObserverOptions<TQueryFnData, TError, TData>['refetchOnReconnect'];
|
|
321
|
+
refetchOnWindowFocus?: QueryObserverOptions<TQueryFnData, TError, TData>['refetchOnWindowFocus'];
|
|
322
|
+
networkMode?: QueryObserverOptions<TQueryFnData, TError, TData>['networkMode'];
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Result of `createQueries(...)`. A reactive "family" of parallel
|
|
326
|
+
* queries indexed by a source store. Composes naturally with
|
|
327
|
+
* `useUnit($items)` for non-Suspense usage and with
|
|
328
|
+
* `useQueries(family)` / `useSuspenseQueries(family)` for React-style
|
|
329
|
+
* consumption.
|
|
330
|
+
*/
|
|
331
|
+
interface QueriesResult<TItem, TData = unknown, TError = Error> {
|
|
332
|
+
/** Per-item snapshots, parallel to `source`. */
|
|
333
|
+
$items: Store<ReadonlyArray<QueryItemState<TItem, TData, TError>>>;
|
|
334
|
+
/** Just the `data` field of `$items` — shortcut for common UIs. */
|
|
335
|
+
$data: Store<ReadonlyArray<TData | undefined>>;
|
|
336
|
+
/** `true` while **any** query in the family is pending. */
|
|
337
|
+
$isPending: Store<boolean>;
|
|
338
|
+
/** `true` only when **every** query in the family has succeeded. */
|
|
339
|
+
$isSuccess: Store<boolean>;
|
|
340
|
+
/** `true` if **any** query in the family is currently fetching. */
|
|
341
|
+
$isFetching: Store<boolean>;
|
|
342
|
+
/** `true` if **any** query in the family errored. */
|
|
343
|
+
$isError: Store<boolean>;
|
|
344
|
+
/**
|
|
345
|
+
* Triggers a parallel `fetchQuery` for every current source item.
|
|
346
|
+
* SSR-friendly — `await allSettled(family.prefetch, { scope })`
|
|
347
|
+
* returns after every queryFn has resolved (or failed).
|
|
348
|
+
*/
|
|
349
|
+
prefetch: EventCallable<void>;
|
|
350
|
+
/**
|
|
351
|
+
* Increment the per-scope refcount and ensure every observer is
|
|
352
|
+
* subscribed to the QueryClient. Call from `useEffect` (or its
|
|
353
|
+
* effector equivalent). The matching `unmounted()` decrements; when
|
|
354
|
+
* the count hits zero, observers unsubscribe.
|
|
355
|
+
*/
|
|
356
|
+
mounted: EventCallable<void>;
|
|
357
|
+
unmounted: EventCallable<void>;
|
|
358
|
+
/** Invalidates every query in the family — re-fetches in background. */
|
|
359
|
+
refresh: EventCallable<void>;
|
|
360
|
+
/** Invalidates one specific item's query. */
|
|
361
|
+
refreshOne: EventCallable<TItem>;
|
|
362
|
+
/** See {@link QueryResult.$queryClient}. */
|
|
363
|
+
$queryClient: Store<QueryClient | null>;
|
|
364
|
+
/**
|
|
365
|
+
* Discriminator that lets `useQueries` / `useSuspenseQueries` and
|
|
366
|
+
* other helpers distinguish a family from a tuple of factories at
|
|
367
|
+
* runtime. Not part of the public API.
|
|
368
|
+
*/
|
|
369
|
+
readonly __family: true;
|
|
370
|
+
}
|
|
232
371
|
|
|
233
|
-
export type { CreateInfiniteQueryOptions, CreateMutationOptions, CreateQueryOptions, EffectorQueryKey, InfiniteQueryResult, MutationResult, MutationStatus, QueryResult, ResolveQueryKeyElement, ResolvedQueryKey, StoreOrValue };
|
|
372
|
+
export type { CreateInfiniteQueryOptions, CreateMutationOptions, CreateQueriesItemOptions, CreateQueriesOptions, CreateQueryOptions, EffectorQueryKey, InfiniteQueryResult, MutationResult, MutationStatus, QueriesResult, QueryItemState, QueryResult, ResolveQueryKeyElement, ResolvedQueryKey, StoreOrValue };
|
package/dist/types.d.ts
CHANGED
|
@@ -118,6 +118,30 @@ interface QueryResult<TData, TError = Error> {
|
|
|
118
118
|
* `$queryClient` and honors `fork({ values: [[$queryClient, qc]] })`.
|
|
119
119
|
*/
|
|
120
120
|
$queryClient: Store<QueryClient | null>;
|
|
121
|
+
/**
|
|
122
|
+
* Lifecycle events for `sample`-driven reactions to fetch completion.
|
|
123
|
+
*
|
|
124
|
+
* - `success` fires with the (post-`select`) data on every newly-finished
|
|
125
|
+
* successful fetch — fresh fetch, refetch, reactive key change, or a
|
|
126
|
+
* cross-scope `setQueryData`.
|
|
127
|
+
* - `failure` fires with the error on every failed fetch.
|
|
128
|
+
*
|
|
129
|
+
* Neither fires for the baseline state observed on `mounted()` (e.g.
|
|
130
|
+
* SSR-hydrated cache data) — the events track *new* fetches, not the
|
|
131
|
+
* initial observation. Each fork scope tracks its own baseline.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* sample({ clock: userQuery.finished.success, target: loadSettings })
|
|
135
|
+
* sample({
|
|
136
|
+
* clock: userQuery.finished.failure,
|
|
137
|
+
* fn: (err) => `Failed: ${err.message}`,
|
|
138
|
+
* target: showToast,
|
|
139
|
+
* })
|
|
140
|
+
*/
|
|
141
|
+
finished: {
|
|
142
|
+
success: Event<TData>;
|
|
143
|
+
failure: Event<TError>;
|
|
144
|
+
};
|
|
121
145
|
}
|
|
122
146
|
interface CreateInfiniteQueryOptions<TQueryFnData = unknown, TError = Error, TPageParam = unknown, TData = InfiniteData<TQueryFnData, TPageParam>, TQueryKey extends EffectorQueryKey = EffectorQueryKey> extends Omit<InfiniteQueryObserverOptions<TQueryFnData, TError, TData, ResolvedQueryKey<TQueryKey>, TPageParam>, 'queryKey' | 'enabled' | 'refetchInterval'> {
|
|
123
147
|
queryKey: TQueryKey;
|
|
@@ -158,6 +182,11 @@ interface InfiniteQueryResult<TData, TError = Error, TPageParam = unknown> {
|
|
|
158
182
|
$observer: Store<InfiniteQueryObserver<any, TError, TData, QueryKey, TPageParam> | null>;
|
|
159
183
|
/** See {@link QueryResult.$queryClient}. */
|
|
160
184
|
$queryClient: Store<QueryClient | null>;
|
|
185
|
+
/** See {@link QueryResult.finished}. */
|
|
186
|
+
finished: {
|
|
187
|
+
success: Event<TData>;
|
|
188
|
+
failure: Event<TError>;
|
|
189
|
+
};
|
|
161
190
|
}
|
|
162
191
|
type CreateMutationOptions<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> = MutationObserverOptions<TData, TError, TVariables, TOnMutateResult> & {
|
|
163
192
|
/** See {@link CreateQueryOptions.name}. */
|
|
@@ -229,5 +258,115 @@ interface MutationResult<TData = unknown, TError = Error, TVariables = void> {
|
|
|
229
258
|
}>;
|
|
230
259
|
};
|
|
231
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Per-item snapshot inside a `createQueries` family. Parallel to the
|
|
263
|
+
* factory's `source` array — one entry per item, in source order.
|
|
264
|
+
*/
|
|
265
|
+
interface QueryItemState<TItem, TData, TError = Error> {
|
|
266
|
+
/** The source item this entry was derived from. */
|
|
267
|
+
source: TItem;
|
|
268
|
+
data: TData | undefined;
|
|
269
|
+
error: TError | null;
|
|
270
|
+
status: QueryStatus;
|
|
271
|
+
isPending: boolean;
|
|
272
|
+
isFetching: boolean;
|
|
273
|
+
isSuccess: boolean;
|
|
274
|
+
isError: boolean;
|
|
275
|
+
isPlaceholderData: boolean;
|
|
276
|
+
fetchStatus: FetchStatus;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Per-item query options produced by `createQueries({ query })`. A pure
|
|
280
|
+
* function of one source item — no effector stores, no closures over
|
|
281
|
+
* mutable state. Reactivity comes from the source store; whenever it
|
|
282
|
+
* updates, this callback re-runs to compute fresh options.
|
|
283
|
+
*
|
|
284
|
+
* `enabled` is a plain boolean (not a `Store`) for the same reason —
|
|
285
|
+
* it's derived from the item, so reactivity is already covered.
|
|
286
|
+
*/
|
|
287
|
+
interface CreateQueriesItemOptions<TQueryFnData, TError, TData, TQueryKey extends ReadonlyArray<unknown>> extends Omit<QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>, 'queryKey' | 'enabled'> {
|
|
288
|
+
queryKey: TQueryKey;
|
|
289
|
+
enabled?: boolean;
|
|
290
|
+
}
|
|
291
|
+
interface CreateQueriesOptions<TItem, TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends ReadonlyArray<unknown> = ReadonlyArray<unknown>> {
|
|
292
|
+
/**
|
|
293
|
+
* Stable name used to derive the SID of the result `$items` store so
|
|
294
|
+
* `serialize(scope)` round-trips it for SSR. Without a name, the
|
|
295
|
+
* QueryClient cache still hydrates via `dehydrate`/`hydrate`, but the
|
|
296
|
+
* `$items` snapshot is silently dropped from `serialize(scope)`.
|
|
297
|
+
*/
|
|
298
|
+
name?: string;
|
|
299
|
+
/**
|
|
300
|
+
* Reactive list of items. Each item becomes one parallel query in the
|
|
301
|
+
* family. Adding / removing items updates the family (spawn / dispose
|
|
302
|
+
* observer). Order is preserved in `$items`.
|
|
303
|
+
*
|
|
304
|
+
* Duplicates in `source` deduplicate observers (same `queryKey` hash)
|
|
305
|
+
* but produce separate `$items` entries — one per occurrence.
|
|
306
|
+
*/
|
|
307
|
+
source: Store<ReadonlyArray<TItem>>;
|
|
308
|
+
/**
|
|
309
|
+
* Per-item options builder. MUST be pure — same item in, same options
|
|
310
|
+
* out. Reactivity is driven by the source store; this callback fires
|
|
311
|
+
* synchronously when source updates.
|
|
312
|
+
*/
|
|
313
|
+
query: (item: TItem) => CreateQueriesItemOptions<TQueryFnData, TError, TData, TQueryKey>;
|
|
314
|
+
/** Shared QueryObserver defaults applied on top of `query(item)`. */
|
|
315
|
+
staleTime?: number;
|
|
316
|
+
gcTime?: number;
|
|
317
|
+
retry?: QueryObserverOptions<TQueryFnData, TError, TData>['retry'];
|
|
318
|
+
retryDelay?: QueryObserverOptions<TQueryFnData, TError, TData>['retryDelay'];
|
|
319
|
+
refetchOnMount?: QueryObserverOptions<TQueryFnData, TError, TData>['refetchOnMount'];
|
|
320
|
+
refetchOnReconnect?: QueryObserverOptions<TQueryFnData, TError, TData>['refetchOnReconnect'];
|
|
321
|
+
refetchOnWindowFocus?: QueryObserverOptions<TQueryFnData, TError, TData>['refetchOnWindowFocus'];
|
|
322
|
+
networkMode?: QueryObserverOptions<TQueryFnData, TError, TData>['networkMode'];
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Result of `createQueries(...)`. A reactive "family" of parallel
|
|
326
|
+
* queries indexed by a source store. Composes naturally with
|
|
327
|
+
* `useUnit($items)` for non-Suspense usage and with
|
|
328
|
+
* `useQueries(family)` / `useSuspenseQueries(family)` for React-style
|
|
329
|
+
* consumption.
|
|
330
|
+
*/
|
|
331
|
+
interface QueriesResult<TItem, TData = unknown, TError = Error> {
|
|
332
|
+
/** Per-item snapshots, parallel to `source`. */
|
|
333
|
+
$items: Store<ReadonlyArray<QueryItemState<TItem, TData, TError>>>;
|
|
334
|
+
/** Just the `data` field of `$items` — shortcut for common UIs. */
|
|
335
|
+
$data: Store<ReadonlyArray<TData | undefined>>;
|
|
336
|
+
/** `true` while **any** query in the family is pending. */
|
|
337
|
+
$isPending: Store<boolean>;
|
|
338
|
+
/** `true` only when **every** query in the family has succeeded. */
|
|
339
|
+
$isSuccess: Store<boolean>;
|
|
340
|
+
/** `true` if **any** query in the family is currently fetching. */
|
|
341
|
+
$isFetching: Store<boolean>;
|
|
342
|
+
/** `true` if **any** query in the family errored. */
|
|
343
|
+
$isError: Store<boolean>;
|
|
344
|
+
/**
|
|
345
|
+
* Triggers a parallel `fetchQuery` for every current source item.
|
|
346
|
+
* SSR-friendly — `await allSettled(family.prefetch, { scope })`
|
|
347
|
+
* returns after every queryFn has resolved (or failed).
|
|
348
|
+
*/
|
|
349
|
+
prefetch: EventCallable<void>;
|
|
350
|
+
/**
|
|
351
|
+
* Increment the per-scope refcount and ensure every observer is
|
|
352
|
+
* subscribed to the QueryClient. Call from `useEffect` (or its
|
|
353
|
+
* effector equivalent). The matching `unmounted()` decrements; when
|
|
354
|
+
* the count hits zero, observers unsubscribe.
|
|
355
|
+
*/
|
|
356
|
+
mounted: EventCallable<void>;
|
|
357
|
+
unmounted: EventCallable<void>;
|
|
358
|
+
/** Invalidates every query in the family — re-fetches in background. */
|
|
359
|
+
refresh: EventCallable<void>;
|
|
360
|
+
/** Invalidates one specific item's query. */
|
|
361
|
+
refreshOne: EventCallable<TItem>;
|
|
362
|
+
/** See {@link QueryResult.$queryClient}. */
|
|
363
|
+
$queryClient: Store<QueryClient | null>;
|
|
364
|
+
/**
|
|
365
|
+
* Discriminator that lets `useQueries` / `useSuspenseQueries` and
|
|
366
|
+
* other helpers distinguish a family from a tuple of factories at
|
|
367
|
+
* runtime. Not part of the public API.
|
|
368
|
+
*/
|
|
369
|
+
readonly __family: true;
|
|
370
|
+
}
|
|
232
371
|
|
|
233
|
-
export type { CreateInfiniteQueryOptions, CreateMutationOptions, CreateQueryOptions, EffectorQueryKey, InfiniteQueryResult, MutationResult, MutationStatus, QueryResult, ResolveQueryKeyElement, ResolvedQueryKey, StoreOrValue };
|
|
372
|
+
export type { CreateInfiniteQueryOptions, CreateMutationOptions, CreateQueriesItemOptions, CreateQueriesOptions, CreateQueryOptions, EffectorQueryKey, InfiniteQueryResult, MutationResult, MutationStatus, QueriesResult, QueryItemState, QueryResult, ResolveQueryKeyElement, ResolvedQueryKey, StoreOrValue };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effector-tanstack-query/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Effector bindings for TanStack Query — core factories (createQuery, createMutation, createInfiniteQuery)",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Effector bindings for TanStack Query — core factories (createQuery, createMutation, createInfiniteQuery, createQueries) plus invalidate / cancel / remove / reset cache actions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ilya Agarkov <ilya.al.ag@gmail.com>",
|
|
7
7
|
"repository": {
|
|
@@ -43,10 +43,11 @@
|
|
|
43
43
|
"!src/__tests__"
|
|
44
44
|
],
|
|
45
45
|
"sideEffects": false,
|
|
46
|
-
"
|
|
46
|
+
"devDependencies": {
|
|
47
47
|
"@tanstack/query-core": "^5.100.10"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
+
"@tanstack/query-core": "^5.0.0",
|
|
50
51
|
"effector": ">=23.0.0"
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
package/src/createBaseQuery.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
sample,
|
|
7
7
|
scopeBind,
|
|
8
8
|
} from 'effector'
|
|
9
|
-
import type { EventCallable, Store } from 'effector'
|
|
9
|
+
import type { Event, EventCallable, Store } from 'effector'
|
|
10
10
|
import type {
|
|
11
11
|
FetchStatus,
|
|
12
12
|
QueryClient,
|
|
@@ -40,6 +40,17 @@ export interface BaseObserverResult<TData, TError> {
|
|
|
40
40
|
isFetching: boolean
|
|
41
41
|
fetchStatus: FetchStatus
|
|
42
42
|
isPlaceholderData: boolean
|
|
43
|
+
/**
|
|
44
|
+
* Timestamp (ms) of the last successful data resolution. Monotonically
|
|
45
|
+
* increases per successful fetch — used to detect newly-finished fetches
|
|
46
|
+
* for the `finished.success` lifecycle event.
|
|
47
|
+
*/
|
|
48
|
+
dataUpdatedAt: number
|
|
49
|
+
/**
|
|
50
|
+
* Timestamp (ms) of the last error. Increments per failed fetch — used to
|
|
51
|
+
* detect newly-finished failures for the `finished.failure` lifecycle event.
|
|
52
|
+
*/
|
|
53
|
+
errorUpdatedAt: number
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
export interface BaseQueryStores<TData, TError, TObserver> {
|
|
@@ -71,6 +82,17 @@ export interface BaseQueryStores<TData, TError, TObserver> {
|
|
|
71
82
|
refresh: EventCallable<void>
|
|
72
83
|
mounted: EventCallable<void>
|
|
73
84
|
unmounted: EventCallable<void>
|
|
85
|
+
/**
|
|
86
|
+
* Lifecycle events for `sample`-driven reactions to fetch completion.
|
|
87
|
+
* `success` fires with the (post-`select`) data on every newly-finished
|
|
88
|
+
* successful fetch; `failure` fires with the error on every failed fetch.
|
|
89
|
+
* Neither fires for the baseline state observed on mount (e.g. hydrated
|
|
90
|
+
* cache) — they track *new* fetches, not initial observability.
|
|
91
|
+
*/
|
|
92
|
+
finished: {
|
|
93
|
+
success: Event<TData>
|
|
94
|
+
failure: Event<TError>
|
|
95
|
+
}
|
|
74
96
|
}
|
|
75
97
|
|
|
76
98
|
export interface BaseQueryOptions {
|
|
@@ -186,6 +208,11 @@ export function createBaseQuery<
|
|
|
186
208
|
const fetchStatusUpdated = createEvent<FetchStatus>()
|
|
187
209
|
const isPlaceholderDataUpdated = createEvent<boolean>()
|
|
188
210
|
|
|
211
|
+
// Lifecycle events. Created once at factory time; dispatched per-scope via
|
|
212
|
+
// scopeBind inside the mount effect so `allSettled` / fork isolation work.
|
|
213
|
+
const finishedSuccess = createEvent<TData>()
|
|
214
|
+
const finishedFailure = createEvent<TError>()
|
|
215
|
+
|
|
189
216
|
const $data = createStore<TData | undefined>(undefined, {
|
|
190
217
|
skipVoid: false,
|
|
191
218
|
...sidConfig(name, '$data'),
|
|
@@ -268,8 +295,19 @@ export function createBaseQuery<
|
|
|
268
295
|
const dispatchIsPlaceholderData = scopeBind(isPlaceholderDataUpdated, {
|
|
269
296
|
safe: true,
|
|
270
297
|
})
|
|
298
|
+
const dispatchFinishedSuccess = scopeBind(finishedSuccess, { safe: true })
|
|
299
|
+
const dispatchFinishedFailure = scopeBind(finishedFailure, { safe: true })
|
|
271
300
|
const dispatchExtras = extras?.bindDispatcher()
|
|
272
301
|
|
|
302
|
+
// Per-mount, per-scope baseline for lifecycle events. The first
|
|
303
|
+
// notification (the immediate getCurrentResult() emit below, or the
|
|
304
|
+
// observer's first callback) establishes the baseline without firing —
|
|
305
|
+
// so hydrated cache data on mount doesn't dispatch `finished.success`.
|
|
306
|
+
// Subsequent increments of dataUpdatedAt / errorUpdatedAt are genuine
|
|
307
|
+
// new fetches and do fire.
|
|
308
|
+
let lastDataUpdatedAt = -1
|
|
309
|
+
let lastErrorUpdatedAt = -1
|
|
310
|
+
|
|
273
311
|
observerSubscriptions.get(observer)?.()
|
|
274
312
|
observer.setOptions({
|
|
275
313
|
...observer.options,
|
|
@@ -289,6 +327,30 @@ export function createBaseQuery<
|
|
|
289
327
|
dispatchFetchStatus(result.fetchStatus)
|
|
290
328
|
dispatchIsPlaceholderData(result.isPlaceholderData)
|
|
291
329
|
dispatchExtras?.(result)
|
|
330
|
+
|
|
331
|
+
if (lastDataUpdatedAt === -1) {
|
|
332
|
+
// Baseline — record current timestamps without emitting.
|
|
333
|
+
lastDataUpdatedAt = result.dataUpdatedAt
|
|
334
|
+
lastErrorUpdatedAt = result.errorUpdatedAt
|
|
335
|
+
} else {
|
|
336
|
+
// A newly-resolved successful fetch. Guard against placeholderData,
|
|
337
|
+
// which carries status 'success' but never advances dataUpdatedAt.
|
|
338
|
+
if (
|
|
339
|
+
result.dataUpdatedAt > lastDataUpdatedAt &&
|
|
340
|
+
result.status === 'success' &&
|
|
341
|
+
!result.isPlaceholderData
|
|
342
|
+
) {
|
|
343
|
+
lastDataUpdatedAt = result.dataUpdatedAt
|
|
344
|
+
dispatchFinishedSuccess(result.data as TData)
|
|
345
|
+
}
|
|
346
|
+
if (
|
|
347
|
+
result.errorUpdatedAt > lastErrorUpdatedAt &&
|
|
348
|
+
result.status === 'error'
|
|
349
|
+
) {
|
|
350
|
+
lastErrorUpdatedAt = result.errorUpdatedAt
|
|
351
|
+
dispatchFinishedFailure(result.error as TError)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
292
354
|
}
|
|
293
355
|
|
|
294
356
|
const unsubscribe = observer.subscribe(dispatch)
|
|
@@ -423,6 +485,10 @@ export function createBaseQuery<
|
|
|
423
485
|
refresh,
|
|
424
486
|
mounted,
|
|
425
487
|
unmounted,
|
|
488
|
+
finished: {
|
|
489
|
+
success: finishedSuccess,
|
|
490
|
+
failure: finishedFailure,
|
|
491
|
+
},
|
|
426
492
|
...(extras?.stores ?? ({} as TExtraStores)),
|
|
427
493
|
}
|
|
428
494
|
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { attach, createEvent, createStore, sample } from 'effector'
|
|
2
|
+
import type { EventCallable, Store } from 'effector'
|
|
3
|
+
import type { QueryClient, QueryFilters, QueryKey } from '@tanstack/query-core'
|
|
4
|
+
import { $queryClient } from './queryClient'
|
|
5
|
+
import { resolveKey } from './resolve'
|
|
6
|
+
import type { EffectorQueryKey } from './types'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Options shared by `createCancel` / `createRemove` / `createReset`.
|
|
10
|
+
*
|
|
11
|
+
* Structurally a `QueryFilters` (from `@tanstack/query-core`) with the
|
|
12
|
+
* `queryKey` field widened to the reactive `EffectorQueryKey` shape — drop a
|
|
13
|
+
* `Store` anywhere in the array and the action resolves it on every call.
|
|
14
|
+
*
|
|
15
|
+
* `queryKey` is **optional**: omit it to target *all* queries, mirroring
|
|
16
|
+
* `queryClient.cancelQueries()` / `removeQueries()` / `resetQueries()` with no
|
|
17
|
+
* filters.
|
|
18
|
+
*/
|
|
19
|
+
export interface CacheActionOptions extends Omit<QueryFilters, 'queryKey'> {
|
|
20
|
+
/**
|
|
21
|
+
* Key (or key prefix) to act on. Supports the same reactive shape as
|
|
22
|
+
* `createQuery.queryKey`. Omit to match every query in the cache.
|
|
23
|
+
*/
|
|
24
|
+
queryKey?: EffectorQueryKey
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Distinct named aliases so each factory documents its own option type. */
|
|
28
|
+
export type CreateCancelOptions = CacheActionOptions
|
|
29
|
+
export type CreateRemoveOptions = CacheActionOptions
|
|
30
|
+
export type CreateResetOptions = CacheActionOptions
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The QueryClient method a given action drives. Receives the resolved filters
|
|
34
|
+
* (with `queryKey` already merged in when present). May be sync (`removeQueries`
|
|
35
|
+
* returns `void`) or async (`cancelQueries` / `resetQueries` return a Promise).
|
|
36
|
+
*/
|
|
37
|
+
type CacheActionRunner = (qc: QueryClient, filters: QueryFilters) => unknown
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Shared builder. Returns a `void` event that, when fired, runs `action`
|
|
41
|
+
* against the resolved `QueryClient` with the resolved filters. Mirrors
|
|
42
|
+
* `createInvalidate`'s locking + reactive-key + per-scope semantics exactly.
|
|
43
|
+
*/
|
|
44
|
+
function buildCacheAction(
|
|
45
|
+
action: CacheActionRunner,
|
|
46
|
+
explicitClient: QueryClient | null,
|
|
47
|
+
options: CacheActionOptions,
|
|
48
|
+
): EventCallable<void> {
|
|
49
|
+
const { queryKey, ...restFilters } = options
|
|
50
|
+
|
|
51
|
+
// Same locking semantics as the other factories: explicit client freezes the
|
|
52
|
+
// store, default flows through global $queryClient (and respects
|
|
53
|
+
// fork({ values: [[$queryClient, qc]] }) for per-scope isolation).
|
|
54
|
+
const $effectiveClient: Store<QueryClient | null> = explicitClient
|
|
55
|
+
? createStore(explicitClient as QueryClient | null, {
|
|
56
|
+
serialize: 'ignore',
|
|
57
|
+
})
|
|
58
|
+
: $queryClient
|
|
59
|
+
|
|
60
|
+
// No queryKey → a store of `undefined`, so the effect omits the field and the
|
|
61
|
+
// action matches all queries.
|
|
62
|
+
const $resolvedKey: Store<QueryKey | undefined> = queryKey
|
|
63
|
+
? resolveKey(queryKey)
|
|
64
|
+
: createStore<QueryKey | undefined>(undefined, { skipVoid: false })
|
|
65
|
+
|
|
66
|
+
const run = createEvent<void>()
|
|
67
|
+
|
|
68
|
+
const runFx = attach({
|
|
69
|
+
source: { qc: $effectiveClient, key: $resolvedKey },
|
|
70
|
+
effect: ({ qc, key }) => {
|
|
71
|
+
if (!qc) return
|
|
72
|
+
const filters: QueryFilters = { ...restFilters }
|
|
73
|
+
if (key !== undefined) filters.queryKey = key
|
|
74
|
+
return action(qc, filters)
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
sample({ clock: run, target: runFx })
|
|
79
|
+
|
|
80
|
+
return run
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseArgs(
|
|
84
|
+
arg1: QueryClient | CacheActionOptions,
|
|
85
|
+
arg2?: CacheActionOptions,
|
|
86
|
+
): [QueryClient | null, CacheActionOptions] {
|
|
87
|
+
if (arg2 !== undefined) return [arg1 as QueryClient, arg2]
|
|
88
|
+
return [null, arg1 as CacheActionOptions]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Builds a `sample`-friendly event that **cancels** in-flight queries on the
|
|
93
|
+
* resolved `QueryClient` (without removing cached data). Wraps
|
|
94
|
+
* `queryClient.cancelQueries` — async, so `await allSettled(cancel, { scope })`
|
|
95
|
+
* waits for cancellation to settle.
|
|
96
|
+
*
|
|
97
|
+
* @example Cancel a user's queries on logout
|
|
98
|
+
* const cancelUserQueries = createCancel({ queryKey: ['user', $userId] })
|
|
99
|
+
* sample({ clock: logoutClicked, target: cancelUserQueries })
|
|
100
|
+
*
|
|
101
|
+
* @example Explicit client (same back-compat overload as createInvalidate)
|
|
102
|
+
* const cancelAll = createCancel(queryClient, {})
|
|
103
|
+
*/
|
|
104
|
+
export function createCancel(options: CacheActionOptions): EventCallable<void>
|
|
105
|
+
export function createCancel(
|
|
106
|
+
queryClient: QueryClient,
|
|
107
|
+
options: CacheActionOptions,
|
|
108
|
+
): EventCallable<void>
|
|
109
|
+
export function createCancel(
|
|
110
|
+
arg1: QueryClient | CacheActionOptions,
|
|
111
|
+
arg2?: CacheActionOptions,
|
|
112
|
+
): EventCallable<void> {
|
|
113
|
+
const [explicitClient, options] = parseArgs(arg1, arg2)
|
|
114
|
+
return buildCacheAction(
|
|
115
|
+
(qc, filters) => qc.cancelQueries(filters),
|
|
116
|
+
explicitClient,
|
|
117
|
+
options,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Builds a `sample`-friendly event that **removes** matching entries from the
|
|
123
|
+
* cache entirely. Wraps `queryClient.removeQueries` — synchronous (no Promise).
|
|
124
|
+
* The next mount of a removed query starts from a pending state.
|
|
125
|
+
*
|
|
126
|
+
* @example Drop stale cache on navigation
|
|
127
|
+
* const removeStale = createRemove({ queryKey: ['stale-cache'] })
|
|
128
|
+
* sample({ clock: navigated, target: removeStale })
|
|
129
|
+
*/
|
|
130
|
+
export function createRemove(options: CacheActionOptions): EventCallable<void>
|
|
131
|
+
export function createRemove(
|
|
132
|
+
queryClient: QueryClient,
|
|
133
|
+
options: CacheActionOptions,
|
|
134
|
+
): EventCallable<void>
|
|
135
|
+
export function createRemove(
|
|
136
|
+
arg1: QueryClient | CacheActionOptions,
|
|
137
|
+
arg2?: CacheActionOptions,
|
|
138
|
+
): EventCallable<void> {
|
|
139
|
+
const [explicitClient, options] = parseArgs(arg1, arg2)
|
|
140
|
+
return buildCacheAction(
|
|
141
|
+
(qc, filters) => qc.removeQueries(filters),
|
|
142
|
+
explicitClient,
|
|
143
|
+
options,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Builds a `sample`-friendly event that **resets** matching queries back to
|
|
149
|
+
* their initial state and refetches any active observers. Wraps
|
|
150
|
+
* `queryClient.resetQueries` — async, so `await allSettled(reset, { scope })`
|
|
151
|
+
* waits for the refetch to settle.
|
|
152
|
+
*
|
|
153
|
+
* @example Reset search results when the filters are cleared
|
|
154
|
+
* const resetSearch = createReset({ queryKey: ['search'] })
|
|
155
|
+
* sample({ clock: filtersCleared, target: resetSearch })
|
|
156
|
+
*/
|
|
157
|
+
export function createReset(options: CacheActionOptions): EventCallable<void>
|
|
158
|
+
export function createReset(
|
|
159
|
+
queryClient: QueryClient,
|
|
160
|
+
options: CacheActionOptions,
|
|
161
|
+
): EventCallable<void>
|
|
162
|
+
export function createReset(
|
|
163
|
+
arg1: QueryClient | CacheActionOptions,
|
|
164
|
+
arg2?: CacheActionOptions,
|
|
165
|
+
): EventCallable<void> {
|
|
166
|
+
const [explicitClient, options] = parseArgs(arg1, arg2)
|
|
167
|
+
return buildCacheAction(
|
|
168
|
+
(qc, filters) => qc.resetQueries(filters),
|
|
169
|
+
explicitClient,
|
|
170
|
+
options,
|
|
171
|
+
)
|
|
172
|
+
}
|