@pinia/colada
Advanced tools
Comparing version 0.5.3 to 0.6.0
import * as vue from 'vue'; | ||
import { Ref, ComputedRef, App, MaybeRefOrGetter, ShallowRef } from 'vue'; | ||
import { Ref, ComputedRef, ShallowRef, App, MaybeRefOrGetter } from 'vue'; | ||
import * as pinia from 'pinia'; | ||
@@ -47,2 +47,12 @@ | ||
/** | ||
* 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>; | ||
/** | ||
* Flattens an object type for readability. | ||
@@ -96,2 +106,123 @@ * @internal | ||
/** | ||
* The status of the request. | ||
* - `pending`: initial state | ||
* - `loading`: request is being made | ||
* - `error`: when the last request failed | ||
* - `success`: when the last request succeeded | ||
*/ | ||
type QueryStatus = 'pending' | 'loading' | 'error' | 'success'; | ||
/** | ||
* Properties of a query entry that will be exposed to the user. Split to keep the documented properties in one place. | ||
* @internal | ||
*/ | ||
interface _UseQueryEntry_State<TResult, TError> { | ||
/** | ||
* The last successful data resolved by the query. | ||
*/ | ||
data: ShallowRef<TResult | undefined>; | ||
/** | ||
* The error rejected by the query. | ||
*/ | ||
error: ShallowRef<TError | null>; | ||
/** | ||
* The status of the query. | ||
* @see {@link QueryStatus} | ||
*/ | ||
status: ShallowRef<QueryStatus>; | ||
/** | ||
* Returns whether the request is still pending its first call. Alias for `status.value === 'pending'` | ||
*/ | ||
isPending: ComputedRef<boolean>; | ||
/** | ||
* Returns whether the request is currently fetching data. | ||
*/ | ||
isFetching: ShallowRef<boolean>; | ||
} | ||
interface UseQueryEntry<TResult = unknown, TError = unknown> extends _UseQueryEntry_State<TResult, TError> { | ||
/** | ||
* When was this data fetched the last time in ms | ||
*/ | ||
when: number; | ||
pending: null | { | ||
abortController: AbortController; | ||
refreshCall: Promise<void>; | ||
when: number; | ||
}; | ||
/** | ||
* Options used to create the query. They can be undefined during hydration but are needed for fetching. This is why `store.ensureEntry()` sets this property. | ||
*/ | ||
options?: UseQueryOptionsWithDefaults<TResult, TError>; | ||
} | ||
declare const useQueryCache: pinia.StoreDefinition<"_pc_query", pinia._UnwrapAll<Pick<{ | ||
caches: vue.ShallowReactive<TreeMapNode<UseQueryEntry<unknown, unknown>>>; | ||
ensureEntry: <TResult = unknown, TError = Error>(keyRaw: UseQueryKey, options: UseQueryOptionsWithDefaults<TResult, TError>) => UseQueryEntry<TResult, TError>; | ||
ensureDefinedQuery: <T>(fn: () => T) => T; | ||
invalidateEntry: (key: UseQueryKey, { refetch: shouldRefetch, exact, }?: { | ||
refetch?: boolean | undefined; | ||
exact?: boolean | undefined; | ||
}) => Promise<unknown> | undefined; | ||
setQueryData: <TResult_1 = unknown>(key: UseQueryKey, data: TResult_1 | ((data: Ref<TResult_1 | undefined>) => void)) => void; | ||
getQueryData: <TResult_2 = unknown>(key: UseQueryKey) => TResult_2 | undefined; | ||
refetch: <TResult_3, TError_1>(entry: UseQueryEntry<TResult_3, TError_1>) => Promise<TResult_3>; | ||
refresh: <TResult_4, TError_2>(entry: UseQueryEntry<TResult_4, TError_2>) => Promise<TResult_4>; | ||
}, "caches">>, Pick<{ | ||
caches: vue.ShallowReactive<TreeMapNode<UseQueryEntry<unknown, unknown>>>; | ||
ensureEntry: <TResult = unknown, TError = Error>(keyRaw: UseQueryKey, options: UseQueryOptionsWithDefaults<TResult, TError>) => UseQueryEntry<TResult, TError>; | ||
ensureDefinedQuery: <T>(fn: () => T) => T; | ||
invalidateEntry: (key: UseQueryKey, { refetch: shouldRefetch, exact, }?: { | ||
refetch?: boolean | undefined; | ||
exact?: boolean | undefined; | ||
}) => Promise<unknown> | undefined; | ||
setQueryData: <TResult_1 = unknown>(key: UseQueryKey, data: TResult_1 | ((data: Ref<TResult_1 | undefined>) => void)) => void; | ||
getQueryData: <TResult_2 = unknown>(key: UseQueryKey) => TResult_2 | undefined; | ||
refetch: <TResult_3, TError_1>(entry: UseQueryEntry<TResult_3, TError_1>) => Promise<TResult_3>; | ||
refresh: <TResult_4, TError_2>(entry: UseQueryEntry<TResult_4, TError_2>) => Promise<TResult_4>; | ||
}, never>, Pick<{ | ||
caches: vue.ShallowReactive<TreeMapNode<UseQueryEntry<unknown, unknown>>>; | ||
ensureEntry: <TResult = unknown, TError = Error>(keyRaw: UseQueryKey, options: UseQueryOptionsWithDefaults<TResult, TError>) => UseQueryEntry<TResult, TError>; | ||
ensureDefinedQuery: <T>(fn: () => T) => T; | ||
invalidateEntry: (key: UseQueryKey, { refetch: shouldRefetch, exact, }?: { | ||
refetch?: boolean | undefined; | ||
exact?: boolean | undefined; | ||
}) => Promise<unknown> | undefined; | ||
setQueryData: <TResult_1 = unknown>(key: UseQueryKey, data: TResult_1 | ((data: Ref<TResult_1 | undefined>) => void)) => void; | ||
getQueryData: <TResult_2 = unknown>(key: UseQueryKey) => TResult_2 | undefined; | ||
refetch: <TResult_3, TError_1>(entry: UseQueryEntry<TResult_3, TError_1>) => Promise<TResult_3>; | ||
refresh: <TResult_4, TError_2>(entry: UseQueryEntry<TResult_4, TError_2>) => Promise<TResult_4>; | ||
}, "ensureEntry" | "ensureDefinedQuery" | "invalidateEntry" | "setQueryData" | "getQueryData" | "refetch" | "refresh">>; | ||
/** | ||
* 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[] | ||
]; | ||
/** | ||
* Transform a tree into a compressed array. | ||
* @param root - root node of the tree | ||
* @returns Array representation of the tree | ||
*/ | ||
declare function serialize(root: TreeMapNode<UseQueryEntry>): UseQueryEntryNodeSerialized[]; | ||
/** | ||
* Return type of `useQuery()`. | ||
@@ -116,5 +247,5 @@ */ | ||
*/ | ||
declare function useQuery<TResult, TError = ErrorDefault>(_options: UseQueryOptions<TResult>): UseQueryReturn<TResult, TError>; | ||
declare function useQuery<TResult, TError = ErrorDefault>(_options: UseQueryOptions<TResult, TError>): UseQueryReturn<TResult, TError>; | ||
interface QueryPluginOptions extends Omit<UseQueryOptions, 'key' | 'query' | 'initialData'> { | ||
interface QueryPluginOptions extends Omit<UseQueryOptions, 'key' | 'query' | 'initialData' | 'transformError'> { | ||
/** | ||
@@ -126,7 +257,31 @@ * Executes setup code inside `useQuery()` to add custom behavior to all queries. **Must be synchronous**. | ||
setup?: <TResult = unknown, TError = ErrorDefault>(context: _Simplify<UseQueryReturn<TResult, TError> & { | ||
options: UseQueryOptionsWithDefaults<TResult>; | ||
options: UseQueryOptionsWithDefaults<TResult, TError>; | ||
}>) => void | Promise<never>; | ||
onSuccess?: () => void; | ||
onSettled?: () => void; | ||
onError?: () => void; | ||
/** | ||
* Global handler for when a query is successful. | ||
* | ||
* @param data - data returned by the query | ||
*/ | ||
onSuccess?: (data: 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?: (data: unknown | undefined, error: unknown | null) => unknown; | ||
/** | ||
* Global error handler for all queries. | ||
* | ||
* @param error - error thrown | ||
*/ | ||
onError?: (error: unknown) => unknown; | ||
/** | ||
* Function to ensure the `error` property is always an instance of the default global type error. Defaults to the | ||
* identity function. | ||
* | ||
* @param error - error thrown | ||
*/ | ||
transformError?: (error: unknown) => ErrorDefault; | ||
} | ||
@@ -169,3 +324,3 @@ declare function QueryPlugin(app: App, { onSuccess, onSettled, onError, ...useQueryOptions }?: QueryPluginOptions): void; | ||
*/ | ||
interface UseQueryOptions<TResult = unknown> { | ||
interface UseQueryOptions<TResult = unknown, TError = ErrorDefault> { | ||
/** | ||
@@ -198,6 +353,22 @@ * The key used to identify the query. Array of primitives **without** reactive values or a reactive array or getter. | ||
/** | ||
* Time in ms after which, once the data is no longer being used, it will be garbage collected to free resources. | ||
* Time in ms after which, once the data is no longer being used, it will be garbage collected to free resources. TODO: **NOT IMPLEMENTED YET** | ||
*/ | ||
gcTime?: number; | ||
initialData?: () => TResult; | ||
/** | ||
* Function to type and ensure the `error` property is always an instance of `TError`. | ||
* | ||
* @param error - error thrown | ||
* @example | ||
* ```ts | ||
* useQuery({ | ||
* key: ['user', id], | ||
* query: () => fetchUser(id), | ||
* transformError: (error): MyCustomError | UnexpectedError => | ||
* // this assumes both `MyCustomError` and a `UnexpectedError` are valid error classes | ||
* error instanceof MyCustomError ? error : new UnexpectedError(error), | ||
* }) | ||
* ``` | ||
*/ | ||
transformError?: (error: unknown) => TError; | ||
refetchOnMount?: _RefetchOnControl; | ||
@@ -216,134 +387,108 @@ refetchOnWindowFocus?: _RefetchOnControl; | ||
refetchOnMount: _RefetchOnControl; | ||
transformError: (error: unknown) => any; | ||
}; | ||
type UseQueryOptionsWithDefaults<TResult = unknown> = typeof USE_QUERY_DEFAULTS & UseQueryOptions<TResult>; | ||
type UseQueryOptionsWithDefaults<TResult = unknown, TError = ErrorDefault> = typeof USE_QUERY_DEFAULTS & UseQueryOptions<TResult, TError>; | ||
/** | ||
* The status of the request. | ||
* The keys to invalidate when a mutation succeeds. | ||
* @internal | ||
*/ | ||
type _MutationKeys<TVars, TResult> = UseQueryKey[] | ((data: TResult, vars: TVars) => UseQueryKey[]); | ||
/** | ||
* The status of the mutation. | ||
* - `pending`: initial state | ||
* - `loading`: request is being made | ||
* - `error`: when the last request failed | ||
* - `success`: when the last request succeeded | ||
* - `loading`: mutation is being made | ||
* - `error`: when the last mutation failed | ||
* - `success`: when the last mutation succeeded | ||
*/ | ||
type UseQueryStatus = 'pending' | 'loading' | 'error' | 'success'; | ||
type MutationStatus = 'pending' | 'loading' | 'error' | 'success'; | ||
/** | ||
* Properties of a query entry that will be exposed to the user. Split to keep the documented properties in one place. | ||
* To avoid using `{}` | ||
* @internal | ||
*/ | ||
interface _UseQueryEntry_State<TResult, TError> { | ||
/** | ||
* The last successful data resolved by the query. | ||
*/ | ||
data: ShallowRef<TResult | undefined>; | ||
/** | ||
* The error rejected by the query. | ||
*/ | ||
error: ShallowRef<TError | null>; | ||
/** | ||
* The status of the query. | ||
* @see {@link UseQueryStatus} | ||
*/ | ||
status: ShallowRef<UseQueryStatus>; | ||
/** | ||
* Returns whether the request is still pending its first call. Alias for `status.value === 'pending'` | ||
*/ | ||
isPending: ComputedRef<boolean>; | ||
/** | ||
* Returns whether the request is currently fetching data. | ||
*/ | ||
isFetching: ShallowRef<boolean>; | ||
interface _EmptyObject { | ||
} | ||
interface UseQueryEntry<TResult = unknown, TError = unknown> extends _UseQueryEntry_State<TResult, TError> { | ||
/** | ||
* When was this data fetched the last time in ms | ||
*/ | ||
when: number; | ||
pending: null | { | ||
refreshCall: Promise<void>; | ||
when: number; | ||
}; | ||
/** | ||
* Options used to create the query. They can be undefined during hydration but are needed for fetching. This is why `store.ensureEntry()` sets this property. | ||
*/ | ||
options?: UseQueryOptionsWithDefaults<TResult>; | ||
} | ||
declare const useQueryCache: pinia.StoreDefinition<"_pc_query", pinia._UnwrapAll<Pick<{ | ||
caches: vue.ShallowReactive<TreeMapNode<UseQueryEntry<unknown, unknown>>>; | ||
ensureEntry: <TResult = unknown, TError = Error>(keyRaw: UseQueryKey, options: UseQueryOptionsWithDefaults<TResult>) => UseQueryEntry<TResult, TError>; | ||
invalidateEntry: (key: UseQueryKey, { refetch: shouldRefetch, exact, }?: { | ||
refetch?: boolean | undefined; | ||
exact?: boolean | undefined; | ||
}) => void; | ||
setQueryData: <TResult_1 = unknown>(key: UseQueryKey, data: TResult_1 | ((data: Ref<TResult_1 | undefined>) => void)) => void; | ||
getQueryData: <TResult_2 = unknown>(key: UseQueryKey) => TResult_2 | undefined; | ||
refetch: <TResult_3, TError_1>(entry: UseQueryEntry<TResult_3, TError_1>) => Promise<TResult_3>; | ||
refresh: <TResult_4, TError_2>(entry: UseQueryEntry<TResult_4, TError_2>) => Promise<TResult_4>; | ||
}, "caches">>, Pick<{ | ||
caches: vue.ShallowReactive<TreeMapNode<UseQueryEntry<unknown, unknown>>>; | ||
ensureEntry: <TResult = unknown, TError = Error>(keyRaw: UseQueryKey, options: UseQueryOptionsWithDefaults<TResult>) => UseQueryEntry<TResult, TError>; | ||
invalidateEntry: (key: UseQueryKey, { refetch: shouldRefetch, exact, }?: { | ||
refetch?: boolean | undefined; | ||
exact?: boolean | undefined; | ||
}) => void; | ||
setQueryData: <TResult_1 = unknown>(key: UseQueryKey, data: TResult_1 | ((data: Ref<TResult_1 | undefined>) => void)) => void; | ||
getQueryData: <TResult_2 = unknown>(key: UseQueryKey) => TResult_2 | undefined; | ||
refetch: <TResult_3, TError_1>(entry: UseQueryEntry<TResult_3, TError_1>) => Promise<TResult_3>; | ||
refresh: <TResult_4, TError_2>(entry: UseQueryEntry<TResult_4, TError_2>) => Promise<TResult_4>; | ||
}, never>, Pick<{ | ||
caches: vue.ShallowReactive<TreeMapNode<UseQueryEntry<unknown, unknown>>>; | ||
ensureEntry: <TResult = unknown, TError = Error>(keyRaw: UseQueryKey, options: UseQueryOptionsWithDefaults<TResult>) => UseQueryEntry<TResult, TError>; | ||
invalidateEntry: (key: UseQueryKey, { refetch: shouldRefetch, exact, }?: { | ||
refetch?: boolean | undefined; | ||
exact?: boolean | undefined; | ||
}) => void; | ||
setQueryData: <TResult_1 = unknown>(key: UseQueryKey, data: TResult_1 | ((data: Ref<TResult_1 | undefined>) => void)) => void; | ||
getQueryData: <TResult_2 = unknown>(key: UseQueryKey) => TResult_2 | undefined; | ||
refetch: <TResult_3, TError_1>(entry: UseQueryEntry<TResult_3, TError_1>) => Promise<TResult_3>; | ||
refresh: <TResult_4, TError_2>(entry: UseQueryEntry<TResult_4, TError_2>) => Promise<TResult_4>; | ||
}, "ensureEntry" | "invalidateEntry" | "setQueryData" | "getQueryData" | "refetch" | "refresh">>; | ||
/** | ||
* Raw data of a query entry. Can be serialized from the server and used to hydrate the store. | ||
* Removes the nullish types from the context type to make `A & TContext` work instead of yield `never`. | ||
* @internal | ||
*/ | ||
type _UseQueryEntryNodeValueSerialized<TResult = unknown, TError = unknown> = [ | ||
type _ReduceContext<TContext> = TContext extends void | null | undefined ? _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 UseMutationOptions<TResult = unknown, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> | void | null = void> { | ||
/** | ||
* The data returned by the query. | ||
* The key of the mutation. If the mutation is successful, it will invalidate the query with the same key and refetch it | ||
*/ | ||
data: TResult | undefined, | ||
mutation: (vars: TVars, context: NoInfer<TContext>) => Promise<TResult>; | ||
/** | ||
* The error thrown by the query. | ||
* Keys to invalidate if the mutation succeeds so that `useQuery()` refetch if used. | ||
*/ | ||
error: TError | null, | ||
keys?: _MutationKeys<TVars, TResult>; | ||
/** | ||
* When was this data fetched the last time in ms | ||
* 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 | ||
* }, | ||
* }) | ||
* ``` | ||
*/ | ||
when?: number | ||
]; | ||
/** | ||
* Serialized version of a query entry node. | ||
* @internal | ||
*/ | ||
type UseQueryEntryNodeSerialized = [ | ||
key: EntryNodeKey, | ||
value: undefined | _UseQueryEntryNodeValueSerialized, | ||
children?: UseQueryEntryNodeSerialized[] | ||
]; | ||
/** | ||
* Transform a tree into a compressed array. | ||
* @param root - root node of the tree | ||
* @returns Array representation of the tree | ||
*/ | ||
declare function serialize(root: TreeMapNode<UseQueryEntry>): UseQueryEntryNodeSerialized[]; | ||
type _MutationKeys<TParams extends readonly any[], TResult> = UseQueryKey[] | ((result: TResult, ...args: TParams) => UseQueryKey[]); | ||
interface UseMutationOptions<TResult = unknown, TParams extends readonly unknown[] = readonly []> { | ||
onMutate?: (vars: TVars) => _Awaitable<TContext>; | ||
/** | ||
* The key of the mutation. If the mutation is successful, it will invalidate the query with the same key and refetch it | ||
* Runs if the mutation encounters an error. | ||
*/ | ||
mutation: (...args: TParams) => Promise<TResult>; | ||
onError?: (context: { | ||
error: TError; | ||
vars: TVars; | ||
} & UseMutationGlobalContext & _ReduceContext<TContext>) => unknown; | ||
/** | ||
* Keys to invalidate if the mutation succeeds so that `useQuery()` refetch if used. | ||
* Runs if the mutation is successful. | ||
*/ | ||
keys?: _MutationKeys<TParams, TResult>; | ||
onSuccess?: (context: { | ||
data: TResult; | ||
vars: TVars; | ||
} & UseMutationGlobalContext & _ReduceContext<TContext>) => unknown; | ||
/** | ||
* Runs after the mutation is settled, regardless of the result. | ||
*/ | ||
onSettled?: (context: { | ||
data: TResult | undefined; | ||
error: TError | undefined; | ||
vars: TVars; | ||
} & UseMutationGlobalContext & _ReduceContext<TContext>) => unknown; | ||
} | ||
interface UseMutationReturn<TResult = unknown, TParams extends readonly unknown[] = readonly [], TError = ErrorDefault> { | ||
interface UseMutationReturn<TResult, TVars, TError> { | ||
/** | ||
@@ -363,12 +508,18 @@ * The result of the mutation. `undefined` if the mutation has not been called yet. | ||
* The status of the mutation. | ||
* @see {@link UseQueryStatus} | ||
* @see {@link MutationStatus} | ||
*/ | ||
status: ShallowRef<UseQueryStatus>; | ||
status: ShallowRef<MutationStatus>; | ||
/** | ||
* Calls the mutation and returns a promise with the result. | ||
* | ||
* @param params - parameters to pass to the mutation | ||
* @param args - parameters to pass to the mutation | ||
*/ | ||
mutate: (...params: TParams) => Promise<TResult>; | ||
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. | ||
@@ -378,4 +529,93 @@ */ | ||
} | ||
declare function useMutation<TResult, TParams extends readonly unknown[] = readonly [], TError = ErrorDefault>(options: UseMutationOptions<TResult, TParams>): UseMutationReturn<TResult, TParams, TError>; | ||
/** | ||
* Setups a mutation. | ||
* | ||
* @param options - Options to create the mutation | ||
* @example | ||
* ```ts | ||
* const { mutate, status, error } = useMutation({ | ||
* mutation: (id: number) => fetch(`/api/todos/${id}`), | ||
* onSuccess({ queryClient }) { | ||
* queryClient.invalidateQueries('todos') | ||
* }, | ||
* }) | ||
* ``` | ||
*/ | ||
declare function useMutation<TResult, TVars = void, TError = ErrorDefault, TContext extends Record<any, any> | void | null = void>(options: UseMutationOptions<TResult, TVars, TError, TContext>): UseMutationReturn<TResult, TVars, TError>; | ||
export { type EntryNodeKey, QueryPlugin, type QueryPluginOptions, TreeMapNode, type TypesConfig, type UseMutationOptions, type UseMutationReturn, type UseQueryEntry, type UseQueryKey, type UseQueryOptions, type UseQueryOptionsWithDefaults, type UseQueryReturn, type UseQueryStatus, delayLoadingRef, serialize, useMutation, useQuery, useQueryCache }; | ||
/** | ||
* 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> | void | null = void>(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; | ||
/** | ||
* Define a query with the given options. Similar to `useQuery(options)` but allows you to reuse the query in multiple | ||
* places. | ||
* | ||
* @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: UseQueryOptions<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. | ||
* | ||
* @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; | ||
export { type EntryNodeKey, type MutationStatus, QueryPlugin, type QueryPluginOptions, type QueryStatus, TreeMapNode, type TypesConfig, type UseMutationOptions, type UseMutationReturn, type UseQueryEntry, type UseQueryKey, type UseQueryOptions, type UseQueryOptionsWithDefaults, type UseQueryReturn, type _Awaitable, type _EmptyObject, type _MaybeArray, type _ReduceContext, defineMutation, defineQuery, delayLoadingRef, serialize, useMutation, useQuery, useQueryCache }; |
@@ -7,8 +7,8 @@ // src/use-mutation.ts | ||
import { | ||
computed as computed2, | ||
getCurrentScope as getCurrentScope2, | ||
shallowReactive, | ||
getCurrentScope as getCurrentScope2, | ||
computed as computed2, | ||
triggerRef, | ||
shallowRef, | ||
toValue as toValue2 | ||
toValue as toValue2, | ||
triggerRef | ||
} from "vue"; | ||
@@ -167,2 +167,9 @@ | ||
const scope = getCurrentScope2(); | ||
const defineQueryMap = /* @__PURE__ */ new WeakMap(); | ||
function ensureDefinedQuery(fn) { | ||
if (!defineQueryMap.has(fn)) { | ||
defineQueryMap.set(fn, scope.run(fn)); | ||
} | ||
return defineQueryMap.get(fn); | ||
} | ||
function ensureEntry(keyRaw, options) { | ||
@@ -192,5 +199,4 @@ if (process.env.NODE_ENV !== "production" && keyRaw.length === 0) { | ||
const entryNode = cachesRaw.find(key.map(stringifyFlatObject)); | ||
if (!entryNode) { | ||
if (!entryNode) | ||
return; | ||
} | ||
const list = exact ? entryNode.value != null ? [entryNode.value] : [] : [...entryNode]; | ||
@@ -201,3 +207,3 @@ for (const entry of list) { | ||
entry.pending = null; | ||
refetch(entry); | ||
return refetch(entry); | ||
} | ||
@@ -212,4 +218,4 @@ } | ||
} | ||
const { key: _key, staleTime } = entry.options; | ||
const key = toValue2(_key).map(stringifyFlatObject); | ||
const { staleTime } = entry.options; | ||
const _key = toValue2(entry.options.key).map(stringifyFlatObject); | ||
if (entry.error.value || isExpired(entry.when, staleTime)) { | ||
@@ -226,7 +232,9 @@ await (entry.pending?.refreshCall ?? refetch(entry)); | ||
} | ||
const key = toValue2(entry.options.key).map(stringifyFlatObject); | ||
const _key = toValue2(entry.options.key).map(stringifyFlatObject); | ||
entry.status.value = "loading"; | ||
const signalController = new AbortController(); | ||
const { signal } = signalController; | ||
const abortController = new AbortController(); | ||
const { signal } = abortController; | ||
entry.pending?.abortController.abort(); | ||
const pendingCall = entry.pending = { | ||
abortController, | ||
refreshCall: entry.options.query({ signal }).then((data) => { | ||
@@ -256,5 +264,4 @@ if (pendingCall === entry.pending && !signal.aborted) { | ||
const entry = caches.get(key.map(stringifyFlatObject)); | ||
if (!entry) { | ||
if (!entry) | ||
return; | ||
} | ||
if (typeof data === "function") { | ||
@@ -273,17 +280,15 @@ ; | ||
} | ||
function prefetch(key) { | ||
const entry = cachesRaw.get(key.map(stringifyFlatObject)); | ||
if (!entry) { | ||
if (process.env.NODE_ENV !== "production") { | ||
console.warn( | ||
`\u26A0\uFE0F trying to prefetch "${String(key)}" but it's not in the registry` | ||
); | ||
} | ||
return; | ||
} | ||
return refetch(entry); | ||
function _preload(_useQueryFn) { | ||
} | ||
return { | ||
caches, | ||
// TODO: figure out if worth or eslint is enough | ||
// used to warn the user against wrong usage and redirect them to the docs | ||
// to use `defineQuery()` instead | ||
// warnChecksMap: | ||
// process.env.NODE_ENV !== 'production' | ||
// ? new WeakMap<object, boolean>() | ||
// : undefined, | ||
ensureEntry, | ||
ensureDefinedQuery, | ||
invalidateEntry, | ||
@@ -316,12 +321,22 @@ setQueryData, | ||
const error = shallowRef2(null); | ||
let pendingPromise = null; | ||
function mutate(...args) { | ||
let pendingCall; | ||
async function mutateAsync(vars) { | ||
status.value = "loading"; | ||
const promise = pendingPromise = options.mutation(...args).then((_data) => { | ||
if (pendingPromise === promise) { | ||
data.value = _data; | ||
let currentData; | ||
let currentError; | ||
let context; | ||
const currentCall = pendingCall = Symbol(); | ||
try { | ||
context = await options.onMutate?.(vars); | ||
const newData = currentData = await options.mutation( | ||
vars, | ||
context | ||
); | ||
await options.onSuccess?.({ data: newData, vars, ...context }); | ||
if (pendingCall === currentCall) { | ||
data.value = newData; | ||
error.value = null; | ||
status.value = "success"; | ||
if (options.keys) { | ||
const keys = typeof options.keys === "function" ? options.keys(_data, ...args) : options.keys; | ||
const keys = typeof options.keys === "function" ? options.keys(newData, vars) : options.keys; | ||
for (const key of keys) { | ||
@@ -332,12 +347,23 @@ store.invalidateEntry(key); | ||
} | ||
return _data; | ||
}).catch((_error) => { | ||
if (pendingPromise === promise) { | ||
error.value = _error; | ||
} catch (newError) { | ||
currentError = newError; | ||
await options.onError?.({ error: newError, vars, ...context }); | ||
if (pendingCall === currentCall) { | ||
error.value = newError; | ||
status.value = "error"; | ||
} | ||
throw _error; | ||
}); | ||
return promise; | ||
throw newError; | ||
} finally { | ||
await options.onSettled?.({ | ||
data: currentData, | ||
error: currentError, | ||
vars, | ||
...context | ||
}); | ||
} | ||
return currentData; | ||
} | ||
function mutate(vars) { | ||
mutateAsync(vars).catch(noop); | ||
} | ||
function reset() { | ||
@@ -348,3 +374,3 @@ data.value = void 0; | ||
} | ||
const mutationReturn = { | ||
return { | ||
data, | ||
@@ -354,18 +380,26 @@ isLoading: computed3(() => status.value === "loading"), | ||
error, | ||
// @ts-expect-error: it would be nice to find a type-only refactor that works | ||
mutate, | ||
// @ts-expect-error: it would be nice to find a type-only refactor that works | ||
mutateAsync, | ||
reset | ||
}; | ||
return mutationReturn; | ||
} | ||
// src/define-mutation.ts | ||
function defineMutation(optionsOrSetup) { | ||
return typeof optionsOrSetup === "function" ? optionsOrSetup : () => useMutation(optionsOrSetup); | ||
} | ||
// src/use-query.ts | ||
import { | ||
computed as computed4, | ||
getCurrentInstance, | ||
getCurrentScope as getCurrentScope3, | ||
onMounted, | ||
onScopeDispose as onScopeDispose2, | ||
onServerPrefetch, | ||
onUnmounted, | ||
toValue as toValue3, | ||
onScopeDispose as onScopeDispose2, | ||
getCurrentScope as getCurrentScope3, | ||
watch as watch2, | ||
getCurrentInstance | ||
watch as watch2 | ||
} from "vue"; | ||
@@ -375,3 +409,2 @@ | ||
import { inject } from "vue"; | ||
var DATA_TYPE_SYMBOL = Symbol(); | ||
var USE_QUERY_DEFAULTS = { | ||
@@ -385,3 +418,5 @@ staleTime: 1e3 * 5, | ||
refetchOnReconnect: true, | ||
refetchOnMount: true | ||
refetchOnMount: true, | ||
// as any to simplify the typing with generics | ||
transformError: (error) => error | ||
}; | ||
@@ -479,2 +514,8 @@ var USE_QUERY_OPTIONS_KEY = process.env.NODE_ENV !== "production" ? Symbol("useQueryOptions") : Symbol(); | ||
// src/define-query.ts | ||
function defineQuery(optionsOrSetup) { | ||
const setupFn = typeof optionsOrSetup === "function" ? optionsOrSetup : () => useQuery(optionsOrSetup); | ||
return () => useQueryCache().ensureDefinedQuery(setupFn); | ||
} | ||
// src/query-plugin.ts | ||
@@ -500,9 +541,9 @@ function QueryPlugin(app, { | ||
if (name === "refetch" || name === "refresh") { | ||
after(() => { | ||
onSuccess(); | ||
onSettled(); | ||
after(async (data) => { | ||
await onSuccess(data); | ||
onSettled(data, null); | ||
}); | ||
_onError(() => { | ||
onError(); | ||
onSettled(); | ||
_onError(async (error) => { | ||
await onError(error); | ||
onSettled(void 0, error); | ||
}); | ||
@@ -515,2 +556,4 @@ } | ||
TreeMapNode, | ||
defineMutation, | ||
defineQuery, | ||
delayLoadingRef, | ||
@@ -517,0 +560,0 @@ serialize, |
128
package.json
{ | ||
"name": "@pinia/colada", | ||
"type": "module", | ||
"version": "0.6.0", | ||
"packageManager": "pnpm@8.14.1", | ||
"version": "0.5.3", | ||
"type": "module", | ||
"description": "The smart data fetching layer for Pinia", | ||
@@ -10,17 +10,42 @@ "publishConfig": { | ||
}, | ||
"main": "./dist/index.cjs", | ||
"module": "./dist/index.js", | ||
"unpkg": "dist/index.global.js", | ||
"jsdelivr": "dist/index.global.js", | ||
"types": "./dist/index.d.ts", | ||
"author": { | ||
"name": "Eduardo San Martin Morote", | ||
"email": "posva13@gmail.com" | ||
}, | ||
"license": "MIT", | ||
"funding": "https://github.com/sponsors/posva", | ||
"homepage": "https://github.com/posva/pinia-colada#readme", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/posva/pinia-colada.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/posva/pinia-colada/issues" | ||
}, | ||
"keywords": [ | ||
"pinia", | ||
"plugin", | ||
"data", | ||
"fetching", | ||
"query", | ||
"mutation", | ||
"cache", | ||
"layer" | ||
], | ||
"sideEffects": false, | ||
"exports": { | ||
".": { | ||
"types": { | ||
"require": "./dist/index.d.cts", | ||
"import": "./dist/index.d.ts" | ||
"import": "./dist/index.d.ts", | ||
"require": "./dist/index.d.cts" | ||
}, | ||
"require": "./dist/index.cjs", | ||
"import": "./dist/index.js" | ||
"import": "./dist/index.js", | ||
"require": "./dist/index.cjs" | ||
} | ||
}, | ||
"main": "./dist/index.cjs", | ||
"module": "./dist/index.js", | ||
"unpkg": "dist/index.global.js", | ||
"jsdelivr": "dist/index.global.js", | ||
"types": "./dist/index.d.ts", | ||
"typesVersions": { | ||
@@ -34,12 +59,12 @@ "*": { | ||
}, | ||
"sideEffects": false, | ||
"funding": "https://github.com/sponsors/posva", | ||
"author": { | ||
"name": "Eduardo San Martin Morote", | ||
"email": "posva13@gmail.com" | ||
}, | ||
"files": [ | ||
"LICENSE", | ||
"README.md", | ||
"dist" | ||
], | ||
"scripts": { | ||
"dev": "vitest --ui --typecheck", | ||
"docs": "vitepress dev docs", | ||
"docs:build": "vitepress build docs", | ||
"docs:build": "pnpm run docs:api:build && vitepress build docs", | ||
"docs:api:build": "node ./scripts/run-typedoc.js", | ||
"play": "pnpm -C playground dev", | ||
@@ -49,4 +74,4 @@ "prepublishOnly": "pnpm run build", | ||
"test": "pnpm run test:cov && pnpm run test:types", | ||
"lint": "prettier -c --parser typescript \"src/**/*.[jt]s?(x)\"", | ||
"lint:fix": "pnpm run lint --write", | ||
"lint": "eslint .", | ||
"lint:fix": "eslint --fix .", | ||
"test:types": "tsc --build ./tsconfig.json", | ||
@@ -58,18 +83,2 @@ "test:cov": "vitest run --typecheck --coverage", | ||
}, | ||
"files": [ | ||
"dist", | ||
"LICENSE", | ||
"README.md" | ||
], | ||
"keywords": [ | ||
"pinia", | ||
"plugin", | ||
"data", | ||
"fetching", | ||
"query", | ||
"mutation", | ||
"cache", | ||
"layer" | ||
], | ||
"license": "MIT", | ||
"size-limit": [ | ||
@@ -96,20 +105,26 @@ { | ||
"devDependencies": { | ||
"@size-limit/preset-small-lib": "^11.0.2", | ||
"@types/node": "^20.11.19", | ||
"@antfu/eslint-config": "^2.8.3", | ||
"@pinia/colada": "workspace:*", | ||
"@shikijs/vitepress-twoslash": "^1.2.0", | ||
"@size-limit/preset-small-lib": "^11.1.1", | ||
"@types/node": "^20.11.30", | ||
"@vitejs/plugin-vue": "^5.0.4", | ||
"@vitest/coverage-v8": "^1.3.0", | ||
"@vitest/ui": "^1.3.0", | ||
"@vue/test-utils": "^2.4.4", | ||
"happy-dom": "^13.3.8", | ||
"@vitest/coverage-v8": "^1.4.0", | ||
"@vitest/ui": "^1.4.0", | ||
"@vue/test-utils": "^2.4.5", | ||
"eslint": "^8.57.0", | ||
"happy-dom": "^14.2.0", | ||
"lint-staged": "^15.2.2", | ||
"pinia": "^2.1.7", | ||
"prettier": "^3.2.5", | ||
"simple-git-hooks": "^2.9.0", | ||
"size-limit": "^11.0.2", | ||
"simple-git-hooks": "^2.11.0", | ||
"size-limit": "^11.1.1", | ||
"standard-version": "^9.5.0", | ||
"tsup": "^8.0.2", | ||
"typescript": "~5.3.3", | ||
"vitepress": "1.0.0-rc.44", | ||
"vitest": "^1.3.0", | ||
"vue": "^3.4.19" | ||
"typedoc": "^0.25.12", | ||
"typedoc-plugin-markdown": "^3.17.1", | ||
"typescript": "~5.4.3", | ||
"vitepress": "1.0.0-rc.45", | ||
"vitest": "^1.4.0", | ||
"vue": "^3.4.21" | ||
}, | ||
@@ -121,17 +136,6 @@ "simple-git-hooks": { | ||
"lint-staged": { | ||
"*.{js,mjs,json,cjs,md}": [ | ||
"prettier --write" | ||
], | ||
"*.ts?(x)": [ | ||
"prettier --parser=typescript --write" | ||
"*.{js,mjs,json,cjs,md,ts}": [ | ||
"eslint --fix" | ||
] | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/posva/pinia-colada.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/posva/pinia-colada/issues" | ||
}, | ||
"homepage": "https://github.com/posva/pinia-colada#readme" | ||
} | ||
} |
@@ -66,5 +66,5 @@ <p align="center"> | ||
<script lang="ts" setup> | ||
import { getContactById, updateContact as _updateContact } from '~/api/contacts' | ||
import { useRoute } from 'vue-router' | ||
import { useQuery, useMutation } from '@pinia/colada' | ||
import { useMutation, useQuery } from '@pinia/colada' | ||
import { updateContact as _updateContact, getContactById } from '~/api/contacts' | ||
@@ -71,0 +71,0 @@ const route = useRoute() |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
201895
1683
10
24