@zenstackhq/tanstack-query 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generator.js +70 -37
- package/generator.js.map +1 -1
- package/package.json +40 -9
- package/runtime/common-5e18c135.d.ts +32 -0
- package/runtime/index.d.mts +1 -1
- package/runtime/index.d.ts +1 -1
- package/runtime/index.js +34 -0
- package/runtime/index.js.map +1 -1
- package/runtime/index.mjs +15 -0
- package/runtime/index.mjs.map +1 -1
- package/runtime/react.d.mts +17 -26
- package/runtime/react.d.ts +17 -26
- package/runtime/react.js +105 -72
- package/runtime/react.js.map +1 -1
- package/runtime/react.mjs +102 -68
- package/runtime/react.mjs.map +1 -1
- package/runtime/svelte.d.mts +16 -25
- package/runtime/svelte.d.ts +16 -25
- package/runtime/svelte.js +97 -71
- package/runtime/svelte.js.map +1 -1
- package/runtime/svelte.mjs +93 -66
- package/runtime/svelte.mjs.map +1 -1
- package/runtime/vue.d.mts +17 -27
- package/runtime/vue.d.ts +17 -27
- package/runtime/vue.js +105 -77
- package/runtime/vue.js.map +1 -1
- package/runtime/vue.mjs +101 -71
- package/runtime/vue.mjs.map +1 -1
- package/runtime-v5/common-5e18c135.d.ts +32 -0
- package/runtime-v5/index.d.mts +20 -0
- package/runtime-v5/index.d.ts +20 -0
- package/runtime-v5/index.js +53 -0
- package/runtime-v5/index.js.map +1 -0
- package/runtime-v5/index.mjs +16 -0
- package/runtime-v5/index.mjs.map +1 -0
- package/runtime-v5/react.d.mts +45 -0
- package/runtime-v5/react.d.ts +45 -0
- package/runtime-v5/react.js +269 -0
- package/runtime-v5/react.js.map +1 -0
- package/runtime-v5/react.mjs +237 -0
- package/runtime-v5/react.mjs.map +1 -0
- package/runtime-v5/svelte.d.mts +56 -0
- package/runtime-v5/svelte.d.ts +56 -0
- package/runtime-v5/svelte.js +296 -0
- package/runtime-v5/svelte.js.map +1 -0
- package/runtime-v5/svelte.mjs +264 -0
- package/runtime-v5/svelte.mjs.map +1 -0
- package/runtime-v5/vue.d.mts +52 -0
- package/runtime-v5/vue.d.ts +52 -0
- package/runtime-v5/vue.js +274 -0
- package/runtime-v5/vue.js.map +1 -0
- package/runtime-v5/vue.mjs +240 -0
- package/runtime-v5/vue.mjs.map +1 -0
- package/runtime/common-83308e88.d.ts +0 -19
package/runtime/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/runtime/react.ts","../../src/runtime/common.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n useInfiniteQuery,\n useMutation,\n useQuery,\n useQueryClient,\n type MutateFunction,\n type QueryClient,\n type UseInfiniteQueryOptions,\n type UseMutationOptions,\n type UseQueryOptions,\n} from '@tanstack/react-query';\nimport { createContext } from 'react';\nimport {\n DEFAULT_QUERY_ENDPOINT,\n FetchFn,\n QUERY_KEY_PREFIX,\n fetcher,\n makeUrl,\n marshal,\n type APIContext,\n} from './common';\n\n/**\n * Context for configuring react hooks.\n */\nexport const RequestHandlerContext = createContext<APIContext>({\n endpoint: DEFAULT_QUERY_ENDPOINT,\n fetch: undefined,\n});\n\n/**\n * Context provider.\n */\nexport const Provider = RequestHandlerContext.Provider;\n\n/**\n * Creates a react-query query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The react-query options object\n * @returns useQuery hook\n */\nexport function query<R>(model: string, url: string, args?: unknown, options?: UseQueryOptions<R>, fetch?: FetchFn) {\n const reqUrl = makeUrl(url, args);\n return useQuery<R>({\n queryKey: [QUERY_KEY_PREFIX + model, url, args],\n queryFn: () => fetcher<R, false>(reqUrl, undefined, fetch, false),\n ...options,\n });\n}\n\n/**\n * Creates a react-query infinite query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The initial request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The react-query infinite query options object\n * @returns useInfiniteQuery hook\n */\nexport function infiniteQuery<R>(\n model: string,\n url: string,\n args?: unknown,\n options?: UseInfiniteQueryOptions<R>,\n fetch?: FetchFn\n) {\n return useInfiniteQuery<R>({\n queryKey: [QUERY_KEY_PREFIX + model, url, args],\n queryFn: ({ pageParam }) => {\n return fetcher<R, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false);\n },\n ...options,\n });\n}\n\n/**\n * Creates a POST mutation with react-query.\n *\n * @param model The name of the model under mutation.\n * @param url The request URL.\n * @param options The react-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function postMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n url: string,\n options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) =>\n fetcher<R, C>(\n url,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n },\n body: marshal(data),\n },\n fetch,\n checkReadBack\n ) as Promise<Result>;\n\n const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);\n const mutation = useMutation<Result, unknown, T>(finalOptions);\n return mutation;\n}\n\n/**\n * Creates a PUT mutation with react-query.\n *\n * @param model The name of the model under mutation.\n * @param url The request URL.\n * @param options The react-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function putMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n url: string,\n options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) =>\n fetcher<R, C>(\n url,\n {\n method: 'PUT',\n headers: {\n 'content-type': 'application/json',\n },\n body: marshal(data),\n },\n fetch,\n checkReadBack\n ) as Promise<Result>;\n\n const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);\n const mutation = useMutation<Result, unknown, T>(finalOptions);\n return mutation;\n}\n\n/**\n * Creates a DELETE mutation with react-query.\n *\n * @param model The name of the model under mutation.\n * @param url The request URL.\n * @param options The react-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function deleteMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n url: string,\n options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) =>\n fetcher<R, C>(\n makeUrl(url, data),\n {\n method: 'DELETE',\n },\n fetch,\n checkReadBack\n ) as Promise<Result>;\n\n const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);\n const mutation = useMutation<Result, unknown, T>(finalOptions);\n return mutation;\n}\n\nfunction mergeOptions<T, R = any>(\n model: string,\n options: Omit<UseMutationOptions<R, unknown, T, unknown>, 'mutationFn'> | undefined,\n invalidateQueries: boolean,\n mutationFn: MutateFunction<R, unknown, T>,\n queryClient: QueryClient\n): UseMutationOptions<R, unknown, T, unknown> {\n const result = { ...options, mutationFn };\n if (options?.onSuccess || invalidateQueries) {\n result.onSuccess = (...args) => {\n if (invalidateQueries) {\n queryClient.invalidateQueries([QUERY_KEY_PREFIX + model]);\n }\n return options?.onSuccess?.(...args);\n };\n }\n return result;\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport * as crossFetch from 'cross-fetch';\n\n/**\n * The default query endpoint.\n */\nexport const DEFAULT_QUERY_ENDPOINT = '/api/model';\n\n/**\n * Prefix for react-query keys.\n */\nexport const QUERY_KEY_PREFIX = 'zenstack:';\n\n/**\n * Function signature for `fetch`.\n */\nexport type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;\n\n/**\n * Context type for configuring the hooks.\n */\nexport type APIContext = {\n /**\n * The endpoint to use for the queries.\n */\n endpoint?: string;\n\n /**\n * A custom fetch function for sending the HTTP requests.\n */\n fetch?: FetchFn;\n};\n\nexport async function fetcher<R, C extends boolean>(\n url: string,\n options?: RequestInit,\n fetch?: FetchFn,\n checkReadBack?: C\n): Promise<C extends true ? R | undefined : R> {\n const _fetch = fetch ?? crossFetch.fetch;\n const res = await _fetch(url, options);\n if (!res.ok) {\n const errData = unmarshal(await res.text());\n if (\n checkReadBack !== false &&\n errData.error?.prisma &&\n errData.error?.code === 'P2004' &&\n errData.error?.reason === 'RESULT_NOT_READABLE'\n ) {\n // policy doesn't allow mutation result to be read back, just return undefined\n return undefined as any;\n }\n const error: Error & { info?: unknown; status?: number } = new Error(\n 'An error occurred while fetching the data.'\n );\n error.info = errData.error;\n error.status = res.status;\n throw error;\n }\n\n const textResult = await res.text();\n try {\n return unmarshal(textResult).data as R;\n } catch (err) {\n console.error(`Unable to deserialize data:`, textResult);\n throw err;\n }\n}\n\nexport function marshal(value: unknown) {\n const { data, meta } = serialize(value);\n if (meta) {\n return JSON.stringify({ ...(data as any), meta: { serialization: meta } });\n } else {\n return JSON.stringify(data);\n }\n}\n\nexport function unmarshal(value: string) {\n const parsed = JSON.parse(value);\n if (parsed.data && parsed.meta?.serialization) {\n const deserializedData = deserialize(parsed.data, parsed.meta.serialization);\n return { ...parsed, data: deserializedData };\n } else {\n return parsed;\n }\n}\n\nexport function makeUrl(url: string, args: unknown) {\n if (!args) {\n return url;\n }\n\n const { data, meta } = serialize(args);\n let result = `${url}?q=${encodeURIComponent(JSON.stringify(data))}`;\n if (meta) {\n result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;\n }\n return result;\n}\n"],"mappings":"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","names":["fetch","fetch"]}
|
|
1
|
+
{"version":3,"sources":["../../src/runtime/react.ts","../../src/runtime/common.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n useInfiniteQuery,\n useMutation,\n useQuery,\n useQueryClient,\n type UseInfiniteQueryOptions,\n type UseMutationOptions,\n type UseQueryOptions,\n} from '@tanstack/react-query';\nimport type { ModelMeta } from '@zenstackhq/runtime/cross';\nimport { createContext, useContext } from 'react';\nimport {\n DEFAULT_QUERY_ENDPOINT,\n FetchFn,\n fetcher,\n getQueryKey,\n makeUrl,\n marshal,\n setupInvalidation,\n type APIContext,\n} from './common';\n\n/**\n * Context for configuring react hooks.\n */\nexport const RequestHandlerContext = createContext<APIContext>({\n endpoint: DEFAULT_QUERY_ENDPOINT,\n fetch: undefined,\n logging: false,\n});\n\n/**\n * Context provider.\n */\nexport const Provider = RequestHandlerContext.Provider;\n\n/**\n * Hooks context.\n */\nexport function getHooksContext() {\n const { endpoint, ...rest } = useContext(RequestHandlerContext);\n return { endpoint: endpoint ?? DEFAULT_QUERY_ENDPOINT, ...rest };\n}\n\n/**\n * Creates a react-query query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The react-query options object\n * @returns useQuery hook\n */\nexport function useModelQuery<R>(\n model: string,\n url: string,\n args?: unknown,\n options?: Omit<UseQueryOptions<R>, 'queryKey'>,\n fetch?: FetchFn\n) {\n const reqUrl = makeUrl(url, args);\n return useQuery<R>({\n queryKey: getQueryKey(model, url, args),\n queryFn: () => fetcher<R, false>(reqUrl, undefined, fetch, false),\n ...options,\n });\n}\n\n/**\n * Creates a react-query infinite query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The initial request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The react-query infinite query options object\n * @returns useInfiniteQuery hook\n */\nexport function useInfiniteModelQuery<R>(\n model: string,\n url: string,\n args?: unknown,\n options?: Omit<UseInfiniteQueryOptions<R>, 'queryKey'>,\n fetch?: FetchFn\n) {\n return useInfiniteQuery<R>({\n queryKey: getQueryKey(model, url, args),\n queryFn: ({ pageParam }) => {\n return fetcher<R, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false);\n },\n ...options,\n });\n}\n\n/**\n * Creates a mutation with react-query.\n *\n * @param model The name of the model under mutation.\n * @param method The HTTP method.\n * @param modelMeta The model metadata.\n * @param url The request URL.\n * @param options The react-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function useModelMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n method: 'POST' | 'PUT' | 'DELETE',\n url: string,\n modelMeta: ModelMeta,\n options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) => {\n const reqUrl = method === 'DELETE' ? makeUrl(url, data) : url;\n const fetchInit: RequestInit = {\n method,\n ...(method !== 'DELETE' && {\n headers: {\n 'content-type': 'application/json',\n },\n body: marshal(data),\n }),\n };\n return fetcher<R, C>(reqUrl, fetchInit, fetch, checkReadBack) as Promise<Result>;\n };\n\n const finalOptions = { ...options, mutationFn };\n if (invalidateQueries) {\n const { logging } = useContext(RequestHandlerContext);\n const operation = url.split('/').pop();\n if (operation) {\n setupInvalidation(\n model,\n operation,\n modelMeta,\n finalOptions,\n (predicate) => queryClient.invalidateQueries({ predicate }),\n logging\n );\n }\n }\n\n return useMutation(finalOptions);\n}\n","/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport { getMutatedModels, getReadModels, type ModelMeta, type PrismaWriteActionType } from '@zenstackhq/runtime/cross';\nimport * as crossFetch from 'cross-fetch';\n\n/**\n * The default query endpoint.\n */\nexport const DEFAULT_QUERY_ENDPOINT = '/api/model';\n\n/**\n * Prefix for react-query keys.\n */\nexport const QUERY_KEY_PREFIX = 'zenstack';\n\n/**\n * Function signature for `fetch`.\n */\nexport type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;\n\n/**\n * Context type for configuring the hooks.\n */\nexport type APIContext = {\n /**\n * The endpoint to use for the queries.\n */\n endpoint?: string;\n\n /**\n * A custom fetch function for sending the HTTP requests.\n */\n fetch?: FetchFn;\n\n /**\n * If logging is enabled.\n */\n logging?: boolean;\n};\n\nexport async function fetcher<R, C extends boolean>(\n url: string,\n options?: RequestInit,\n fetch?: FetchFn,\n checkReadBack?: C\n): Promise<C extends true ? R | undefined : R> {\n const _fetch = fetch ?? crossFetch.fetch;\n const res = await _fetch(url, options);\n if (!res.ok) {\n const errData = unmarshal(await res.text());\n if (\n checkReadBack !== false &&\n errData.error?.prisma &&\n errData.error?.code === 'P2004' &&\n errData.error?.reason === 'RESULT_NOT_READABLE'\n ) {\n // policy doesn't allow mutation result to be read back, just return undefined\n return undefined as any;\n }\n const error: Error & { info?: unknown; status?: number } = new Error(\n 'An error occurred while fetching the data.'\n );\n error.info = errData.error;\n error.status = res.status;\n throw error;\n }\n\n const textResult = await res.text();\n try {\n return unmarshal(textResult).data as R;\n } catch (err) {\n console.error(`Unable to deserialize data:`, textResult);\n throw err;\n }\n}\n\ntype QueryKey = [string /* prefix */, string /* model */, string /* operation */, unknown /* args */];\n\n/**\n * Computes query key for the given model, operation and query args.\n * @param model Model name.\n * @param urlOrOperation Prisma operation (e.g, `findMany`) or request URL. If it's a URL, the last path segment will be used as the operation name.\n * @param args Prisma query arguments.\n * @returns Query key\n */\nexport function getQueryKey(model: string, urlOrOperation: string, args: unknown): QueryKey {\n if (!urlOrOperation) {\n throw new Error('Invalid urlOrOperation');\n }\n const operation = urlOrOperation.split('/').pop();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return [QUERY_KEY_PREFIX, model, operation!, args];\n}\n\nexport function marshal(value: unknown) {\n const { data, meta } = serialize(value);\n if (meta) {\n return JSON.stringify({ ...(data as any), meta: { serialization: meta } });\n } else {\n return JSON.stringify(data);\n }\n}\n\nexport function unmarshal(value: string) {\n const parsed = JSON.parse(value);\n if (parsed.data && parsed.meta?.serialization) {\n const deserializedData = deserialize(parsed.data, parsed.meta.serialization);\n return { ...parsed, data: deserializedData };\n } else {\n return parsed;\n }\n}\n\nexport function makeUrl(url: string, args: unknown) {\n if (!args) {\n return url;\n }\n\n const { data, meta } = serialize(args);\n let result = `${url}?q=${encodeURIComponent(JSON.stringify(data))}`;\n if (meta) {\n result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;\n }\n return result;\n}\n\ntype InvalidationPredicate = ({ queryKey }: { queryKey: readonly unknown[] }) => boolean;\n\n// sets up invalidation hook for a mutation\nexport function setupInvalidation(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: { onSuccess?: (...args: any[]) => any },\n invalidate: (predicate: InvalidationPredicate) => Promise<void>,\n logging = false\n) {\n const origOnSuccess = options?.onSuccess;\n options.onSuccess = async (...args: unknown[]) => {\n const [_, variables] = args;\n const predicate = await getInvalidationPredicate(\n model,\n operation as PrismaWriteActionType,\n variables,\n modelMeta,\n logging\n );\n await invalidate(predicate);\n return origOnSuccess?.(...args);\n };\n}\n\n// gets a predicate for evaluating whether a query should be invalidated\nasync function getInvalidationPredicate(\n model: string,\n operation: PrismaWriteActionType,\n mutationArgs: any,\n modelMeta: ModelMeta,\n logging = false\n) {\n const mutatedModels = await getMutatedModels(model, operation, mutationArgs, modelMeta);\n\n return ({ queryKey }: { queryKey: readonly unknown[] }) => {\n const [_model, queryModel, queryOp, args] = queryKey as QueryKey;\n\n if (mutatedModels.includes(queryModel)) {\n // direct match\n if (logging) {\n console.log(`Invalidating query [${queryKey}] due to mutation \"${model}.${operation}\"`);\n }\n return true;\n }\n\n if (args) {\n // traverse query args to find nested reads that match the model under mutation\n if (findNestedRead(queryModel, mutatedModels, modelMeta, args)) {\n if (logging) {\n console.log(`Invalidating query [${queryKey}] due to mutation \"${model}.${operation}\"`);\n }\n return true;\n }\n }\n\n return false;\n };\n}\n\n// find nested reads that match the given models\nfunction findNestedRead(visitingModel: string, targetModels: string[], modelMeta: ModelMeta, args: any) {\n const modelsRead = getReadModels(visitingModel, modelMeta, args);\n return targetModels.some((m) => modelsRead.includes(m));\n}\n"],"mappings":"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","names":["fetch","fetch"]}
|
package/runtime/svelte.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as _tanstack_svelte_query from '@tanstack/svelte-query';
|
|
2
2
|
import { QueryOptions, CreateInfiniteQueryOptions, MutationOptions } from '@tanstack/svelte-query';
|
|
3
|
-
import {
|
|
3
|
+
import { ModelMeta } from '@zenstackhq/runtime/cross';
|
|
4
|
+
import { A as APIContext, F as FetchFn } from './common-5e18c135.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Key for setting and getting the global query context.
|
|
@@ -10,6 +11,14 @@ declare const SvelteQueryContextKey = "zenstack-svelte-query-context";
|
|
|
10
11
|
* Set context for the generated TanStack Query hooks.
|
|
11
12
|
*/
|
|
12
13
|
declare function setHooksContext(context: APIContext): void;
|
|
14
|
+
/**
|
|
15
|
+
* Hooks context.
|
|
16
|
+
*/
|
|
17
|
+
declare function getHooksContext(): {
|
|
18
|
+
fetch?: FetchFn | undefined;
|
|
19
|
+
logging?: boolean | undefined;
|
|
20
|
+
endpoint: string;
|
|
21
|
+
};
|
|
13
22
|
/**
|
|
14
23
|
* Creates a svelte-query query.
|
|
15
24
|
*
|
|
@@ -19,7 +28,7 @@ declare function setHooksContext(context: APIContext): void;
|
|
|
19
28
|
* @param options The svelte-query options object
|
|
20
29
|
* @returns useQuery hook
|
|
21
30
|
*/
|
|
22
|
-
declare function
|
|
31
|
+
declare function useModelQuery<R>(model: string, url: string, args?: unknown, options?: Omit<QueryOptions<R>, 'queryKey'>, fetch?: FetchFn): _tanstack_svelte_query.CreateQueryResult<R, unknown>;
|
|
23
32
|
/**
|
|
24
33
|
* Creates a svelte-query infinite query.
|
|
25
34
|
*
|
|
@@ -29,36 +38,18 @@ declare function query<R>(model: string, url: string, args?: unknown, options?:
|
|
|
29
38
|
* @param options The svelte-query infinite query options object
|
|
30
39
|
* @returns useQuery hook
|
|
31
40
|
*/
|
|
32
|
-
declare function
|
|
41
|
+
declare function useInfiniteModelQuery<R>(model: string, url: string, args?: unknown, options?: Omit<CreateInfiniteQueryOptions<R>, 'queryKey'>, fetch?: FetchFn): _tanstack_svelte_query.CreateInfiniteQueryResult<R, unknown>;
|
|
33
42
|
/**
|
|
34
43
|
* Creates a POST mutation with svelte-query.
|
|
35
44
|
*
|
|
36
45
|
* @param model The name of the model under mutation.
|
|
46
|
+
* @param method The HTTP method.
|
|
47
|
+
* @param modelMeta The model metadata.
|
|
37
48
|
* @param url The request URL.
|
|
38
49
|
* @param options The svelte-query options.
|
|
39
50
|
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
40
51
|
* @returns useMutation hooks
|
|
41
52
|
*/
|
|
42
|
-
declare function
|
|
43
|
-
/**
|
|
44
|
-
* Creates a PUT mutation with svelte-query.
|
|
45
|
-
*
|
|
46
|
-
* @param model The name of the model under mutation.
|
|
47
|
-
* @param url The request URL.
|
|
48
|
-
* @param options The svelte-query options.
|
|
49
|
-
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
50
|
-
* @returns useMutation hooks
|
|
51
|
-
*/
|
|
52
|
-
declare function putMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, url: string, options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_svelte_query.CreateMutationResult<Result, unknown, T, unknown>;
|
|
53
|
-
/**
|
|
54
|
-
* Creates a DELETE mutation with svelte-query.
|
|
55
|
-
*
|
|
56
|
-
* @param model The name of the model under mutation.
|
|
57
|
-
* @param url The request URL.
|
|
58
|
-
* @param options The svelte-query options.
|
|
59
|
-
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
60
|
-
* @returns useMutation hooks
|
|
61
|
-
*/
|
|
62
|
-
declare function deleteMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, url: string, options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_svelte_query.CreateMutationResult<Result, unknown, T, unknown>;
|
|
53
|
+
declare function useModelMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, method: 'POST' | 'PUT' | 'DELETE', url: string, modelMeta: ModelMeta, options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_svelte_query.CreateMutationResult<Result, unknown, T, unknown>;
|
|
63
54
|
|
|
64
|
-
export { APIContext as RequestHandlerContext, SvelteQueryContextKey,
|
|
55
|
+
export { APIContext as RequestHandlerContext, SvelteQueryContextKey, getHooksContext, setHooksContext, useInfiniteModelQuery, useModelMutation, useModelQuery };
|
package/runtime/svelte.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as _tanstack_svelte_query from '@tanstack/svelte-query';
|
|
2
2
|
import { QueryOptions, CreateInfiniteQueryOptions, MutationOptions } from '@tanstack/svelte-query';
|
|
3
|
-
import {
|
|
3
|
+
import { ModelMeta } from '@zenstackhq/runtime/cross';
|
|
4
|
+
import { A as APIContext, F as FetchFn } from './common-5e18c135.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Key for setting and getting the global query context.
|
|
@@ -10,6 +11,14 @@ declare const SvelteQueryContextKey = "zenstack-svelte-query-context";
|
|
|
10
11
|
* Set context for the generated TanStack Query hooks.
|
|
11
12
|
*/
|
|
12
13
|
declare function setHooksContext(context: APIContext): void;
|
|
14
|
+
/**
|
|
15
|
+
* Hooks context.
|
|
16
|
+
*/
|
|
17
|
+
declare function getHooksContext(): {
|
|
18
|
+
fetch?: FetchFn | undefined;
|
|
19
|
+
logging?: boolean | undefined;
|
|
20
|
+
endpoint: string;
|
|
21
|
+
};
|
|
13
22
|
/**
|
|
14
23
|
* Creates a svelte-query query.
|
|
15
24
|
*
|
|
@@ -19,7 +28,7 @@ declare function setHooksContext(context: APIContext): void;
|
|
|
19
28
|
* @param options The svelte-query options object
|
|
20
29
|
* @returns useQuery hook
|
|
21
30
|
*/
|
|
22
|
-
declare function
|
|
31
|
+
declare function useModelQuery<R>(model: string, url: string, args?: unknown, options?: Omit<QueryOptions<R>, 'queryKey'>, fetch?: FetchFn): _tanstack_svelte_query.CreateQueryResult<R, unknown>;
|
|
23
32
|
/**
|
|
24
33
|
* Creates a svelte-query infinite query.
|
|
25
34
|
*
|
|
@@ -29,36 +38,18 @@ declare function query<R>(model: string, url: string, args?: unknown, options?:
|
|
|
29
38
|
* @param options The svelte-query infinite query options object
|
|
30
39
|
* @returns useQuery hook
|
|
31
40
|
*/
|
|
32
|
-
declare function
|
|
41
|
+
declare function useInfiniteModelQuery<R>(model: string, url: string, args?: unknown, options?: Omit<CreateInfiniteQueryOptions<R>, 'queryKey'>, fetch?: FetchFn): _tanstack_svelte_query.CreateInfiniteQueryResult<R, unknown>;
|
|
33
42
|
/**
|
|
34
43
|
* Creates a POST mutation with svelte-query.
|
|
35
44
|
*
|
|
36
45
|
* @param model The name of the model under mutation.
|
|
46
|
+
* @param method The HTTP method.
|
|
47
|
+
* @param modelMeta The model metadata.
|
|
37
48
|
* @param url The request URL.
|
|
38
49
|
* @param options The svelte-query options.
|
|
39
50
|
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
40
51
|
* @returns useMutation hooks
|
|
41
52
|
*/
|
|
42
|
-
declare function
|
|
43
|
-
/**
|
|
44
|
-
* Creates a PUT mutation with svelte-query.
|
|
45
|
-
*
|
|
46
|
-
* @param model The name of the model under mutation.
|
|
47
|
-
* @param url The request URL.
|
|
48
|
-
* @param options The svelte-query options.
|
|
49
|
-
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
50
|
-
* @returns useMutation hooks
|
|
51
|
-
*/
|
|
52
|
-
declare function putMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, url: string, options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_svelte_query.CreateMutationResult<Result, unknown, T, unknown>;
|
|
53
|
-
/**
|
|
54
|
-
* Creates a DELETE mutation with svelte-query.
|
|
55
|
-
*
|
|
56
|
-
* @param model The name of the model under mutation.
|
|
57
|
-
* @param url The request URL.
|
|
58
|
-
* @param options The svelte-query options.
|
|
59
|
-
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
60
|
-
* @returns useMutation hooks
|
|
61
|
-
*/
|
|
62
|
-
declare function deleteMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, url: string, options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_svelte_query.CreateMutationResult<Result, unknown, T, unknown>;
|
|
53
|
+
declare function useModelMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, method: 'POST' | 'PUT' | 'DELETE', url: string, modelMeta: ModelMeta, options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_svelte_query.CreateMutationResult<Result, unknown, T, unknown>;
|
|
63
54
|
|
|
64
|
-
export { APIContext as RequestHandlerContext, SvelteQueryContextKey,
|
|
55
|
+
export { APIContext as RequestHandlerContext, SvelteQueryContextKey, getHooksContext, setHooksContext, useInfiniteModelQuery, useModelMutation, useModelQuery };
|
package/runtime/svelte.js
CHANGED
|
@@ -22,6 +22,18 @@ var __spreadValues = (a, b) => {
|
|
|
22
22
|
return a;
|
|
23
23
|
};
|
|
24
24
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
25
|
+
var __objRest = (source, exclude) => {
|
|
26
|
+
var target = {};
|
|
27
|
+
for (var prop in source)
|
|
28
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
29
|
+
target[prop] = source[prop];
|
|
30
|
+
if (source != null && __getOwnPropSymbols)
|
|
31
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
32
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
33
|
+
target[prop] = source[prop];
|
|
34
|
+
}
|
|
35
|
+
return target;
|
|
36
|
+
};
|
|
25
37
|
var __export = (target, all) => {
|
|
26
38
|
for (var name in all)
|
|
27
39
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -68,12 +80,11 @@ var __async = (__this, __arguments, generator) => {
|
|
|
68
80
|
var svelte_exports = {};
|
|
69
81
|
__export(svelte_exports, {
|
|
70
82
|
SvelteQueryContextKey: () => SvelteQueryContextKey,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
setHooksContext: () => setHooksContext
|
|
83
|
+
getHooksContext: () => getHooksContext,
|
|
84
|
+
setHooksContext: () => setHooksContext,
|
|
85
|
+
useInfiniteModelQuery: () => useInfiniteModelQuery,
|
|
86
|
+
useModelMutation: () => useModelMutation,
|
|
87
|
+
useModelQuery: () => useModelQuery
|
|
77
88
|
});
|
|
78
89
|
module.exports = __toCommonJS(svelte_exports);
|
|
79
90
|
var import_svelte_query = require("@tanstack/svelte-query");
|
|
@@ -81,8 +92,10 @@ var import_svelte = require("svelte");
|
|
|
81
92
|
|
|
82
93
|
// src/runtime/common.ts
|
|
83
94
|
var import_browser = require("@zenstackhq/runtime/browser");
|
|
95
|
+
var import_cross = require("@zenstackhq/runtime/cross");
|
|
84
96
|
var crossFetch = __toESM(require("cross-fetch"));
|
|
85
|
-
var
|
|
97
|
+
var DEFAULT_QUERY_ENDPOINT = "/api/model";
|
|
98
|
+
var QUERY_KEY_PREFIX = "zenstack";
|
|
86
99
|
function fetcher(url, options, fetch2, checkReadBack) {
|
|
87
100
|
return __async(this, null, function* () {
|
|
88
101
|
var _a, _b, _c;
|
|
@@ -138,96 +151,109 @@ function makeUrl(url, args) {
|
|
|
138
151
|
}
|
|
139
152
|
return result;
|
|
140
153
|
}
|
|
154
|
+
function setupInvalidation(model, operation, modelMeta, options, invalidate, logging = false) {
|
|
155
|
+
const origOnSuccess = options == null ? void 0 : options.onSuccess;
|
|
156
|
+
options.onSuccess = (...args) => __async(this, null, function* () {
|
|
157
|
+
const [_, variables] = args;
|
|
158
|
+
const predicate = yield getInvalidationPredicate(
|
|
159
|
+
model,
|
|
160
|
+
operation,
|
|
161
|
+
variables,
|
|
162
|
+
modelMeta,
|
|
163
|
+
logging
|
|
164
|
+
);
|
|
165
|
+
yield invalidate(predicate);
|
|
166
|
+
return origOnSuccess == null ? void 0 : origOnSuccess(...args);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function getInvalidationPredicate(model, operation, mutationArgs, modelMeta, logging = false) {
|
|
170
|
+
return __async(this, null, function* () {
|
|
171
|
+
const mutatedModels = yield (0, import_cross.getMutatedModels)(model, operation, mutationArgs, modelMeta);
|
|
172
|
+
return ({ queryKey }) => {
|
|
173
|
+
const [_model, queryModel, queryOp, args] = queryKey;
|
|
174
|
+
if (mutatedModels.includes(queryModel)) {
|
|
175
|
+
if (logging) {
|
|
176
|
+
console.log(`Invalidating query [${queryKey}] due to mutation "${model}.${operation}"`);
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
if (args) {
|
|
181
|
+
if (findNestedRead(queryModel, mutatedModels, modelMeta, args)) {
|
|
182
|
+
if (logging) {
|
|
183
|
+
console.log(`Invalidating query [${queryKey}] due to mutation "${model}.${operation}"`);
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function findNestedRead(visitingModel, targetModels, modelMeta, args) {
|
|
193
|
+
const modelsRead = (0, import_cross.getReadModels)(visitingModel, modelMeta, args);
|
|
194
|
+
return targetModels.some((m) => modelsRead.includes(m));
|
|
195
|
+
}
|
|
141
196
|
|
|
142
197
|
// src/runtime/svelte.ts
|
|
143
198
|
var SvelteQueryContextKey = "zenstack-svelte-query-context";
|
|
144
199
|
function setHooksContext(context) {
|
|
145
200
|
(0, import_svelte.setContext)(SvelteQueryContextKey, context);
|
|
146
201
|
}
|
|
147
|
-
function
|
|
202
|
+
function getHooksContext() {
|
|
203
|
+
const _a = (0, import_svelte.getContext)(SvelteQueryContextKey), { endpoint } = _a, rest = __objRest(_a, ["endpoint"]);
|
|
204
|
+
return __spreadValues({ endpoint: endpoint != null ? endpoint : DEFAULT_QUERY_ENDPOINT }, rest);
|
|
205
|
+
}
|
|
206
|
+
function useModelQuery(model, url, args, options, fetch2) {
|
|
148
207
|
const reqUrl = makeUrl(url, args);
|
|
149
208
|
return (0, import_svelte_query.createQuery)(__spreadValues({
|
|
150
209
|
queryKey: [QUERY_KEY_PREFIX + model, url, args],
|
|
151
210
|
queryFn: () => fetcher(reqUrl, void 0, fetch2, false)
|
|
152
211
|
}, options));
|
|
153
212
|
}
|
|
154
|
-
function
|
|
213
|
+
function useInfiniteModelQuery(model, url, args, options, fetch2) {
|
|
155
214
|
return (0, import_svelte_query.createInfiniteQuery)(__spreadValues({
|
|
156
215
|
queryKey: [QUERY_KEY_PREFIX + model, url, args],
|
|
157
216
|
queryFn: ({ pageParam }) => fetcher(makeUrl(url, pageParam != null ? pageParam : args), void 0, fetch2, false)
|
|
158
217
|
}, options));
|
|
159
218
|
}
|
|
160
|
-
function
|
|
161
|
-
const queryClient = (0, import_svelte_query.useQueryClient)();
|
|
162
|
-
const mutationFn = (data) => fetcher(
|
|
163
|
-
url,
|
|
164
|
-
{
|
|
165
|
-
method: "POST",
|
|
166
|
-
headers: {
|
|
167
|
-
"content-type": "application/json"
|
|
168
|
-
},
|
|
169
|
-
body: marshal(data)
|
|
170
|
-
},
|
|
171
|
-
fetch2,
|
|
172
|
-
checkReadBack
|
|
173
|
-
);
|
|
174
|
-
const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
|
|
175
|
-
const mutation = (0, import_svelte_query.createMutation)(finalOptions);
|
|
176
|
-
return mutation;
|
|
177
|
-
}
|
|
178
|
-
function putMutation(model, url, options, fetch2, invalidateQueries = true, checkReadBack) {
|
|
219
|
+
function useModelMutation(model, method, url, modelMeta, options, fetch2, invalidateQueries = true, checkReadBack) {
|
|
179
220
|
const queryClient = (0, import_svelte_query.useQueryClient)();
|
|
180
|
-
const mutationFn = (data) =>
|
|
181
|
-
url,
|
|
182
|
-
{
|
|
183
|
-
method
|
|
221
|
+
const mutationFn = (data) => {
|
|
222
|
+
const reqUrl = method === "DELETE" ? makeUrl(url, data) : url;
|
|
223
|
+
const fetchInit = __spreadValues({
|
|
224
|
+
method
|
|
225
|
+
}, method !== "DELETE" && {
|
|
184
226
|
headers: {
|
|
185
227
|
"content-type": "application/json"
|
|
186
228
|
},
|
|
187
229
|
body: marshal(data)
|
|
188
|
-
}
|
|
189
|
-
fetch2,
|
|
190
|
-
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
);
|
|
206
|
-
const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
|
|
207
|
-
const mutation = (0, import_svelte_query.createMutation)(finalOptions);
|
|
208
|
-
return mutation;
|
|
209
|
-
}
|
|
210
|
-
function mergeOptions(model, options, invalidateQueries, mutationFn, queryClient) {
|
|
211
|
-
const result = __spreadProps(__spreadValues({}, options), { mutationFn });
|
|
212
|
-
if ((options == null ? void 0 : options.onSuccess) || invalidateQueries) {
|
|
213
|
-
result.onSuccess = (...args) => {
|
|
214
|
-
var _a;
|
|
215
|
-
if (invalidateQueries) {
|
|
216
|
-
queryClient.invalidateQueries([QUERY_KEY_PREFIX + model]);
|
|
217
|
-
}
|
|
218
|
-
return (_a = options == null ? void 0 : options.onSuccess) == null ? void 0 : _a.call(options, ...args);
|
|
219
|
-
};
|
|
230
|
+
});
|
|
231
|
+
return fetcher(reqUrl, fetchInit, fetch2, checkReadBack);
|
|
232
|
+
};
|
|
233
|
+
const finalOptions = __spreadProps(__spreadValues({}, options), { mutationFn });
|
|
234
|
+
if (invalidateQueries) {
|
|
235
|
+
const { logging } = (0, import_svelte.getContext)(SvelteQueryContextKey);
|
|
236
|
+
const operation = url.split("/").pop();
|
|
237
|
+
if (operation) {
|
|
238
|
+
setupInvalidation(
|
|
239
|
+
model,
|
|
240
|
+
operation,
|
|
241
|
+
modelMeta,
|
|
242
|
+
finalOptions,
|
|
243
|
+
(predicate) => queryClient.invalidateQueries({ predicate }),
|
|
244
|
+
logging
|
|
245
|
+
);
|
|
246
|
+
}
|
|
220
247
|
}
|
|
221
|
-
return
|
|
248
|
+
return (0, import_svelte_query.createMutation)(finalOptions);
|
|
222
249
|
}
|
|
223
250
|
// Annotate the CommonJS export names for ESM import in node:
|
|
224
251
|
0 && (module.exports = {
|
|
225
252
|
SvelteQueryContextKey,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
setHooksContext
|
|
253
|
+
getHooksContext,
|
|
254
|
+
setHooksContext,
|
|
255
|
+
useInfiniteModelQuery,
|
|
256
|
+
useModelMutation,
|
|
257
|
+
useModelQuery
|
|
232
258
|
});
|
|
233
259
|
//# sourceMappingURL=svelte.js.map
|
package/runtime/svelte.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/runtime/svelte.ts","../../src/runtime/common.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n createInfiniteQuery,\n createMutation,\n createQuery,\n useQueryClient,\n type CreateInfiniteQueryOptions,\n type MutateFunction,\n type MutationOptions,\n type QueryClient,\n type QueryOptions,\n} from '@tanstack/svelte-query';\nimport { setContext } from 'svelte';\nimport { APIContext, FetchFn, QUERY_KEY_PREFIX, fetcher, makeUrl, marshal } from './common';\n\nexport { APIContext as RequestHandlerContext } from './common';\n\n/**\n * Key for setting and getting the global query context.\n */\nexport const SvelteQueryContextKey = 'zenstack-svelte-query-context';\n\n/**\n * Set context for the generated TanStack Query hooks.\n */\nexport function setHooksContext(context: APIContext) {\n setContext(SvelteQueryContextKey, context);\n}\n\n/**\n * Creates a svelte-query query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The svelte-query options object\n * @returns useQuery hook\n */\nexport function query<R>(model: string, url: string, args?: unknown, options?: QueryOptions<R>, fetch?: FetchFn) {\n const reqUrl = makeUrl(url, args);\n return createQuery<R>({\n queryKey: [QUERY_KEY_PREFIX + model, url, args],\n queryFn: () => fetcher<R, false>(reqUrl, undefined, fetch, false),\n ...options,\n });\n}\n\n/**\n * Creates a svelte-query infinite query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The initial request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The svelte-query infinite query options object\n * @returns useQuery hook\n */\nexport function infiniteQuery<R>(\n model: string,\n url: string,\n args?: unknown,\n options?: CreateInfiniteQueryOptions<R>,\n fetch?: FetchFn\n) {\n return createInfiniteQuery<R>({\n queryKey: [QUERY_KEY_PREFIX + model, url, args],\n queryFn: ({ pageParam }) => fetcher<R, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false),\n ...options,\n });\n}\n\n/**\n * Creates a POST mutation with svelte-query.\n *\n * @param model The name of the model under mutation.\n * @param url The request URL.\n * @param options The svelte-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function postMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n url: string,\n options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) =>\n fetcher<R, C>(\n url,\n {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n },\n body: marshal(data),\n },\n fetch,\n checkReadBack\n ) as Promise<Result>;\n\n const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);\n const mutation = createMutation<Result, unknown, T>(finalOptions);\n return mutation;\n}\n\n/**\n * Creates a PUT mutation with svelte-query.\n *\n * @param model The name of the model under mutation.\n * @param url The request URL.\n * @param options The svelte-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function putMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n url: string,\n options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) =>\n fetcher<R, C>(\n url,\n {\n method: 'PUT',\n headers: {\n 'content-type': 'application/json',\n },\n body: marshal(data),\n },\n fetch,\n checkReadBack\n ) as Promise<Result>;\n\n const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);\n const mutation = createMutation<Result, unknown, T>(finalOptions);\n return mutation;\n}\n\n/**\n * Creates a DELETE mutation with svelte-query.\n *\n * @param model The name of the model under mutation.\n * @param url The request URL.\n * @param options The svelte-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function deleteMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n url: string,\n options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) =>\n fetcher<R, C>(\n makeUrl(url, data),\n {\n method: 'DELETE',\n },\n fetch,\n checkReadBack\n ) as Promise<Result>;\n\n const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);\n const mutation = createMutation<Result, unknown, T>(finalOptions);\n return mutation;\n}\n\nfunction mergeOptions<T, R = any>(\n model: string,\n options: Omit<MutationOptions<R, unknown, T, unknown>, 'mutationFn'> | undefined,\n invalidateQueries: boolean,\n mutationFn: MutateFunction<R, unknown, T>,\n queryClient: QueryClient\n): MutationOptions<R, unknown, T, unknown> {\n const result = { ...options, mutationFn };\n if (options?.onSuccess || invalidateQueries) {\n result.onSuccess = (...args) => {\n if (invalidateQueries) {\n queryClient.invalidateQueries([QUERY_KEY_PREFIX + model]);\n }\n return options?.onSuccess?.(...args);\n };\n }\n return result;\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport * as crossFetch from 'cross-fetch';\n\n/**\n * The default query endpoint.\n */\nexport const DEFAULT_QUERY_ENDPOINT = '/api/model';\n\n/**\n * Prefix for react-query keys.\n */\nexport const QUERY_KEY_PREFIX = 'zenstack:';\n\n/**\n * Function signature for `fetch`.\n */\nexport type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;\n\n/**\n * Context type for configuring the hooks.\n */\nexport type APIContext = {\n /**\n * The endpoint to use for the queries.\n */\n endpoint?: string;\n\n /**\n * A custom fetch function for sending the HTTP requests.\n */\n fetch?: FetchFn;\n};\n\nexport async function fetcher<R, C extends boolean>(\n url: string,\n options?: RequestInit,\n fetch?: FetchFn,\n checkReadBack?: C\n): Promise<C extends true ? R | undefined : R> {\n const _fetch = fetch ?? crossFetch.fetch;\n const res = await _fetch(url, options);\n if (!res.ok) {\n const errData = unmarshal(await res.text());\n if (\n checkReadBack !== false &&\n errData.error?.prisma &&\n errData.error?.code === 'P2004' &&\n errData.error?.reason === 'RESULT_NOT_READABLE'\n ) {\n // policy doesn't allow mutation result to be read back, just return undefined\n return undefined as any;\n }\n const error: Error & { info?: unknown; status?: number } = new Error(\n 'An error occurred while fetching the data.'\n );\n error.info = errData.error;\n error.status = res.status;\n throw error;\n }\n\n const textResult = await res.text();\n try {\n return unmarshal(textResult).data as R;\n } catch (err) {\n console.error(`Unable to deserialize data:`, textResult);\n throw err;\n }\n}\n\nexport function marshal(value: unknown) {\n const { data, meta } = serialize(value);\n if (meta) {\n return JSON.stringify({ ...(data as any), meta: { serialization: meta } });\n } else {\n return JSON.stringify(data);\n }\n}\n\nexport function unmarshal(value: string) {\n const parsed = JSON.parse(value);\n if (parsed.data && parsed.meta?.serialization) {\n const deserializedData = deserialize(parsed.data, parsed.meta.serialization);\n return { ...parsed, data: deserializedData };\n } else {\n return parsed;\n }\n}\n\nexport function makeUrl(url: string, args: unknown) {\n if (!args) {\n return url;\n }\n\n const { data, meta } = serialize(args);\n let result = `${url}?q=${encodeURIComponent(JSON.stringify(data))}`;\n if (meta) {\n result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;\n }\n return result;\n}\n"],"mappings":"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","names":["fetch","fetch"]}
|
|
1
|
+
{"version":3,"sources":["../../src/runtime/svelte.ts","../../src/runtime/common.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n createInfiniteQuery,\n createMutation,\n createQuery,\n useQueryClient,\n type CreateInfiniteQueryOptions,\n type MutationOptions,\n type QueryOptions,\n} from '@tanstack/svelte-query';\nimport { ModelMeta } from '@zenstackhq/runtime/cross';\nimport { getContext, setContext } from 'svelte';\nimport {\n APIContext,\n DEFAULT_QUERY_ENDPOINT,\n FetchFn,\n QUERY_KEY_PREFIX,\n fetcher,\n makeUrl,\n marshal,\n setupInvalidation,\n} from './common';\n\nexport { APIContext as RequestHandlerContext } from './common';\n\n/**\n * Key for setting and getting the global query context.\n */\nexport const SvelteQueryContextKey = 'zenstack-svelte-query-context';\n\n/**\n * Set context for the generated TanStack Query hooks.\n */\nexport function setHooksContext(context: APIContext) {\n setContext(SvelteQueryContextKey, context);\n}\n\n/**\n * Hooks context.\n */\nexport function getHooksContext() {\n const { endpoint, ...rest } = getContext<APIContext>(SvelteQueryContextKey);\n return { endpoint: endpoint ?? DEFAULT_QUERY_ENDPOINT, ...rest };\n}\n\n/**\n * Creates a svelte-query query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The svelte-query options object\n * @returns useQuery hook\n */\nexport function useModelQuery<R>(\n model: string,\n url: string,\n args?: unknown,\n options?: Omit<QueryOptions<R>, 'queryKey'>,\n fetch?: FetchFn\n) {\n const reqUrl = makeUrl(url, args);\n return createQuery<R>({\n queryKey: [QUERY_KEY_PREFIX + model, url, args],\n queryFn: () => fetcher<R, false>(reqUrl, undefined, fetch, false),\n ...options,\n });\n}\n\n/**\n * Creates a svelte-query infinite query.\n *\n * @param model The name of the model under query.\n * @param url The request URL.\n * @param args The initial request args object, URL-encoded and appended as \"?q=\" parameter\n * @param options The svelte-query infinite query options object\n * @returns useQuery hook\n */\nexport function useInfiniteModelQuery<R>(\n model: string,\n url: string,\n args?: unknown,\n options?: Omit<CreateInfiniteQueryOptions<R>, 'queryKey'>,\n fetch?: FetchFn\n) {\n return createInfiniteQuery<R>({\n queryKey: [QUERY_KEY_PREFIX + model, url, args],\n queryFn: ({ pageParam }) => fetcher<R, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false),\n ...options,\n });\n}\n\n/**\n * Creates a POST mutation with svelte-query.\n *\n * @param model The name of the model under mutation.\n * @param method The HTTP method.\n * @param modelMeta The model metadata.\n * @param url The request URL.\n * @param options The svelte-query options.\n * @param invalidateQueries Whether to invalidate queries after mutation.\n * @returns useMutation hooks\n */\nexport function useModelMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(\n model: string,\n method: 'POST' | 'PUT' | 'DELETE',\n url: string,\n modelMeta: ModelMeta,\n options?: Omit<MutationOptions<Result, unknown, T>, 'mutationFn'>,\n fetch?: FetchFn,\n invalidateQueries = true,\n checkReadBack?: C\n) {\n const queryClient = useQueryClient();\n const mutationFn = (data: any) => {\n const reqUrl = method === 'DELETE' ? makeUrl(url, data) : url;\n const fetchInit: RequestInit = {\n method,\n ...(method !== 'DELETE' && {\n headers: {\n 'content-type': 'application/json',\n },\n body: marshal(data),\n }),\n };\n return fetcher<R, C>(reqUrl, fetchInit, fetch, checkReadBack) as Promise<Result>;\n };\n\n const finalOptions = { ...options, mutationFn };\n if (invalidateQueries) {\n const { logging } = getContext<APIContext>(SvelteQueryContextKey);\n const operation = url.split('/').pop();\n if (operation) {\n setupInvalidation(\n model,\n operation,\n modelMeta,\n finalOptions,\n (predicate) => queryClient.invalidateQueries({ predicate }),\n logging\n );\n }\n }\n\n return createMutation(finalOptions);\n}\n","/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport { getMutatedModels, getReadModels, type ModelMeta, type PrismaWriteActionType } from '@zenstackhq/runtime/cross';\nimport * as crossFetch from 'cross-fetch';\n\n/**\n * The default query endpoint.\n */\nexport const DEFAULT_QUERY_ENDPOINT = '/api/model';\n\n/**\n * Prefix for react-query keys.\n */\nexport const QUERY_KEY_PREFIX = 'zenstack';\n\n/**\n * Function signature for `fetch`.\n */\nexport type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;\n\n/**\n * Context type for configuring the hooks.\n */\nexport type APIContext = {\n /**\n * The endpoint to use for the queries.\n */\n endpoint?: string;\n\n /**\n * A custom fetch function for sending the HTTP requests.\n */\n fetch?: FetchFn;\n\n /**\n * If logging is enabled.\n */\n logging?: boolean;\n};\n\nexport async function fetcher<R, C extends boolean>(\n url: string,\n options?: RequestInit,\n fetch?: FetchFn,\n checkReadBack?: C\n): Promise<C extends true ? R | undefined : R> {\n const _fetch = fetch ?? crossFetch.fetch;\n const res = await _fetch(url, options);\n if (!res.ok) {\n const errData = unmarshal(await res.text());\n if (\n checkReadBack !== false &&\n errData.error?.prisma &&\n errData.error?.code === 'P2004' &&\n errData.error?.reason === 'RESULT_NOT_READABLE'\n ) {\n // policy doesn't allow mutation result to be read back, just return undefined\n return undefined as any;\n }\n const error: Error & { info?: unknown; status?: number } = new Error(\n 'An error occurred while fetching the data.'\n );\n error.info = errData.error;\n error.status = res.status;\n throw error;\n }\n\n const textResult = await res.text();\n try {\n return unmarshal(textResult).data as R;\n } catch (err) {\n console.error(`Unable to deserialize data:`, textResult);\n throw err;\n }\n}\n\ntype QueryKey = [string /* prefix */, string /* model */, string /* operation */, unknown /* args */];\n\n/**\n * Computes query key for the given model, operation and query args.\n * @param model Model name.\n * @param urlOrOperation Prisma operation (e.g, `findMany`) or request URL. If it's a URL, the last path segment will be used as the operation name.\n * @param args Prisma query arguments.\n * @returns Query key\n */\nexport function getQueryKey(model: string, urlOrOperation: string, args: unknown): QueryKey {\n if (!urlOrOperation) {\n throw new Error('Invalid urlOrOperation');\n }\n const operation = urlOrOperation.split('/').pop();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return [QUERY_KEY_PREFIX, model, operation!, args];\n}\n\nexport function marshal(value: unknown) {\n const { data, meta } = serialize(value);\n if (meta) {\n return JSON.stringify({ ...(data as any), meta: { serialization: meta } });\n } else {\n return JSON.stringify(data);\n }\n}\n\nexport function unmarshal(value: string) {\n const parsed = JSON.parse(value);\n if (parsed.data && parsed.meta?.serialization) {\n const deserializedData = deserialize(parsed.data, parsed.meta.serialization);\n return { ...parsed, data: deserializedData };\n } else {\n return parsed;\n }\n}\n\nexport function makeUrl(url: string, args: unknown) {\n if (!args) {\n return url;\n }\n\n const { data, meta } = serialize(args);\n let result = `${url}?q=${encodeURIComponent(JSON.stringify(data))}`;\n if (meta) {\n result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;\n }\n return result;\n}\n\ntype InvalidationPredicate = ({ queryKey }: { queryKey: readonly unknown[] }) => boolean;\n\n// sets up invalidation hook for a mutation\nexport function setupInvalidation(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: { onSuccess?: (...args: any[]) => any },\n invalidate: (predicate: InvalidationPredicate) => Promise<void>,\n logging = false\n) {\n const origOnSuccess = options?.onSuccess;\n options.onSuccess = async (...args: unknown[]) => {\n const [_, variables] = args;\n const predicate = await getInvalidationPredicate(\n model,\n operation as PrismaWriteActionType,\n variables,\n modelMeta,\n logging\n );\n await invalidate(predicate);\n return origOnSuccess?.(...args);\n };\n}\n\n// gets a predicate for evaluating whether a query should be invalidated\nasync function getInvalidationPredicate(\n model: string,\n operation: PrismaWriteActionType,\n mutationArgs: any,\n modelMeta: ModelMeta,\n logging = false\n) {\n const mutatedModels = await getMutatedModels(model, operation, mutationArgs, modelMeta);\n\n return ({ queryKey }: { queryKey: readonly unknown[] }) => {\n const [_model, queryModel, queryOp, args] = queryKey as QueryKey;\n\n if (mutatedModels.includes(queryModel)) {\n // direct match\n if (logging) {\n console.log(`Invalidating query [${queryKey}] due to mutation \"${model}.${operation}\"`);\n }\n return true;\n }\n\n if (args) {\n // traverse query args to find nested reads that match the model under mutation\n if (findNestedRead(queryModel, mutatedModels, modelMeta, args)) {\n if (logging) {\n console.log(`Invalidating query [${queryKey}] due to mutation \"${model}.${operation}\"`);\n }\n return true;\n }\n }\n\n return false;\n };\n}\n\n// find nested reads that match the given models\nfunction findNestedRead(visitingModel: string, targetModels: string[], modelMeta: ModelMeta, args: any) {\n const modelsRead = getReadModels(visitingModel, modelMeta, args);\n return targetModels.some((m) => modelsRead.includes(m));\n}\n"],"mappings":"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","names":["fetch","fetch"]}
|