UNPKG

@pinia/colada

Version:

The smart data fetching layer for Pinia

1,089 lines (1,071 loc) 44.1 kB
import * as vue from 'vue'; import { MaybeRefOrGetter, ComputedRef, ShallowRef, EffectScope, ComponentInternalInstance, Plugin } from 'vue'; import * as pinia from 'pinia'; import { Pinia } from 'pinia'; /** * Allows you to extend the default types of the library. * * @example * ```ts * // types-extension.d.ts * import '@pinia/colada' * export {} * declare module '@pinia/colada' { * interface TypesConfig { * defaultError: MyCustomError * } * } * ``` */ interface TypesConfig { } /** * The default error type used. * @internal */ type ErrorDefault = TypesConfig extends Record<'defaultError', infer E> ? E : Error; /** * The status of data. * - `pending`: initial state * - `error`: has an error * - `success`: has data */ type DataStateStatus = 'pending' | 'error' | 'success'; /** * Internal base type for data state. */ interface _DataState_Base<TResult, TError> { /** * The last successfully resolved data. */ data: TResult; /** * The last rejected error. */ error: TError; /** * The status of the data. * @see {@link DataStateStatus} */ status: DataStateStatus; } interface DataState_Success<TResult> extends _DataState_Base<TResult, null> { status: 'success'; } interface DataState_Error<TResult, TError, TDataInitial> extends _DataState_Base<TResult | TDataInitial, TError> { status: 'error'; } interface DataState_Pending<TDataInitial> extends _DataState_Base<TDataInitial, null> { status: 'pending'; } /** * Possible states for data based on its status. */ type DataState<TResult, TError, TDataInitial = undefined> = DataState_Success<TResult> | DataState_Error<TResult, TError, TDataInitial> | DataState_Pending<TDataInitial>; /** * The status of an async operation tied to pinia colada e.g. queries and mutations. * - `idle`: not loading * - `loading`: currently loading */ type AsyncStatus = 'idle' | 'loading'; /** * Possible values for `refetchOnMount`, `refetchOnWindowFocus`, and `refetchOnReconnect`. * `true` refetches if data is stale (calles `refresh()`), `false` never refetches, `'always'` always refetches. */ type RefetchOnControl = boolean | 'always'; /** * Options for queries that can be globally overridden. */ interface UseQueryOptionsGlobal { /** * Whether the query should be enabled or not. If `false`, the query will not be executed until `refetch()` or * `refresh()` is called. If it becomes `true`, the query will be refreshed. */ enabled?: MaybeRefOrGetter<boolean>; /** * Time in ms after which the data is considered stale and will be refreshed on next read. * @default 5000 (5 seconds) */ staleTime?: number; /** * Time in ms after which, once the data is no longer being used, it will be garbage collected to free resources. Set to `false` to disable garbage collection. * @default 300_000 (5 minutes) */ gcTime?: number | false; /** * Whether to refetch the query when the component is mounted. * @default true */ refetchOnMount?: MaybeRefOrGetter<RefetchOnControl>; /** * Whether to refetch the query when the window regains focus. * @default true */ refetchOnWindowFocus?: MaybeRefOrGetter<RefetchOnControl>; /** * Whether to refetch the query when the network reconnects. * @default true */ refetchOnReconnect?: MaybeRefOrGetter<RefetchOnControl>; /** * A placeholder data that is initially shown while the query is loading for the first time. This will also show the * `status` as `success` until the query finishes loading (no matter the outcome of the query). Note: unlike with * `initialData`, the placeholder does not change the cache state. */ placeholderData?: (previousData: unknown) => any; } /** * Context object passed to the `query` function of `useQuery()`. * @see {@link UseQueryOptions} */ interface UseQueryFnContext { /** * `AbortSignal` instance attached to the query call. If the call becomes outdated (e.g. due to a new call with the * same key), the signal will be aborted. */ signal: AbortSignal; } /** * Options for `useQuery()`. Can be extended by plugins. * * @example * ```ts * // use-query-plugin.d.ts * export {} // needed * declare module '@pinia/colada' { * interface UseQueryOptions { * // Whether to refresh the data when the component is mounted. * refreshOnMount?: boolean * } * } * ``` */ interface UseQueryOptions<TResult = unknown, TError = ErrorDefault, TDataInitial extends TResult | undefined = TResult | undefined> extends Pick<UseQueryOptionsGlobal, 'gcTime' | 'enabled' | 'refetchOnMount' | 'refetchOnReconnect' | 'refetchOnWindowFocus' | 'staleTime'> { /** * The key used to identify the query. Array of primitives **without** reactive values or a reactive array or getter. * It should be treaded as an array of dependencies of your queries, e.g. if you use the `route.params.id` property, * it should also be part of the key: * * ```ts * import { useRoute } from 'vue-router' * import { useQuery } from '@pinia/colada' * * const route = useRoute() * const { data } = useQuery({ * // pass a getter function (or computed, ref, etc.) to ensure reactivity * key: () => ['user', route.params.id], * query: () => fetchUser(route.params.id), * }) * ``` */ key: MaybeRefOrGetter<EntryKey>; /** * The function that will be called to fetch the data. It **must** be async. */ query: (context: UseQueryFnContext) => Promise<TResult>; /** * The data which is initially set to the query while the query is loading for the first time. * Note: unlike with `placeholderData`, setting the initial data changes the state of the query (it will be set to `success`). */ initialData?: () => TDataInitial; /** * A placeholder data that is initially shown while the query is loading for the first time. This will also show the * `status` as `success` until the query finishes loading (no matter the outcome of the query). Note: unlike with * `initialData`, the placeholder does not change the cache state. */ placeholderData?: NoInfer<TDataInitial> | NoInfer<TResult> | (<T extends TResult>(previousData: T | undefined) => NoInfer<TDataInitial> | NoInfer<TResult> | undefined); } /** * Default options for `useQuery()`. Modifying this object will affect all the queries that don't override these */ declare const USE_QUERY_DEFAULTS: { staleTime: number; gcTime: NonNullable<UseQueryOptions["gcTime"]>; refetchOnWindowFocus: NonNullable<UseQueryOptions["refetchOnWindowFocus"]>; refetchOnReconnect: NonNullable<UseQueryOptions["refetchOnReconnect"]>; refetchOnMount: NonNullable<UseQueryOptions["refetchOnMount"]>; enabled: MaybeRefOrGetter<boolean>; }; type UseQueryOptionsWithDefaults<TResult = unknown, TError = ErrorDefault, TDataInitial extends TResult | undefined = undefined> = UseQueryOptions<TResult, TError, TDataInitial> & typeof USE_QUERY_DEFAULTS; /** * Key type for nodes in the tree map. Differently from {@link EntryKey}, this type is serializable to JSON. */ type EntryNodeKey = string | number; /** * Internal data structure used to store the data of `useQuery()`. `T` should be serializable to JSON. * @internal */ declare class TreeMapNode<T = unknown> { value: T | undefined; children?: Map<EntryNodeKey, TreeMapNode<T>>; constructor(); constructor(keys: EntryNodeKey[], value: T | undefined); /** * Sets the value while building the tree * * @param keys - key as an array * @param value - value to set */ set(keys: EntryNodeKey[], value?: T): void; /** * Finds the node at the given path of keys. * * @param keys - path of keys */ find(keys: EntryNodeKey[]): TreeMapNode<T> | undefined; /** * Gets the value at the given path of keys. * * @param keys - path of keys */ get(keys: EntryNodeKey[]): T | undefined; /** * Delete the node at the given path of keys and all its children. * * @param keys - path of keys */ delete(keys: EntryNodeKey[]): void; /** * Iterates over the node values if not null or undefined and all its children. Goes in depth first order. Allows a `for (const of node)` loop. */ [Symbol.iterator](): IterableIterator<T>; } /** * Raw data of a query entry. Can be serialized from the server and used to hydrate the store. * @internal */ type _UseQueryEntryNodeValueSerialized<TResult = unknown, TError = unknown> = [ /** * The data returned by the query. */ data: TResult | undefined, /** * The error thrown by the query. */ error: TError | null, /** * When was this data fetched the last time in ms */ when?: number ]; /** * Serialized version of a query entry node. * @internal */ type UseQueryEntryNodeSerialized = [ key: EntryNodeKey, value: undefined | _UseQueryEntryNodeValueSerialized, children?: UseQueryEntryNodeSerialized[] ]; /** * Type that represents a value that can be an array or a single value. * @internal */ type _MaybeArray<T> = T | T[]; /** * Type that represents a value that can be a promise or a single value. * @internal */ type _Awaitable<T> = T | Promise<T>; type _JSONPrimitive = string | number | boolean | null | undefined; /** * Utility type to represent a flat object that can be stringified with `JSON.stringify` no matter the order of keys. * @internal */ interface _ObjectFlat { [key: string]: _JSONPrimitive | Array<_JSONPrimitive>; } /** * Creates a {@link QueryCache}'s `caches` key from an entry's {@link UseQueryOptions#key}. * @param key - key of the entry */ declare const toCacheKey: (key: EntryKey) => EntryNodeKey[]; /** * To avoid using `{}` * @internal */ interface _EmptyObject { } /** * Removes the `MaybeRefOrGetter` wrapper from all fields of an object. * @internal */ type _RemoveMaybeRef<T> = { [K in keyof T]: T[K] extends MaybeRefOrGetter<infer U> ? MaybeRefOrGetter<U> extends T[K] ? U : T[K] : T[K]; }; /** * Key used to identify a query or a mutation. Always an array. */ type EntryKey = Array<EntryNodeKey | _ObjectFlat>; /** * Options for mutations that can be globally overridden. */ interface UseMutationOptionsGlobal { /** * Runs before a mutation is executed. It can return a value that will be * passed to `mutation`, `onSuccess`, `onError` and `onSettled`. If it * returns a promise, it will be awaited before running `mutation`. */ onMutate?: ( /** * The variables passed to the mutation. */ vars: unknown) => _Awaitable<UseMutationGlobalContext | undefined | void | null>; /** * Runs when a mutation is successful. */ onSuccess?: ( /** * The result of the mutation. */ data: unknown, /** * The variables passed to the mutation. */ vars: unknown, /** * The merged context from `onMutate` and the global context. */ context: UseMutationGlobalContext) => unknown; /** * Runs when a mutation encounters an error. */ onError?: ( /** * The error thrown by the mutation. */ error: unknown, /** * The variables passed to the mutation. */ vars: unknown, /** * The merged context from `onMutate` and the global context. Properties returned by `onMutate` can be `undefined` * if `onMutate` throws. */ context: Partial<Record<keyof UseMutationGlobalContext, never>> | UseMutationGlobalContext) => unknown; /** * Runs after the mutation is settled, regardless of the result. */ onSettled?: ( /** * The result of the mutation. `undefined` when a mutation failed. */ data: unknown | undefined, /** * The error thrown by the mutation. `undefined` if the mutation was successful. */ error: unknown | undefined, /** * The variables passed to the mutation. */ vars: unknown, /** * The merged context from `onMutate` and the global context. Properties returned by `onMutate` can be `undefined` * if `onMutate` throws. */ context: Partial<Record<keyof UseMutationGlobalContext, never>> | UseMutationGlobalContext) => unknown; } /** * Options to create a mutation. */ interface UseMutationOptions<TResult = unknown, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> = _EmptyObject> { /** * The key of the mutation. If the mutation is successful, it will invalidate the mutation with the same key and refetch it */ mutation: (vars: TVars, context: _ReduceContext<NoInfer<TContext>>) => Promise<TResult>; /** * Optional key to identify the mutation globally and access it through other * helpers like `useMutationState()`. If you don't need to reference the * mutation elsewhere, you should ignore this option. */ key?: _MutationKey<NoInfer<TVars>>; /** * Runs before the mutation is executed. **It should be placed before `mutation()` for `context` to be inferred**. It * can return a value that will be passed to `mutation`, `onSuccess`, `onError` and `onSettled`. If it returns a * promise, it will be awaited before running `mutation`. * * @example * ```ts * useMutation({ * // must appear before `mutation` for `{ foo: string }` to be inferred * // within `mutation` * onMutate() { * return { foo: 'bar' } * }, * mutation: (id: number, { foo }) => { * console.log(foo) // bar * return fetch(`/api/todos/${id}`) * }, * onSuccess(context) { * console.log(context.foo) // bar * }, * }) * ``` */ onMutate?: ( /** * The variables passed to the mutation. */ vars: NoInfer<TVars>, context: UseMutationGlobalContext) => _Awaitable<TContext | undefined | void | null>; /** * Runs if the mutation is successful. */ onSuccess?: ( /** * The result of the mutation. */ data: NoInfer<TResult>, /** * The variables passed to the mutation. */ vars: NoInfer<TVars>, /** * The merged context from `onMutate` and the global context. */ context: UseMutationGlobalContext & _ReduceContext<NoInfer<TContext>>) => unknown; /** * Runs if the mutation encounters an error. */ onError?: ( /** * The error thrown by the mutation. */ error: TError, /** * The variables passed to the mutation. */ vars: NoInfer<TVars>, /** * The merged context from `onMutate` and the global context. Properties returned by `onMutate` can be `undefined` * if `onMutate` throws. */ context: (Partial<Record<keyof UseMutationGlobalContext, never>> & Partial<Record<keyof _ReduceContext<NoInfer<TContext>>, never>>) | (UseMutationGlobalContext & _ReduceContext<NoInfer<TContext>>)) => unknown; /** * Runs after the mutation is settled, regardless of the result. */ onSettled?: ( /** * The result of the mutation. `undefined` if the mutation failed. */ data: NoInfer<TResult> | undefined, /** * The error thrown by the mutation. `undefined` if the mutation was successful. */ error: TError | undefined, /** * The variables passed to the mutation. */ vars: NoInfer<TVars>, /** * The merged context from `onMutate` and the global context. Properties returned by `onMutate` can be `undefined` * if `onMutate` throws. */ context: (Partial<Record<keyof UseMutationGlobalContext, never>> & Partial<Record<keyof _ReduceContext<NoInfer<TContext>>, never>>) | (UseMutationGlobalContext & _ReduceContext<NoInfer<TContext>>)) => unknown; } /** * Valid keys for a mutation. Similar to query keys. * @see {@link EntryKey} * @internal */ type _MutationKey<TVars> = EntryKey | ((vars: TVars) => EntryKey); /** * Removes the nullish types from the context type to make `A & TContext` work instead of yield `never`. * @internal */ type _ReduceContext<TContext> = TContext extends void | null | undefined ? _EmptyObject : Record<any, any> extends TContext ? _EmptyObject : TContext; /** * Context object returned by a global `onMutate` function that is merged with the context returned by a local * `onMutate`. * @example * ```ts * declare module '@pinia/colada' { * export interface UseMutationGlobalContext { * router: Router // from vue-router * } * } * * // add the `router` to the context * app.use(MutationPlugin, { * onMutate() { * return { router } * }, * }) * ``` */ interface UseMutationGlobalContext { } interface UseMutationReturn<TResult, TVars, TError> { key?: EntryKey | ((vars: NoInfer<TVars>) => EntryKey); /** * The combined state of the mutation. Contains its data, error, and status. It enables type narrowing based on the {@link UseMutationReturn.status}. */ state: ComputedRef<DataState<TResult, TError>>; /** * The status of the mutation. * @see {@link DataStateStatus} */ status: ShallowRef<DataStateStatus>; /** * Status of the mutation. Becomes `'loading'` while the mutation is being fetched, is `'idle'` otherwise. */ asyncStatus: ShallowRef<AsyncStatus>; /** * The result of the mutation. `undefined` if the mutation has not been called yet. */ data: ShallowRef<TResult | undefined>; /** * The error of the mutation. `null` if the mutation has not been called yet or if it was successful. */ error: ShallowRef<TError | null>; /** * Whether the mutation is currently executing. */ isLoading: ComputedRef<boolean>; /** * The variables passed to the mutation. They are initially `undefined` and change every time the mutation is called. */ variables: ShallowRef<TVars | undefined>; /** * Calls the mutation and returns a promise with the result. * * @param vars - parameters to pass to the mutation */ mutateAsync: unknown | void extends TVars ? () => Promise<TResult> : (vars: TVars) => Promise<TResult>; /** * Calls the mutation without returning a promise to avoid unhandled promise rejections. * * @param args - parameters to pass to the mutation */ mutate: (...args: unknown | void extends TVars ? [] : [vars: TVars]) => void; /** * Resets the state of the mutation to its initial state. */ reset: () => void; } /** * Setups a mutation. * * @param options - Options to create the mutation * @example * ```ts * const queryCache = useQueryCache() * const { mutate, status, error } = useMutation({ * mutation: (id: number) => fetch(`/api/todos/${id}`), * onSuccess() { * queryCache.invalidateQueries('todos') * }, * }) * ``` */ declare function useMutation<TResult, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> = _EmptyObject>(options: UseMutationOptions<TResult, TVars, TError, TContext>): UseMutationReturn<TResult, TVars, TError>; /** * Define a mutation with the given options. Similar to `useMutation(options)` but allows you to reuse the mutation in * multiple places. * * @param options - the options to define the mutation * @example * ```ts * const useCreateTodo = defineMutation({ * mutation: (todoText: string) => * fetch('/api/todos', { * method: 'POST', * body: JSON.stringify({ text: todoText }), * }), * }) * ``` */ declare function defineMutation<TResult, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> = _EmptyObject>(options: UseMutationOptions<TResult, TVars, TError, TContext>): () => UseMutationReturn<TResult, TVars, TError>; /** * Define a mutation with a function setup. Allows to return arbitrary values from the mutation function, create * contextual refs, rename the returned values, etc. * * @param setup - a function to setup the mutation * @example * ```ts * const useCreateTodo = defineMutation(() => { * const todoText = ref('') * const { data, mutate, ...rest } = useMutation({ * mutation: () => * fetch('/api/todos', { * method: 'POST', * body: JSON.stringify({ text: todoText.value }), * }), * }) * // expose the todoText ref and rename other methods for convenience * return { ...rest, createTodo: mutate, todo: data, todoText } * }) * ``` */ declare function defineMutation<T>(setup: () => T): () => T; /** * Allows defining extensions to the query entry that are returned by `useQuery()`. */ interface UseQueryEntryExtensions<TResult, TError, TDataInitial extends TResult | undefined = TResult | undefined> { } /** * NOTE: Entries could be classes but the point of having all functions within the store is to allow plugins to hook * into actions. */ /** * A query entry in the cache. */ interface UseQueryEntry<TResult = unknown, TError = unknown, TDataInitial extends TResult | undefined = TResult | undefined> { /** * The state of the query. Contains the data, error and status. */ state: ShallowRef<DataState<TResult, TError, TDataInitial>>; /** * A placeholder `data` that is initially shown while the query is loading for the first time. This will also show the * `status` as `success` until the query finishes loading (no matter the outcome). */ placeholderData: TDataInitial | TResult | null | undefined; /** * The status of the query. */ asyncStatus: ShallowRef<AsyncStatus>; /** * When was this data set in the entry for the last time in ms. It can also * be 0 if the entry has been invalidated. */ when: number; /** * The serialized key associated with this query entry. */ key: EntryNodeKey[]; /** * Components and effects scopes that use this query entry. */ deps: Set<EffectScope | ComponentInternalInstance>; /** * Timeout id that scheduled a garbage collection. It is set here to clear it when the entry is used by a different component */ gcTimeout: ReturnType<typeof setTimeout> | undefined; /** * The current pending request. */ pending: null | { /** * The abort controller used to cancel the request and which `signal` is passed to the query function. */ abortController: AbortController; /** * The promise created by `queryCache.fetch` that is currently pending. */ refreshCall: Promise<DataState<TResult, TError, TDataInitial>>; /** * When was this `pending` object created. */ when: number; }; /** * Options used to create the query. They can be `null` during hydration but are needed for fetching. This is why * `store.ensure()` sets this property. Note these options might be shared by multiple query entries when the key is * dynamic and that's why some methods like {@link fetch} receive the options as an argument. */ options: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial> | null; /** * Whether the data is stale or not, requires `options.staleTime` to be set. */ readonly stale: boolean; /** * Whether the query is currently being used by a Component or EffectScope (e.g. a store). */ readonly active: boolean; /** * Extensions to the query entry added by plugins. */ ext: UseQueryEntryExtensions<TResult, TError, TDataInitial>; /** * Component `__hmrId` to track wrong usage of `useQuery` and warn the user. * @internal */ __hmr?: { id?: string; deps?: Set<EffectScope | ComponentInternalInstance>; skip?: boolean; }; } /** * Filter to get entries from the cache. */ interface UseQueryEntryFilter { /** * A key to filter the entries. */ key?: EntryKey; /** * If true, it will only match the exact key, not the children. * * @example * ```ts * { key: ['a'], exact: true } * // will match ['a'] but not ['a', 'b'], while * { key: ['a'] } * // will match both * ``` */ exact?: boolean; /** * If `true` or `false`, it will only return entries that match the stale status. If set to `null` or `undefined`, it matches both. * Requires `entry.options` to be set. */ stale?: boolean | null; /** * If `true` or `false`, it will only return entries that match the active status. If set to `null` or `undefined`, it matches both. */ active?: boolean | null; /** * If it has a non _nullish_ value, it only returns the entries with the given status. */ status?: DataStateStatus | null; /** * Pass a predicate to filter the entries. This will be executed for each entry matching the other filters. * @param entry - entry to filter */ predicate?: (entry: UseQueryEntry) => boolean; } /** * A query entry that is defined with {@link defineQuery}. * @internal */ type DefineQueryEntry = [entries: UseQueryEntry[], returnValue: unknown, effect: EffectScope]; /** * Composable to get the cache of the queries. As any other composable, it can be used inside the `setup` function of a * component, within another composable, or in injectable contexts like stores and navigation guards. */ declare const useQueryCache: pinia.StoreDefinition<"_pc_query", Pick<{ caches: vue.Ref<TreeMapNode<UseQueryEntry<unknown, unknown, unknown>>, TreeMapNode<UseQueryEntry<unknown, unknown, unknown>>>; ensureDefinedQuery: <T>(fn: () => T) => DefineQueryEntry; /** * Scope to track effects and components that use the query cache. * @internal */ _s: vue.Raw<EffectScope>; setQueryData: <TResult = unknown>(key: EntryKey, data: TResult | ((oldData: TResult | undefined) => TResult)) => void; getQueryData: <TResult = unknown>(key: EntryKey) => TResult | undefined; invalidateQueries: (filters?: UseQueryEntryFilter) => Promise<unknown>; cancelQueries: (filters?: UseQueryEntryFilter, reason?: unknown) => void; invalidate: (entry: UseQueryEntry) => void; fetch: <TResult, TError, TDataInitial extends TResult | undefined>(entry: UseQueryEntry<TResult, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial>) => Promise<DataState<TResult, TError, TDataInitial>>; refresh: <TResult, TError, TDataInitial extends TResult | undefined>(entry: UseQueryEntry<TResult, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial>) => Promise<DataState<TResult, TError, TDataInitial>>; ensure: <TResult = unknown, TError = Error, TDataInitial extends TResult | undefined = undefined>(opts: UseQueryOptions<TResult, TError, TDataInitial>, previousEntry?: UseQueryEntry<TResult, TError, TDataInitial>) => UseQueryEntry<TResult, TError, TDataInitial>; extend: <TResult = unknown, TError = Error>(_entry: UseQueryEntry<TResult, TError>) => void; track: (entry: UseQueryEntry, effect: EffectScope | ComponentInternalInstance | null | undefined) => void; untrack: (entry: UseQueryEntry, effect: EffectScope | ComponentInternalInstance | undefined | null) => void; cancel: (entry: UseQueryEntry, reason?: unknown) => void; create: <TResult, TError, TDataInitial extends TResult | undefined>(key: EntryNodeKey[], options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial> | null, initialData?: TDataInitial, error?: TError | null, when?: number) => UseQueryEntry<TResult, TError, TDataInitial>; remove: (entry: UseQueryEntry) => void; setEntryState: <TResult, TError>(entry: UseQueryEntry<TResult, TError>, state: DataState<NoInfer<TResult>, NoInfer<TError>>) => void; getEntries: (filters?: UseQueryEntryFilter) => UseQueryEntry[]; }, "caches" | "_s">, Pick<{ caches: vue.Ref<TreeMapNode<UseQueryEntry<unknown, unknown, unknown>>, TreeMapNode<UseQueryEntry<unknown, unknown, unknown>>>; ensureDefinedQuery: <T>(fn: () => T) => DefineQueryEntry; /** * Scope to track effects and components that use the query cache. * @internal */ _s: vue.Raw<EffectScope>; setQueryData: <TResult = unknown>(key: EntryKey, data: TResult | ((oldData: TResult | undefined) => TResult)) => void; getQueryData: <TResult = unknown>(key: EntryKey) => TResult | undefined; invalidateQueries: (filters?: UseQueryEntryFilter) => Promise<unknown>; cancelQueries: (filters?: UseQueryEntryFilter, reason?: unknown) => void; invalidate: (entry: UseQueryEntry) => void; fetch: <TResult, TError, TDataInitial extends TResult | undefined>(entry: UseQueryEntry<TResult, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial>) => Promise<DataState<TResult, TError, TDataInitial>>; refresh: <TResult, TError, TDataInitial extends TResult | undefined>(entry: UseQueryEntry<TResult, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial>) => Promise<DataState<TResult, TError, TDataInitial>>; ensure: <TResult = unknown, TError = Error, TDataInitial extends TResult | undefined = undefined>(opts: UseQueryOptions<TResult, TError, TDataInitial>, previousEntry?: UseQueryEntry<TResult, TError, TDataInitial>) => UseQueryEntry<TResult, TError, TDataInitial>; extend: <TResult = unknown, TError = Error>(_entry: UseQueryEntry<TResult, TError>) => void; track: (entry: UseQueryEntry, effect: EffectScope | ComponentInternalInstance | null | undefined) => void; untrack: (entry: UseQueryEntry, effect: EffectScope | ComponentInternalInstance | undefined | null) => void; cancel: (entry: UseQueryEntry, reason?: unknown) => void; create: <TResult, TError, TDataInitial extends TResult | undefined>(key: EntryNodeKey[], options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial> | null, initialData?: TDataInitial, error?: TError | null, when?: number) => UseQueryEntry<TResult, TError, TDataInitial>; remove: (entry: UseQueryEntry) => void; setEntryState: <TResult, TError>(entry: UseQueryEntry<TResult, TError>, state: DataState<NoInfer<TResult>, NoInfer<TError>>) => void; getEntries: (filters?: UseQueryEntryFilter) => UseQueryEntry[]; }, never>, Pick<{ caches: vue.Ref<TreeMapNode<UseQueryEntry<unknown, unknown, unknown>>, TreeMapNode<UseQueryEntry<unknown, unknown, unknown>>>; ensureDefinedQuery: <T>(fn: () => T) => DefineQueryEntry; /** * Scope to track effects and components that use the query cache. * @internal */ _s: vue.Raw<EffectScope>; setQueryData: <TResult = unknown>(key: EntryKey, data: TResult | ((oldData: TResult | undefined) => TResult)) => void; getQueryData: <TResult = unknown>(key: EntryKey) => TResult | undefined; invalidateQueries: (filters?: UseQueryEntryFilter) => Promise<unknown>; cancelQueries: (filters?: UseQueryEntryFilter, reason?: unknown) => void; invalidate: (entry: UseQueryEntry) => void; fetch: <TResult, TError, TDataInitial extends TResult | undefined>(entry: UseQueryEntry<TResult, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial>) => Promise<DataState<TResult, TError, TDataInitial>>; refresh: <TResult, TError, TDataInitial extends TResult | undefined>(entry: UseQueryEntry<TResult, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial>) => Promise<DataState<TResult, TError, TDataInitial>>; ensure: <TResult = unknown, TError = Error, TDataInitial extends TResult | undefined = undefined>(opts: UseQueryOptions<TResult, TError, TDataInitial>, previousEntry?: UseQueryEntry<TResult, TError, TDataInitial>) => UseQueryEntry<TResult, TError, TDataInitial>; extend: <TResult = unknown, TError = Error>(_entry: UseQueryEntry<TResult, TError>) => void; track: (entry: UseQueryEntry, effect: EffectScope | ComponentInternalInstance | null | undefined) => void; untrack: (entry: UseQueryEntry, effect: EffectScope | ComponentInternalInstance | undefined | null) => void; cancel: (entry: UseQueryEntry, reason?: unknown) => void; create: <TResult, TError, TDataInitial extends TResult | undefined>(key: EntryNodeKey[], options?: UseQueryOptionsWithDefaults<TResult, TError, TDataInitial> | null, initialData?: TDataInitial, error?: TError | null, when?: number) => UseQueryEntry<TResult, TError, TDataInitial>; remove: (entry: UseQueryEntry) => void; setEntryState: <TResult, TError>(entry: UseQueryEntry<TResult, TError>, state: DataState<NoInfer<TResult>, NoInfer<TError>>) => void; getEntries: (filters?: UseQueryEntryFilter) => UseQueryEntry[]; }, "ensureDefinedQuery" | "setQueryData" | "getQueryData" | "invalidateQueries" | "cancelQueries" | "invalidate" | "fetch" | "refresh" | "ensure" | "extend" | "track" | "untrack" | "cancel" | "create" | "remove" | "setEntryState" | "getEntries">>; /** * The cache of the queries. It's the store returned by {@link useQueryCache}. */ type QueryCache = ReturnType<typeof useQueryCache>; /** * Transform a tree into a compressed array. * @param root - root node of the tree * @returns Array representation of the tree */ declare function serializeTreeMap(root: TreeMapNode<UseQueryEntry>): UseQueryEntryNodeSerialized[]; /** * Hydrates the query cache with the serialized cache. Used during SSR. * @param queryCache - query cache * @param serializedCache - serialized cache */ declare function hydrateQueryCache(queryCache: QueryCache, serializedCache: UseQueryEntryNodeSerialized[]): void; /** * Serializes the query cache to a compressed array. Used during SSR. * @param queryCache - query cache */ declare function serializeQueryCache(queryCache: QueryCache): UseQueryEntryNodeSerialized[]; /** * Return type of `useQuery()`. */ interface UseQueryReturn<TResult = unknown, TError = ErrorDefault, TDataInitial extends TResult | undefined = undefined> extends UseQueryEntryExtensions<TResult, TError, TDataInitial> { /** * The state of the query. Contains its data, error, and status. */ state: ComputedRef<DataState<TResult, TError, TDataInitial>>; /** * Status of the query. Becomes `'loading'` while the query is being fetched, is `'idle'` otherwise. */ asyncStatus: ComputedRef<AsyncStatus>; /** * The last successful data resolved by the query. Alias for `state.value.data`. * * @see {@link state} */ data: ShallowRef<TResult | TDataInitial>; /** * The error rejected by the query. Alias for `state.value.error`. * * @see {@link state} */ error: ShallowRef<TError | null>; /** * The status of the query. Alias for `state.value.status`. * * @see {@link state} * @see {@link DataStateStatus} */ status: ShallowRef<DataStateStatus>; /** * Returns whether the request is still pending its first call. Alias for `status.value === 'pending'` */ isPending: ComputedRef<boolean>; /** * Returns whether the `data` is the `placeholderData`. */ isPlaceholderData: ComputedRef<boolean>; /** * Returns whether the request is currently fetching data. Alias for `asyncStatus.value === 'loading'` */ isLoading: ShallowRef<boolean>; /** * Ensures the current data is fresh. If the data is stale, refetch, if not return as is. * @param throwOnError - whether to throw an error if the refresh fails. Defaults to `false` * @returns a promise that resolves when the refresh is done */ refresh: (throwOnError?: boolean) => Promise<DataState<TResult, TError, TDataInitial>>; /** * Ignores fresh data and triggers a new fetch * @param throwOnError - whether to throw an error if the fetch fails. Defaults to `false` * @returns a promise that resolves when the fetch is done */ refetch: (throwOnError?: boolean) => Promise<DataState<TResult, TError, TDataInitial>>; } /** * Ensures and return a shared query state based on the `key` option. * * @param _options - The options of the query */ declare function useQuery<TResult, TError = ErrorDefault, TDataInitial extends TResult | undefined = undefined>(_options: UseQueryOptions<TResult, TError, TDataInitial>): UseQueryReturn<TResult, TError, TDataInitial>; /** * Options to define a query with `defineQuery()`. Similar to {@link UseQueryOptions} but disallows reactive values as * `defineQuery()` is used outside of an effect scope. */ interface DefineQueryOptions<TResult = unknown, TError = ErrorDefault> extends _RemoveMaybeRef<UseQueryOptions<TResult, TError>> { } /** * Define a query with the given options. Similar to `useQuery(options)` but allows you to reuse the query in multiple * places. It only allow static values in options. If you need dynamic values, use the function version. * * @param options - the options to define the query * @example * ```ts * const useTodoList = defineQuery({ * key: ['todos'], * query: () => fetch('/api/todos', { method: 'GET' }), * }) * ``` */ declare function defineQuery<TResult, TError = ErrorDefault>(options: DefineQueryOptions<TResult, TError>): () => UseQueryReturn<TResult, TError>; /** * Define a query with a setup function. Allows to return arbitrary values from the query function, create contextual * refs, rename the returned values, etc. The setup function will be called only once, like stores, and **must be * synchronous**. * * @param setup - a function to setup the query * @example * ```ts * const useFilteredTodos = defineQuery(() => { * const todoFilter = ref<'all' | 'finished' | 'unfinished'>('all') * const { data, ...rest } = useQuery({ * key: ['todos', { filter: todoFilter.value }], * query: () => * fetch(`/api/todos?filter=${todoFilter.value}`, { method: 'GET' }), * }) * // expose the todoFilter ref and rename data for convenience * return { ...rest, todoList: data, todoFilter } * }) * ``` */ declare function defineQuery<T>(setup: () => T): () => T; /** * Context passed to a Pinia Colada plugin. */ interface PiniaColadaPluginContext { /** * The query cache used by the application. */ queryCache: QueryCache; /** * The Pinia instance used by the application. */ pinia: Pinia; /** * An effect scope to collect effects. It should be used if you use any reactivity API like `ref()`, `watch()`, `computed()`, etc. * @see https://vuejs.org/api/reactivity-advanced.html#effectscope */ scope: EffectScope; } /** * A Pinia Colada plugin. */ interface PiniaColadaPlugin { (context: PiniaColadaPluginContext): void; } /** * Options for the Pinia Colada plugin. */ interface PiniaColadaOptions { /** * Pinia instance to use. This is only needed if installing before the Pinia plugin. */ pinia?: Pinia; /** * Pinia Colada plugins to install. */ plugins?: PiniaColadaPlugin[]; /** * Global options for queries. These will apply to all `useQuery()`, `defineQuery()`, etc. */ queryOptions?: UseQueryOptionsGlobal; /** * Global options for mutations. These will apply to all `useMutation()`, `defineMutation()`, etc. */ mutationOptions?: UseMutationOptionsGlobal; } /** * Plugin that installs the Query and Mutation plugins alongside some extra plugins. * * @see {@link QueryPlugin} to only install the Query plugin. * * @param app - Vue App * @param options - Pinia Colada options */ declare const PiniaColada: Plugin<PiniaColadaOptions>; /** * Options for {@link PiniaColadaQueryHooksPlugin}. */ interface PiniaColadaQueryHooksPluginOptions { /** * Global handler for when a query is successful. * * @param data - data returned by the query */ onSuccess?: <TResult = unknown>(data: TResult, entry: UseQueryEntry<TResult, unknown>) => unknown; /** * Global handler for when a query is settled (either successfully or with an error). Will await for the `onSuccess` * or `onError` handlers to resolve if they return a promise. * * @param data - data returned by the query if any * @param error - error thrown if any */ onSettled?: <TResult = unknown, TError = unknown>(data: TResult | undefined, error: TError | null, entry: UseQueryEntry<TResult, TError>) => unknown; /** * Global error handler for all queries. * * @param error - error thrown */ onError?: <TError = unknown>(error: TError, entry: UseQueryEntry<unknown, TError>) => unknown; } /** * Allows to add global hooks to all queries: * - `onSuccess`: called when a query is successful * - `onError`: called when a query throws an error * - `onSettled`: called when a query is settled (either successfully or with an error) * @param options - Pinia Colada Query Hooks plugin options * * @example * ```ts * import { PiniaColada, PiniaColadaQueryHooksPlugin } from '@pinia/colada' * * const app = createApp(App) * // app setup with other plugins * app.use(PiniaColada, { * plugins: [ * PiniaColadaQueryHooksPlugin({ * onError(error, entry) { * // ... * }, * }), * ], * }) * ``` */ declare function PiniaColadaQueryHooksPlugin(options: PiniaColadaQueryHooksPluginOptions): PiniaColadaPlugin; /** * Options for {@link useInfiniteQuery}. * * @experimental See https://github.com/posva/pinia-colada/issues/178 */ interface UseInfiniteQueryOptions<TResult, TError, TDataInitial extends TResult | undefined = TResult | undefined, TPages = unknown> extends Omit<UseQueryOptions<TResult, TError, TDataInitial>, 'query' | 'initialData' | 'placeholderData'> { /** * The function that will be called to fetch the data. It **must** be async. */ query: (pages: NoInfer<TPages>, context: UseQueryFnContext) => Promise<TResult>; initialPage: TPages | (() => TPages); merge: (result: NoInfer<TPages>, current: NoInfer<TResult>) => NoInfer<TPages>; } /** * Store and merge paginated data into a single cache entry. Allows to handle * infinite scrolling. This is an **experimental** API and is subject to * change. * * @param options - Options to configure the infinite query. * * @experimental See https://github.com/posva/pinia-colada/issues/178 */ declare function useInfiniteQuery<TResult, TError = ErrorDefault, TPage = unknown>(options: UseInfiniteQueryOptions<TResult, TError, TResult | undefined, TPage>): { loadMore: () => Promise<DataState<TPage, TError, TPage>>; state: vue.ComputedRef<DataState<TPage, TError, TPage>>; asyncStatus: vue.ComputedRef<AsyncStatus>; data: vue.ShallowRef<TPage>; error: vue.ShallowRef<TError>; status: vue.ShallowRef<DataStateStatus>; isPending: vue.ComputedRef<boolean>; isPlaceholderData: vue.ComputedRef<boolean>; isLoading: vue.ShallowRef<boolean>; }; export { type AsyncStatus, type DataState, type DataStateStatus, type DataState_Error, type DataState_Pending, type DataState_Success, type EntryKey, type EntryNodeKey, PiniaColada, type PiniaColadaOptions, type PiniaColadaPlugin, type PiniaColadaPluginContext, PiniaColadaQueryHooksPlugin, type PiniaColadaQueryHooksPluginOptions, type QueryCache, type RefetchOnControl, TreeMapNode, type TypesConfig, type UseInfiniteQueryOptions, type UseMutationOptions, type UseMutationReturn, type UseQueryEntry, type UseQueryEntryExtensions, type UseQueryEntryFilter, type UseQueryOptions, type UseQueryOptionsGlobal, type UseQueryOptionsWithDefaults, type UseQueryReturn, type _Awaitable, type _DataState_Base, type _EmptyObject, type _MaybeArray, type _ReduceContext, defineMutation, defineQuery, hydrateQueryCache, serializeQueryCache, serializeTreeMap, toCacheKey, useInfiniteQuery, useMutation, useQuery, useQueryCache };