@effector-tanstack-query/core 0.2.0 → 0.4.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/src/types.ts CHANGED
@@ -28,15 +28,48 @@ export type EffectorQueryKey = ReadonlyArray<
28
28
  StoreOrValue<string | number | bigint | boolean | null | undefined | object>
29
29
  >
30
30
 
31
+ /**
32
+ * Resolves a single `EffectorQueryKey` element to its runtime value type:
33
+ * `Store<T>` → `T`, everything else is left as-is. Used by `ResolvedQueryKey`
34
+ * so `queryFn`'s context can present `queryKey` with stores already unwrapped.
35
+ */
36
+ export type ResolveQueryKeyElement<T> = T extends Store<infer U> ? U : T
37
+
38
+ /**
39
+ * Tuple-aware mapping that walks an `EffectorQueryKey` and replaces each
40
+ * `Store<T>` with `T`, preserving tuple shape (length, readonly-ness, element
41
+ * positions). Drives the `TQueryKey` generic of `CreateQueryOptions` so the
42
+ * `queryFn({ queryKey })` parameter is typed with the resolved values:
43
+ *
44
+ * ```ts
45
+ * const $name = createStore<string>('pikachu')
46
+ * createQuery({
47
+ * queryKey: ['pokemon', $name],
48
+ * queryFn: ({ queryKey }) => fetchByName(queryKey[1]),
49
+ * // ^ string — no cast needed
50
+ * })
51
+ * ```
52
+ */
53
+ export type ResolvedQueryKey<T extends ReadonlyArray<unknown>> = {
54
+ readonly [K in keyof T]: ResolveQueryKeyElement<T[K]>
55
+ }
56
+
31
57
  export interface CreateQueryOptions<
32
58
  TQueryFnData = unknown,
33
59
  TError = Error,
34
60
  TData = TQueryFnData,
61
+ TQueryKey extends EffectorQueryKey = EffectorQueryKey,
35
62
  > extends Omit<
36
- QueryObserverOptions<TQueryFnData, TError, TData>,
63
+ QueryObserverOptions<
64
+ TQueryFnData,
65
+ TError,
66
+ TData,
67
+ TQueryFnData,
68
+ ResolvedQueryKey<TQueryKey>
69
+ >,
37
70
  'queryKey' | 'enabled' | 'refetchInterval'
38
71
  > {
39
- queryKey: EffectorQueryKey
72
+ queryKey: TQueryKey
40
73
  enabled?: StoreOrValue<boolean>
41
74
  /**
42
75
  * Polling interval in milliseconds, `false` to disable, or a Store for
@@ -46,7 +79,13 @@ export interface CreateQueryOptions<
46
79
  * from TanStack Query is also still supported.
47
80
  */
48
81
  refetchInterval?:
49
- | QueryObserverOptions<TQueryFnData, TError, TData>['refetchInterval']
82
+ | QueryObserverOptions<
83
+ TQueryFnData,
84
+ TError,
85
+ TData,
86
+ TQueryFnData,
87
+ ResolvedQueryKey<TQueryKey>
88
+ >['refetchInterval']
50
89
  | Store<number | false | undefined>
51
90
  /**
52
91
  * Stable name used to derive SIDs for the internal effector stores so that
@@ -128,17 +167,18 @@ export interface CreateInfiniteQueryOptions<
128
167
  TError = Error,
129
168
  TPageParam = unknown,
130
169
  TData = InfiniteData<TQueryFnData, TPageParam>,
170
+ TQueryKey extends EffectorQueryKey = EffectorQueryKey,
131
171
  > extends Omit<
132
172
  InfiniteQueryObserverOptions<
133
173
  TQueryFnData,
134
174
  TError,
135
175
  TData,
136
- ReadonlyArray<unknown>,
176
+ ResolvedQueryKey<TQueryKey>,
137
177
  TPageParam
138
178
  >,
139
179
  'queryKey' | 'enabled' | 'refetchInterval'
140
180
  > {
141
- queryKey: EffectorQueryKey
181
+ queryKey: TQueryKey
142
182
  enabled?: StoreOrValue<boolean>
143
183
  /** See {@link CreateQueryOptions.refetchInterval}. */
144
184
  refetchInterval?:
@@ -146,7 +186,7 @@ export interface CreateInfiniteQueryOptions<
146
186
  TQueryFnData,
147
187
  TError,
148
188
  TData,
149
- ReadonlyArray<unknown>,
189
+ ResolvedQueryKey<TQueryKey>,
150
190
  TPageParam
151
191
  >['refetchInterval']
152
192
  | Store<number | false | undefined>
@@ -268,3 +308,149 @@ export interface MutationResult<
268
308
  failure: Event<{ params: TVariables; error: TError }>
269
309
  }
270
310
  }
311
+
312
+ /**
313
+ * Per-item snapshot inside a `createQueries` family. Parallel to the
314
+ * factory's `source` array — one entry per item, in source order.
315
+ */
316
+ export interface QueryItemState<TItem, TData, TError = Error> {
317
+ /** The source item this entry was derived from. */
318
+ source: TItem
319
+ data: TData | undefined
320
+ error: TError | null
321
+ status: QueryStatus
322
+ isPending: boolean
323
+ isFetching: boolean
324
+ isSuccess: boolean
325
+ isError: boolean
326
+ isPlaceholderData: boolean
327
+ fetchStatus: FetchStatus
328
+ }
329
+
330
+ /**
331
+ * Per-item query options produced by `createQueries({ query })`. A pure
332
+ * function of one source item — no effector stores, no closures over
333
+ * mutable state. Reactivity comes from the source store; whenever it
334
+ * updates, this callback re-runs to compute fresh options.
335
+ *
336
+ * `enabled` is a plain boolean (not a `Store`) for the same reason —
337
+ * it's derived from the item, so reactivity is already covered.
338
+ */
339
+ export interface CreateQueriesItemOptions<
340
+ TQueryFnData,
341
+ TError,
342
+ TData,
343
+ TQueryKey extends ReadonlyArray<unknown>,
344
+ > extends Omit<
345
+ QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
346
+ 'queryKey' | 'enabled'
347
+ > {
348
+ queryKey: TQueryKey
349
+ enabled?: boolean
350
+ }
351
+
352
+ export interface CreateQueriesOptions<
353
+ TItem,
354
+ TQueryFnData = unknown,
355
+ TError = Error,
356
+ TData = TQueryFnData,
357
+ TQueryKey extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
358
+ > {
359
+ /**
360
+ * Stable name used to derive the SID of the result `$items` store so
361
+ * `serialize(scope)` round-trips it for SSR. Without a name, the
362
+ * QueryClient cache still hydrates via `dehydrate`/`hydrate`, but the
363
+ * `$items` snapshot is silently dropped from `serialize(scope)`.
364
+ */
365
+ name?: string
366
+ /**
367
+ * Reactive list of items. Each item becomes one parallel query in the
368
+ * family. Adding / removing items updates the family (spawn / dispose
369
+ * observer). Order is preserved in `$items`.
370
+ *
371
+ * Duplicates in `source` deduplicate observers (same `queryKey` hash)
372
+ * but produce separate `$items` entries — one per occurrence.
373
+ */
374
+ source: Store<ReadonlyArray<TItem>>
375
+ /**
376
+ * Per-item options builder. MUST be pure — same item in, same options
377
+ * out. Reactivity is driven by the source store; this callback fires
378
+ * synchronously when source updates.
379
+ */
380
+ query: (
381
+ item: TItem,
382
+ ) => CreateQueriesItemOptions<TQueryFnData, TError, TData, TQueryKey>
383
+ /** Shared QueryObserver defaults applied on top of `query(item)`. */
384
+ staleTime?: number
385
+ gcTime?: number
386
+ retry?: QueryObserverOptions<TQueryFnData, TError, TData>['retry']
387
+ retryDelay?: QueryObserverOptions<TQueryFnData, TError, TData>['retryDelay']
388
+ refetchOnMount?: QueryObserverOptions<
389
+ TQueryFnData,
390
+ TError,
391
+ TData
392
+ >['refetchOnMount']
393
+ refetchOnReconnect?: QueryObserverOptions<
394
+ TQueryFnData,
395
+ TError,
396
+ TData
397
+ >['refetchOnReconnect']
398
+ refetchOnWindowFocus?: QueryObserverOptions<
399
+ TQueryFnData,
400
+ TError,
401
+ TData
402
+ >['refetchOnWindowFocus']
403
+ networkMode?: QueryObserverOptions<
404
+ TQueryFnData,
405
+ TError,
406
+ TData
407
+ >['networkMode']
408
+ }
409
+
410
+ /**
411
+ * Result of `createQueries(...)`. A reactive "family" of parallel
412
+ * queries indexed by a source store. Composes naturally with
413
+ * `useUnit($items)` for non-Suspense usage and with
414
+ * `useQueries(family)` / `useSuspenseQueries(family)` for React-style
415
+ * consumption.
416
+ */
417
+ export interface QueriesResult<TItem, TData = unknown, TError = Error> {
418
+ /** Per-item snapshots, parallel to `source`. */
419
+ $items: Store<ReadonlyArray<QueryItemState<TItem, TData, TError>>>
420
+ /** Just the `data` field of `$items` — shortcut for common UIs. */
421
+ $data: Store<ReadonlyArray<TData | undefined>>
422
+ /** `true` while **any** query in the family is pending. */
423
+ $isPending: Store<boolean>
424
+ /** `true` only when **every** query in the family has succeeded. */
425
+ $isSuccess: Store<boolean>
426
+ /** `true` if **any** query in the family is currently fetching. */
427
+ $isFetching: Store<boolean>
428
+ /** `true` if **any** query in the family errored. */
429
+ $isError: Store<boolean>
430
+ /**
431
+ * Triggers a parallel `fetchQuery` for every current source item.
432
+ * SSR-friendly — `await allSettled(family.prefetch, { scope })`
433
+ * returns after every queryFn has resolved (or failed).
434
+ */
435
+ prefetch: EventCallable<void>
436
+ /**
437
+ * Increment the per-scope refcount and ensure every observer is
438
+ * subscribed to the QueryClient. Call from `useEffect` (or its
439
+ * effector equivalent). The matching `unmounted()` decrements; when
440
+ * the count hits zero, observers unsubscribe.
441
+ */
442
+ mounted: EventCallable<void>
443
+ unmounted: EventCallable<void>
444
+ /** Invalidates every query in the family — re-fetches in background. */
445
+ refresh: EventCallable<void>
446
+ /** Invalidates one specific item's query. */
447
+ refreshOne: EventCallable<TItem>
448
+ /** See {@link QueryResult.$queryClient}. */
449
+ $queryClient: Store<QueryClient | null>
450
+ /**
451
+ * Discriminator that lets `useQueries` / `useSuspenseQueries` and
452
+ * other helpers distinguish a family from a tuple of factories at
453
+ * runtime. Not part of the public API.
454
+ */
455
+ readonly __family: true
456
+ }