UNPKG

@pinia/colada

Version:

The smart data fetching layer for Vue.js

1,232 lines (1,216 loc) 54.4 kB
import * as vue from 'vue'; import { MaybeRefOrGetter, ShallowRef, EffectScope, ComponentInternalInstance, ComputedRef, Plugin } from 'vue'; import * as pinia from 'pinia'; import { Pinia } from 'pinia'; /** * 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. * @internal */ interface _DataState_Base<TData, TError> { /** * The last successfully resolved data. */ data: TData; /** * The last rejected error. */ error: TError; /** * The status of the data. * @see {@link DataStateStatus} */ status: DataStateStatus; } interface DataState_Success<TData, TDataInitial> extends _DataState_Base<TData | Exclude<TDataInitial, undefined>, null> { status: 'success'; } interface DataState_Error<TData, TError, TDataInitial> extends _DataState_Base<TData | 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<TData, TError, TDataInitial = undefined> = DataState_Success<TData, TDataInitial> | DataState_Error<TData, 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'; /** * 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; declare function toCacheKey(key: undefined): undefined; declare function toCacheKey(key: EntryKey): string; declare function toCacheKey(key: EntryKey | undefined): string | undefined; /** * Used for keys * * @internal */ type JSONPrimitive = string | number | boolean | null; /** * Used for keys * * @internal */ type JSONValue = JSONPrimitive | JSONObject | JSONArray; /** * Used for keys. Interface to avoid deep recursion. * * @internal */ interface JSONObject { readonly [key: string]: JSONValue | undefined; } /** * Used for keys. Interface to avoid deep recursion. * * @internal */ interface JSONArray extends Array<JSONValue> { } /** * Key used to identify a query or a mutation. Must be a JSON serializable * value. Type is unknwon to avoid deep type recursion. */ type EntryKey = readonly JSONValue[]; /** * Internal symbol used to tag the data type of the entry key. * * @internal */ declare const ENTRY_DATA_TAG: unique symbol; /** * Internal symbol used to tag the error type of the entry key. * * @internal */ declare const ENTRY_ERROR_TAG: unique symbol; /** * Internal symbol used to tag the data initial type of the entry key. * * @internal */ declare const ENTRY_DATA_INITIAL_TAG: unique symbol; /** * Same as {@link EntryKey} but with a data tag that allows inference of the data type. * Used by `defineQueryOptions()`. */ type EntryKeyTagged<TData, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined> = EntryKey & { [ENTRY_DATA_TAG]: TData; [ENTRY_ERROR_TAG]: TError; [ENTRY_DATA_INITIAL_TAG]: TDataInitial; }; /** * 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<TData = unknown, TError = ErrorDefault, TDataInitial extends TData | undefined = 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<TData>; /** * The data which is initially set to the query while the query is loading * for the first time. Note: unlike with {@link placeholderData}, setting the * initial data changes the state of the query (it will be set to `success`). * * @see {@link placeholderData} */ 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 {@link initialData}, the placeholder does not change the cache state. * * @see {@link initialData} */ placeholderData?: NoInfer<TDataInitial> | NoInfer<TData> | (<T extends TData>(previousData: T | undefined) => NoInfer<TDataInitial> | NoInfer<TData> | 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<TData = unknown, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined> = UseQueryOptions<TData, TError, TDataInitial> & typeof USE_QUERY_DEFAULTS; /** * Base interface for {@link EntryFilter}. * * @internal */ interface EntryFilter_Base<TEntry> { /** * A key to filter the entries. */ key?: EntryKey; /** * If `true`, it will only match the entry of the given `key`, skipping any children entries. * It also makes `key` required. * * @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: TEntry) => boolean; } /** * Filter to get exactly one entry from the cache. Requires the `key` to be set. * * @internal */ interface EntryFilter_Key<TEntry> extends EntryFilter_Base<TEntry> { key: EntryKey; exact: true; } /** * Filter to get multiple matching entries from the cache. * * @internal */ interface EntryFilter_NoKey<TEntry> extends EntryFilter_Base<TEntry> { exact?: false; } /** * Base interface to filter entries from a cache. * * @internal */ type EntryFilter<TEntry> = EntryFilter_NoKey<TEntry> | EntryFilter_Key<TEntry>; /** * Allows defining extensions to the query entry that are returned by `useQuery()`. */ interface UseQueryEntryExtensions<TData, TError, TDataInitial extends TData | undefined = 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<TData = unknown, TError = unknown, TDataInitial extends TData | undefined = unknown extends TData ? unknown : undefined> { /** * The state of the query. Contains the data, error and status. */ state: ShallowRef<DataState<TData, 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 | TData | 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: EntryKey; /** * Seriaized version of the key. Used to retrieve the entry from the cache. */ keyHash: string; /** * 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<TData, 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<TData, 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<TData, TError, TDataInitial>; /** * Internal property to store the HMR ids of the components that are using * this query and force refetching. * * @internal */ __hmr?: { /** * Reference count of the components using this query. */ ids: Map<string, number>; }; } /** * Filter object to get entries from the query cache. * * @see {@link QueryCache.getEntries} * @see {@link QueryCache.cancelQueries} * @see {@link QueryCache#invalidateQueries} */ type UseQueryEntryFilter = EntryFilter<UseQueryEntry>; /** * A query entry that is defined with {@link defineQuery}. * @internal */ type DefineQueryEntry = [ lastEnsuredEntries: UseQueryEntry[], returnValue: unknown, effect: EffectScope, paused: ShallowRef<boolean> ]; /** * 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<Map<string, UseQueryEntry<unknown, unknown, unknown>>, Map<string, 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: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey, data: NoInfer<TData> | Exclude<NoInfer<TDataInitial>, undefined> | ((oldData: TData | TDataInitial | undefined) => TData | Exclude<TDataInitial, undefined>)) => void; setQueriesData: <TData = unknown>(filters: UseQueryEntryFilter, updater: (previous: TData | undefined) => TData) => void; getQueryData: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey) => TData | TDataInitial | undefined; invalidateQueries: (filters?: UseQueryEntryFilter, refetchActive?: boolean | "all") => Promise<unknown>; cancelQueries: (filters?: UseQueryEntryFilter, reason?: unknown) => void; invalidate: (entry: UseQueryEntry) => void; fetch: <TData, TError, TDataInitial extends TData | undefined>(entry: UseQueryEntry<TData, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial>) => Promise<DataState<TData, TError, TDataInitial>>; refresh: <TData, TError, TDataInitial extends TData | undefined>(entry: UseQueryEntry<TData, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial>) => Promise<DataState<TData, TError, TDataInitial>>; ensure: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(opts: UseQueryOptions<TData, TError, TDataInitial>, previousEntry?: UseQueryEntry<TData, TError, TDataInitial>) => UseQueryEntry<TData, TError, TDataInitial>; extend: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(_entry: UseQueryEntry<TData, TError, TDataInitial>) => 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: <TData, TError, TDataInitial extends TData | undefined>(key: EntryKey, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial> | null, initialData?: TDataInitial, error?: TError | null, when?: number) => UseQueryEntry<TData, TError, TDataInitial>; remove: (entry: UseQueryEntry) => void; get: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey) => UseQueryEntry<TData, TError, TDataInitial> | undefined; setEntryState: <TData, TError, TDataInitial extends TData | undefined = TData>(entry: UseQueryEntry<TData, TError, TDataInitial>, state: DataState<NoInfer<TData>, NoInfer<TError>, NoInfer<TDataInitial>>) => void; getEntries: (filters?: UseQueryEntryFilter) => UseQueryEntry[]; }, "caches" | "_s">, Pick<{ caches: vue.Ref<Map<string, UseQueryEntry<unknown, unknown, unknown>>, Map<string, 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: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey, data: NoInfer<TData> | Exclude<NoInfer<TDataInitial>, undefined> | ((oldData: TData | TDataInitial | undefined) => TData | Exclude<TDataInitial, undefined>)) => void; setQueriesData: <TData = unknown>(filters: UseQueryEntryFilter, updater: (previous: TData | undefined) => TData) => void; getQueryData: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey) => TData | TDataInitial | undefined; invalidateQueries: (filters?: UseQueryEntryFilter, refetchActive?: boolean | "all") => Promise<unknown>; cancelQueries: (filters?: UseQueryEntryFilter, reason?: unknown) => void; invalidate: (entry: UseQueryEntry) => void; fetch: <TData, TError, TDataInitial extends TData | undefined>(entry: UseQueryEntry<TData, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial>) => Promise<DataState<TData, TError, TDataInitial>>; refresh: <TData, TError, TDataInitial extends TData | undefined>(entry: UseQueryEntry<TData, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial>) => Promise<DataState<TData, TError, TDataInitial>>; ensure: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(opts: UseQueryOptions<TData, TError, TDataInitial>, previousEntry?: UseQueryEntry<TData, TError, TDataInitial>) => UseQueryEntry<TData, TError, TDataInitial>; extend: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(_entry: UseQueryEntry<TData, TError, TDataInitial>) => 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: <TData, TError, TDataInitial extends TData | undefined>(key: EntryKey, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial> | null, initialData?: TDataInitial, error?: TError | null, when?: number) => UseQueryEntry<TData, TError, TDataInitial>; remove: (entry: UseQueryEntry) => void; get: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey) => UseQueryEntry<TData, TError, TDataInitial> | undefined; setEntryState: <TData, TError, TDataInitial extends TData | undefined = TData>(entry: UseQueryEntry<TData, TError, TDataInitial>, state: DataState<NoInfer<TData>, NoInfer<TError>, NoInfer<TDataInitial>>) => void; getEntries: (filters?: UseQueryEntryFilter) => UseQueryEntry[]; }, never>, Pick<{ caches: vue.Ref<Map<string, UseQueryEntry<unknown, unknown, unknown>>, Map<string, 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: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey, data: NoInfer<TData> | Exclude<NoInfer<TDataInitial>, undefined> | ((oldData: TData | TDataInitial | undefined) => TData | Exclude<TDataInitial, undefined>)) => void; setQueriesData: <TData = unknown>(filters: UseQueryEntryFilter, updater: (previous: TData | undefined) => TData) => void; getQueryData: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey) => TData | TDataInitial | undefined; invalidateQueries: (filters?: UseQueryEntryFilter, refetchActive?: boolean | "all") => Promise<unknown>; cancelQueries: (filters?: UseQueryEntryFilter, reason?: unknown) => void; invalidate: (entry: UseQueryEntry) => void; fetch: <TData, TError, TDataInitial extends TData | undefined>(entry: UseQueryEntry<TData, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial>) => Promise<DataState<TData, TError, TDataInitial>>; refresh: <TData, TError, TDataInitial extends TData | undefined>(entry: UseQueryEntry<TData, TError, TDataInitial>, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial>) => Promise<DataState<TData, TError, TDataInitial>>; ensure: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(opts: UseQueryOptions<TData, TError, TDataInitial>, previousEntry?: UseQueryEntry<TData, TError, TDataInitial>) => UseQueryEntry<TData, TError, TDataInitial>; extend: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(_entry: UseQueryEntry<TData, TError, TDataInitial>) => 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: <TData, TError, TDataInitial extends TData | undefined>(key: EntryKey, options?: UseQueryOptionsWithDefaults<TData, TError, TDataInitial> | null, initialData?: TDataInitial, error?: TError | null, when?: number) => UseQueryEntry<TData, TError, TDataInitial>; remove: (entry: UseQueryEntry) => void; get: <TData = unknown, TError = Error, TDataInitial extends TData | undefined = undefined>(key: EntryKeyTagged<TData, TError, TDataInitial> | EntryKey) => UseQueryEntry<TData, TError, TDataInitial> | undefined; setEntryState: <TData, TError, TDataInitial extends TData | undefined = TData>(entry: UseQueryEntry<TData, TError, TDataInitial>, state: DataState<NoInfer<TData>, NoInfer<TError>, NoInfer<TDataInitial>>) => void; getEntries: (filters?: UseQueryEntryFilter) => UseQueryEntry[]; }, "cancel" | "get" | "ensureDefinedQuery" | "setQueryData" | "setQueriesData" | "getQueryData" | "invalidateQueries" | "cancelQueries" | "invalidate" | "fetch" | "refresh" | "ensure" | "extend" | "track" | "untrack" | "create" | "remove" | "setEntryState" | "getEntries">>; /** * The cache of the queries. It's the store returned by {@link useQueryCache}. */ type QueryCache = ReturnType<typeof useQueryCache>; /** * Checks if the given object is a query cache. Used in SSR to apply custom serialization. * * @param cache - the object to check * * @see {@link QueryCache} * @see {@link serializeQueryCache} */ declare function isQueryCache(cache: unknown): cache is QueryCache; /** * Raw data of a query entry. Can be serialized from the server and used to * hydrate the store. * * @internal */ type _UseQueryEntryNodeValueSerialized<TData = unknown, TError = unknown> = [ /** * The data returned by the query. */ data: TData | undefined, /** * The error thrown by the query. */ error: TError | null, /** * When was this data fetched the last time in ms */ when?: number ]; /** * 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: Record<string, _UseQueryEntryNodeValueSerialized>): void; /** * Serializes the query cache to a compressed version. Used during SSR. * * @param queryCache - query cache */ declare function serializeQueryCache(queryCache: QueryCache): Record<string, _UseQueryEntryNodeValueSerialized>; /** * Return type of `useQuery()`. */ interface UseQueryReturn<TData = unknown, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined> extends UseQueryEntryExtensions<TData, TError, TDataInitial> { /** * The state of the query. Contains its data, error, and status. */ state: ComputedRef<DataState<TData, 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<TData | 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<TData, 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<TData, TError, TDataInitial>>; } /** * Ensures and return a shared query state based on the `key` option. * * @param options - The options of the query * * @example * ```ts * const { state } = useQuery({ * key: ['documents'], * query: () => getDocuments(), * }) * ``` */ declare function useQuery<TData, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined>(options: UseQueryOptions<TData, TError, TDataInitial> | (() => DefineQueryOptions<TData, TError, TDataInitial>)): UseQueryReturn<TData, TError, TDataInitial>; /** * `useQuery` for dynamic typed query keys. Requires options defined with * {@link defineQueryOptions}. * * @param setupOptions - options defined with {@link defineQueryOptions} * @param paramsGetter - a getter or ref that returns the parameters for the `setupOptions` * * @example * ```ts * import { defineQueryOptions, useQuery } from '@pinia/colada' * * const documentDetailsQuery = defineQueryOptions((id: number ) => ({ * key: ['documents', id], * query: () => fetchDocument(id), * })) * * useQuery(documentDetailsQuery, 4) * useQuery(documentDetailsQuery, () => route.params.id) * useQuery(documentDetailsQuery, () => props.id) * ``` */ declare function useQuery<Params, TData, TError, TDataInitial extends TData | undefined>(setupOptions: (params: Params) => DefineQueryOptions<TData, TError, TDataInitial>, paramsGetter: MaybeRefOrGetter<NoInfer<Params>>): UseQueryReturn<TData, TError, TDataInitial>; /** * Type that represents a value that can be an array or a single value. * * @internal */ type _MaybeArray<T> = T | T[]; /** * Checks if a type is exactly `any`. * * @internal */ type IsAny<T> = 0 extends 1 & T ? true : false; /** * Checks if a type is exactly `unknown`. This is useful to determine if a type is * * @internal */ type IsUnknown<T> = IsAny<T> extends true ? false : unknown extends T ? true : false; /** * Type that represents a value that can be a promise or a single value. * * @internal */ type _Awaitable<T> = T | Promise<T>; /** * To avoid using `{}` * @internal */ interface _EmptyObject { } /** * @internal */ type _IsMaybeRefOrGetter<T> = [T] extends [MaybeRefOrGetter<infer U>] ? MaybeRefOrGetter<U> extends T ? true : false : false; /** * @internal */ type _UnwrapMaybeRefOrGetter<T> = T extends MaybeRefOrGetter<infer U> ? U : T; /** * Removes the `MaybeRefOrGetter` wrapper from all fields of an object. * @internal */ type _RemoveMaybeRef<T> = { [K in keyof T]: _IsMaybeRefOrGetter<NonNullable<T[K]>> extends true ? _UnwrapMaybeRefOrGetter<T[K]> : T[K]; }; /** * Options to define a query with `defineQuery()`. Similar to * {@link UseQueryOptions} but disallows reactive values as `defineQuery()` is * used outside of an effect scope. */ type DefineQueryOptions<TData = unknown, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined> = _RemoveMaybeRef<UseQueryOptions<TData, TError, TDataInitial>> & { initialData?: () => TDataInitial; placeholderData?: NoInfer<TDataInitial> | NoInfer<TData> | (<T extends TData>(previousData: T | undefined) => NoInfer<TDataInitial> | NoInfer<TData> | undefined); }; /** * Define a query with the given options. Similar to `useQuery(options)` but * allows you to reuse **all** of the query state 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<TData, TError = ErrorDefault>(options: DefineQueryOptions<TData, TError>): () => UseQueryReturn<TData, 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; /** * Tagged version of {@link DefineQueryOptions} that includes a key with * data type information. */ interface DefineQueryOptionsTagged<TData = unknown, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined> extends DefineQueryOptions<TData, TError, TDataInitial> { key: EntryKeyTagged<TData, TError, TDataInitial>; } /** * Define dynamic query options by passing a function that accepts an arbitrary * parameter and returns the query options. Enables type-safe query keys. * Must be passed to {@link useQuery} alongside a getter for the params. * * @param setupOptions - A function that returns the query options. */ declare function defineQueryOptions<Params, TData, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined>(setupOptions: (params: Params) => DefineQueryOptions<TData, TError, TDataInitial>): (params: Params) => DefineQueryOptionsTagged<TData, TError, TDataInitial>; /** * Define static query options that are type safe with * `queryCache.getQueryData()`. Can be passed directly to {@link useQuery}. * * @param options - The query options. */ declare function defineQueryOptions<TData, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined>(options: DefineQueryOptions<TData, TError, TDataInitial>): DefineQueryOptionsTagged<TData, TError, TDataInitial>; /** * Return type for the {@link useQueryState} composable. * * @see {@link useQueryState} */ interface UseQueryStateReturn<TData = unknown, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined> { /** * `state` of the query entry. * * @see {@link UseQueryReturn#state} */ state: ComputedRef<DataState<TData, TError, TDataInitial> | undefined>; /** * `data` of the query entry. * * @see {@link UseQueryReturn#data} */ data: ComputedRef<TData | TDataInitial | undefined>; /** * `error` of the query entry. * * @see {@link UseQueryReturn#error} */ error: ComputedRef<TError | null | undefined>; /** * `status` of the query entry. * * @see {@link DataStateStatus} * @see {@link UseQueryReturn#status} */ status: ComputedRef<DataStateStatus | undefined>; /** * `asyncStatus` of the query entry. * * @see {@link AsyncStatus} * @see {@link UseQueryReturn#asyncStatus} */ asyncStatus: ComputedRef<AsyncStatus | undefined>; /** * Is the query entry currently pending or non existent. */ isPending: ComputedRef<boolean>; } /** * Reactive access to the state of a query entry without fetching it. * * @param key - tagged key of the query entry to access */ declare function useQueryState<TData, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined>(key: MaybeRefOrGetter<EntryKeyTagged<TData, TError, TDataInitial>>): UseQueryStateReturn<TData, TError, TDataInitial>; /** * Reactive access to the state of a query entry without fetching it. * * @param setupOptions - function that returns the query options based on the provided params * @param paramsGetter - getter for the parameters used to generate the query key * * @see {@link DefineQueryOptions} * @see {@link defineQueryOptions} */ declare function useQueryState<Params, TData, TError, TDataInitial extends TData | undefined>(setupOptions: (params: Params) => DefineQueryOptions<TData, TError, TDataInitial>, paramsGetter: MaybeRefOrGetter<NoInfer<Params>>): UseQueryStateReturn<TData, TError, TDataInitial>; /** * Reactive access to the state of a query entry without fetching it. * * @param key - key of the query entry to access */ declare function useQueryState<TData, TError = ErrorDefault, TDataInitial extends TData | undefined = undefined>(key: MaybeRefOrGetter<EntryKey>): UseQueryStateReturn<TData, TError, TDataInitial>; /** * Options for {@link useInfiniteQuery}. * * @experimental See https://github.com/posva/pinia-colada/issues/178 */ interface UseInfiniteQueryOptions<TData, TError, TDataInitial extends TData | undefined = TData | undefined, TPages = unknown> extends Omit<UseQueryOptions<TData, TError, TDataInitial>, 'query' | 'initialData' | 'placeholderData' | 'key'> { key: UseQueryOptions<TPages, TError, TPages>['key']; /** * The function that will be called to fetch the data. It **must** be async. */ query: (pages: NoInfer<TPages>, context: UseQueryFnContext) => Promise<TData>; initialPage: TPages | (() => TPages); merge: (result: NoInfer<TPages>, current: NoInfer<TData>) => NoInfer<TPages>; } interface UseInfiniteQueryReturn<TPage = unknown, TError = ErrorDefault> extends Omit<UseQueryReturn<TPage, TError, TPage>, 'refetch' | 'refresh'> { loadMore: () => Promise<unknown>; } /** * 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<TData, TError = ErrorDefault, TPage = unknown>(options: UseInfiniteQueryOptions<TData, TError, TData | undefined, TPage>): UseInfiniteQueryReturn<TPage, TError>; /** * 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<TData, 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<TData, 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<TData | 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<TData> : (vars: TVars) => Promise<TData>; /** * 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<TData, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> = _EmptyObject>(options: UseMutationOptions<TData, TVars, TError, TContext>): UseMutationReturn<TData, TVars, TError>; /** * 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; /** * Time in ms after which, once the mutation is no longer being used, it will be * garbage collected to free resources. Set to `false` to disable garbage * collection (not recommended). * * @default 60_000 (1 minute) */ gcTime?: number | false; } /** * Default options for `useMutation()`. Modifying this object will affect all mutations. */ declare const USE_MUTATION_DEFAULTS: { gcTime: NonNullable<UseMutationOptions["gcTime"]>; }; /** * Options to create a mutation. */ interface UseMutationOptions<TData = unknown, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> = _EmptyObject> extends Pick<UseMutationOptionsGlobal, 'gcTime'> { /** * 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<TData>; /** * 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<TData>, /** * 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<TData> | 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; } /** * Global default options for `useMutations()`. * @internal */ type UseMutationOptionsGlobalDefaults = UseMutationOptionsGlobal & typeof USE_MUTATION_DEFAULTS; /** * 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<TData, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> = _EmptyObject>(options: UseMutationOptions<TData, TVars, TError, TContext>): () => UseMutationReturn<TData, 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; /** * 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 {@link https://vuejs.org/api/reactivity-advanced.html#effectscope} */ scop