@zenstackhq/tanstack-query 2.0.0-alpha.7 → 2.0.0-beta.1

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.
Files changed (59) hide show
  1. package/generator.js +35 -52
  2. package/generator.js.map +1 -1
  3. package/package.json +5 -4
  4. package/runtime/common--_wRp2eo.d.mts +121 -0
  5. package/runtime/common--_wRp2eo.d.ts +121 -0
  6. package/runtime/index.d.mts +1 -1
  7. package/runtime/index.d.ts +1 -1
  8. package/runtime/index.js +3 -1
  9. package/runtime/index.js.map +1 -1
  10. package/runtime/index.mjs +3 -1
  11. package/runtime/index.mjs.map +1 -1
  12. package/runtime/react.d.mts +3 -6
  13. package/runtime/react.d.ts +3 -6
  14. package/runtime/react.js +37 -8
  15. package/runtime/react.js.map +1 -1
  16. package/runtime/react.mjs +37 -8
  17. package/runtime/react.mjs.map +1 -1
  18. package/runtime/svelte.d.mts +3 -6
  19. package/runtime/svelte.d.ts +3 -6
  20. package/runtime/svelte.js +37 -8
  21. package/runtime/svelte.js.map +1 -1
  22. package/runtime/svelte.mjs +37 -8
  23. package/runtime/svelte.mjs.map +1 -1
  24. package/runtime/vue.d.mts +3 -6
  25. package/runtime/vue.d.ts +3 -6
  26. package/runtime/vue.js +37 -8
  27. package/runtime/vue.js.map +1 -1
  28. package/runtime/vue.mjs +37 -8
  29. package/runtime/vue.mjs.map +1 -1
  30. package/runtime-v5/common--_wRp2eo.d.mts +121 -0
  31. package/runtime-v5/common--_wRp2eo.d.ts +121 -0
  32. package/runtime-v5/index.d.mts +1 -1
  33. package/runtime-v5/index.d.ts +1 -1
  34. package/runtime-v5/index.js +3 -1
  35. package/runtime-v5/index.js.map +1 -1
  36. package/runtime-v5/index.mjs +3 -1
  37. package/runtime-v5/index.mjs.map +1 -1
  38. package/runtime-v5/react.d.mts +4 -8
  39. package/runtime-v5/react.d.ts +4 -8
  40. package/runtime-v5/react.js +43 -11
  41. package/runtime-v5/react.js.map +1 -1
  42. package/runtime-v5/react.mjs +43 -11
  43. package/runtime-v5/react.mjs.map +1 -1
  44. package/runtime-v5/svelte.d.mts +3 -5
  45. package/runtime-v5/svelte.d.ts +3 -5
  46. package/runtime-v5/svelte.js +37 -8
  47. package/runtime-v5/svelte.js.map +1 -1
  48. package/runtime-v5/svelte.mjs +37 -8
  49. package/runtime-v5/svelte.mjs.map +1 -1
  50. package/runtime-v5/vue.d.mts +3 -6
  51. package/runtime-v5/vue.d.ts +3 -6
  52. package/runtime-v5/vue.js +37 -8
  53. package/runtime-v5/vue.js.map +1 -1
  54. package/runtime-v5/vue.mjs +37 -8
  55. package/runtime-v5/vue.mjs.map +1 -1
  56. package/runtime/common-ZSgIGN1q.d.mts +0 -56
  57. package/runtime/common-ZSgIGN1q.d.ts +0 -56
  58. package/runtime-v5/common-ZSgIGN1q.d.mts +0 -56
  59. package/runtime-v5/common-ZSgIGN1q.d.ts +0 -56
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Function signature for `fetch`.
3
+ */
4
+ type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;
5
+ /**
6
+ * Type for query and mutation errors.
7
+ */
8
+ type QueryError = Error & {
9
+ /**
10
+ * Additional error information.
11
+ */
12
+ info?: unknown;
13
+ /**
14
+ * HTTP status code.
15
+ */
16
+ status?: number;
17
+ };
18
+ /**
19
+ * Result of optimistic data provider.
20
+ */
21
+ type OptimisticDataProviderResult = {
22
+ /**
23
+ * Kind of the result.
24
+ * - Update: use the `data` field to update the query cache.
25
+ * - Skip: skip the optimistic update for this query.
26
+ * - ProceedDefault: proceed with the default optimistic update.
27
+ */
28
+ kind: 'Update' | 'Skip' | 'ProceedDefault';
29
+ /**
30
+ * Data to update the query cache. Only applicable if `kind` is 'Update'.
31
+ *
32
+ * If the data is an object with fields updated, it should have a `$optimistic`
33
+ * field set to `true`. If it's an array and an element object is created or updated,
34
+ * the element should have a `$optimistic` field set to `true`.
35
+ */
36
+ data?: any;
37
+ };
38
+ /**
39
+ * Optimistic data provider.
40
+ *
41
+ * @param args Arguments.
42
+ * @param args.queryModel The model of the query.
43
+ * @param args.queryOperation The operation of the query, `findMany`, `count`, etc.
44
+ * @param args.queryArgs The arguments of the query.
45
+ * @param args.currentData The current cache data for the query.
46
+ * @param args.mutationArgs The arguments of the mutation.
47
+ */
48
+ type OptimisticDataProvider = (args: {
49
+ queryModel: string;
50
+ queryOperation: string;
51
+ queryArgs: any;
52
+ currentData: any;
53
+ mutationArgs: any;
54
+ }) => OptimisticDataProviderResult | Promise<OptimisticDataProviderResult>;
55
+ /**
56
+ * Extra mutation options.
57
+ */
58
+ type ExtraMutationOptions = {
59
+ /**
60
+ * Whether to automatically invalidate queries potentially affected by the mutation. Defaults to `true`.
61
+ */
62
+ invalidateQueries?: boolean;
63
+ /**
64
+ * Whether to optimistically update queries potentially affected by the mutation. Defaults to `false`.
65
+ */
66
+ optimisticUpdate?: boolean;
67
+ /**
68
+ * A callback for computing optimistic update data for each query cache entry.
69
+ */
70
+ optimisticDataProvider?: OptimisticDataProvider;
71
+ };
72
+ /**
73
+ * Extra query options.
74
+ */
75
+ type ExtraQueryOptions = {
76
+ /**
77
+ * Whether to opt-in to optimistic updates for this query. Defaults to `true`.
78
+ */
79
+ optimisticUpdate?: boolean;
80
+ };
81
+ /**
82
+ * Context type for configuring the hooks.
83
+ */
84
+ type APIContext = {
85
+ /**
86
+ * The endpoint to use for the queries.
87
+ */
88
+ endpoint?: string;
89
+ /**
90
+ * A custom fetch function for sending the HTTP requests.
91
+ */
92
+ fetch?: FetchFn;
93
+ /**
94
+ * If logging is enabled.
95
+ */
96
+ logging?: boolean;
97
+ };
98
+ type QueryKey = [
99
+ string,
100
+ string,
101
+ string,
102
+ unknown,
103
+ {
104
+ infinite: boolean;
105
+ optimisticUpdate: boolean;
106
+ }
107
+ ];
108
+ /**
109
+ * Computes query key for the given model, operation and query args.
110
+ * @param model Model name.
111
+ * @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.
112
+ * @param args Prisma query arguments.
113
+ * @param options Query options, including `infinite` indicating if it's an infinite query (defaults to false), and `optimisticUpdate` indicating if optimistic updates are enabled (defaults to true).
114
+ * @returns Query key
115
+ */
116
+ declare function getQueryKey(model: string, urlOrOperation: string, args: unknown, options?: {
117
+ infinite: boolean;
118
+ optimisticUpdate: boolean;
119
+ }): QueryKey;
120
+
121
+ export { type APIContext as A, type ExtraMutationOptions as E, type FetchFn as F, type QueryError as Q, type ExtraQueryOptions as a, getQueryKey as g };
@@ -1,4 +1,4 @@
1
- export { F as FetchFn, Q as QueryError, g as getQueryKey } from './common-ZSgIGN1q.mjs';
1
+ export { E as ExtraMutationOptions, a as ExtraQueryOptions, F as FetchFn, Q as QueryError, g as getQueryKey } from './common--_wRp2eo.mjs';
2
2
 
3
3
  type Enumerable<T> = T | Array<T>;
4
4
  type _TupleToUnion<T> = T extends (infer E)[] ? E : never;
@@ -1,4 +1,4 @@
1
- export { F as FetchFn, Q as QueryError, g as getQueryKey } from './common-ZSgIGN1q.js';
1
+ export { E as ExtraMutationOptions, a as ExtraQueryOptions, F as FetchFn, Q as QueryError, g as getQueryKey } from './common--_wRp2eo.js';
2
2
 
3
3
  type Enumerable<T> = T | Array<T>;
4
4
  type _TupleToUnion<T> = T extends (infer E)[] ? E : never;
@@ -39,11 +39,13 @@ var import_browser = require("@zenstackhq/runtime/browser");
39
39
  var import_cross = require("@zenstackhq/runtime/cross");
40
40
  var crossFetch = __toESM(require("cross-fetch"));
41
41
  var QUERY_KEY_PREFIX = "zenstack";
42
- function getQueryKey(model, urlOrOperation, args, infinite = false, optimisticUpdate = false) {
42
+ function getQueryKey(model, urlOrOperation, args, options = { infinite: false, optimisticUpdate: true }) {
43
43
  if (!urlOrOperation) {
44
44
  throw new Error("Invalid urlOrOperation");
45
45
  }
46
46
  const operation = urlOrOperation.split("/").pop();
47
+ const infinite = options.infinite;
48
+ const optimisticUpdate = options.infinite ? false : options.optimisticUpdate;
47
49
  return [QUERY_KEY_PREFIX, model, operation, args, { infinite, optimisticUpdate }];
48
50
  }
49
51
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime-v5/index.ts","../../src/runtime/common.ts"],"sourcesContent":["export * from '../runtime/prisma-types';\nexport { type FetchFn, type QueryError, getQueryKey } from '../runtime/common';\n","/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport {\n applyMutation,\n getMutatedModels,\n getReadModels,\n type ModelMeta,\n type PrismaWriteActionType,\n} 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 * Type for query and mutation errors.\n */\nexport type QueryError = Error & {\n /**\n * Additional error information.\n */\n info?: unknown;\n\n /**\n * HTTP status code.\n */\n status?: number;\n};\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: QueryError = new Error('An error occurred while fetching the data.');\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 = [\n string /* prefix */,\n string /* model */,\n string /* operation */,\n unknown /* args */,\n {\n infinite: boolean;\n optimisticUpdate: boolean;\n } /* flags */\n];\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 * @param infinite Whether the query is infinite.\n * @param optimisticUpdate Whether the query is optimistically updated.\n * @returns Query key\n */\nexport function getQueryKey(\n model: string,\n urlOrOperation: string,\n args: unknown,\n infinite = false,\n optimisticUpdate = false\n): 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, { infinite, optimisticUpdate }];\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;\ntype InvalidateFunc = (predicate: InvalidationPredicate) => Promise<void>;\ntype MutationOptions = {\n onMutate?: (...args: any[]) => any;\n onSuccess?: (...args: any[]) => any;\n onSettled?: (...args: any[]) => any;\n};\n\n// sets up invalidation hook for a mutation\nexport function setupInvalidation(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions,\n invalidate: InvalidateFunc,\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 [_, queryModel, , args] = queryKey as QueryKey;\n\n if (mutatedModels.includes(queryModel)) {\n // direct match\n if (logging) {\n console.log(`Invalidating query ${JSON.stringify(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(\n `Invalidating query ${JSON.stringify(queryKey)} due to mutation \"${model}.${operation}\"`\n );\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\ntype QueryCache = {\n queryKey: readonly unknown[];\n state: {\n data: unknown;\n error: unknown;\n };\n}[];\n\ntype SetCacheFunc = (queryKey: readonly unknown[], data: unknown) => void;\n\nexport function setupOptimisticUpdate(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n invalidate?: InvalidateFunc,\n logging = false\n) {\n const origOnMutate = options?.onMutate;\n const origOnSettled = options?.onSettled;\n\n // optimistic update on mutate\n options.onMutate = async (...args: unknown[]) => {\n const [variables] = args;\n await optimisticUpdate(\n model,\n operation as PrismaWriteActionType,\n variables,\n modelMeta,\n queryCache,\n setCache,\n logging\n );\n return origOnMutate?.(...args);\n };\n\n // invalidate on settled\n options.onSettled = async (...args: unknown[]) => {\n if (invalidate) {\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 }\n return origOnSettled?.(...args);\n };\n}\n\n// optimistically updates query cache\nasync function optimisticUpdate(\n mutationModel: string,\n mutationOp: string,\n mutationArgs: any,\n modelMeta: ModelMeta,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n logging = false\n) {\n for (const cacheItem of queryCache) {\n const {\n queryKey,\n state: { data, error },\n } = cacheItem;\n\n if (error) {\n if (logging) {\n console.warn(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to error:`, error);\n }\n continue;\n }\n\n const [_, queryModel, queryOp, _queryArgs, { optimisticUpdate }] = queryKey as QueryKey;\n if (!optimisticUpdate) {\n if (logging) {\n console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to opt-out`);\n }\n continue;\n }\n\n const mutatedData = await applyMutation(\n queryModel,\n queryOp,\n data,\n mutationModel,\n mutationOp as PrismaWriteActionType,\n mutationArgs,\n modelMeta,\n logging\n );\n\n if (mutatedData !== undefined) {\n // mutation applicable to this query, update cache\n if (logging) {\n console.log(\n `Optimistically updating query ${JSON.stringify(\n queryKey\n )} due to mutation \"${mutationModel}.${mutationOp}\"`\n );\n }\n setCache(queryKey, mutatedData);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,qBAAuC;AACvC,mBAMO;AACP,iBAA4B;AAUrB,IAAM,mBAAmB;AAgGzB,SAAS,YACZ,OACA,gBACA,MACA,WAAW,OACX,mBAAmB,OACX;AACR,MAAI,CAAC,gBAAgB;AACjB,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC5C;AACA,QAAM,YAAY,eAAe,MAAM,GAAG,EAAE,IAAI;AAEhD,SAAO,CAAC,kBAAkB,OAAO,WAAY,MAAM,EAAE,UAAU,iBAAiB,CAAC;AACrF;","names":[]}
1
+ {"version":3,"sources":["../../src/runtime-v5/index.ts","../../src/runtime/common.ts"],"sourcesContent":["export {\n getQueryKey,\n type ExtraMutationOptions,\n type ExtraQueryOptions,\n type FetchFn,\n type QueryError,\n} from '../runtime/common';\nexport * from '../runtime/prisma-types';\n","/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport {\n applyMutation,\n getMutatedModels,\n getReadModels,\n type ModelMeta,\n type PrismaWriteActionType,\n} 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 * Type for query and mutation errors.\n */\nexport type QueryError = Error & {\n /**\n * Additional error information.\n */\n info?: unknown;\n\n /**\n * HTTP status code.\n */\n status?: number;\n};\n\n/**\n * Result of optimistic data provider.\n */\nexport type OptimisticDataProviderResult = {\n /**\n * Kind of the result.\n * - Update: use the `data` field to update the query cache.\n * - Skip: skip the optimistic update for this query.\n * - ProceedDefault: proceed with the default optimistic update.\n */\n kind: 'Update' | 'Skip' | 'ProceedDefault';\n\n /**\n * Data to update the query cache. Only applicable if `kind` is 'Update'.\n *\n * If the data is an object with fields updated, it should have a `$optimistic`\n * field set to `true`. If it's an array and an element object is created or updated,\n * the element should have a `$optimistic` field set to `true`.\n */\n data?: any;\n};\n\n/**\n * Optimistic data provider.\n *\n * @param args Arguments.\n * @param args.queryModel The model of the query.\n * @param args.queryOperation The operation of the query, `findMany`, `count`, etc.\n * @param args.queryArgs The arguments of the query.\n * @param args.currentData The current cache data for the query.\n * @param args.mutationArgs The arguments of the mutation.\n */\nexport type OptimisticDataProvider = (args: {\n queryModel: string;\n queryOperation: string;\n queryArgs: any;\n currentData: any;\n mutationArgs: any;\n}) => OptimisticDataProviderResult | Promise<OptimisticDataProviderResult>;\n\n/**\n * Extra mutation options.\n */\nexport type ExtraMutationOptions = {\n /**\n * Whether to automatically invalidate queries potentially affected by the mutation. Defaults to `true`.\n */\n invalidateQueries?: boolean;\n\n /**\n * Whether to optimistically update queries potentially affected by the mutation. Defaults to `false`.\n */\n optimisticUpdate?: boolean;\n\n /**\n * A callback for computing optimistic update data for each query cache entry.\n */\n optimisticDataProvider?: OptimisticDataProvider;\n};\n\n/**\n * Extra query options.\n */\nexport type ExtraQueryOptions = {\n /**\n * Whether to opt-in to optimistic updates for this query. Defaults to `true`.\n */\n optimisticUpdate?: boolean;\n};\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: QueryError = new Error('An error occurred while fetching the data.');\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 = [\n string /* prefix */,\n string /* model */,\n string /* operation */,\n unknown /* args */,\n {\n infinite: boolean;\n optimisticUpdate: boolean;\n } /* flags */\n];\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 * @param options Query options, including `infinite` indicating if it's an infinite query (defaults to false), and `optimisticUpdate` indicating if optimistic updates are enabled (defaults to true).\n * @returns Query key\n */\nexport function getQueryKey(\n model: string,\n urlOrOperation: string,\n args: unknown,\n options: { infinite: boolean; optimisticUpdate: boolean } = { infinite: false, optimisticUpdate: true }\n): QueryKey {\n if (!urlOrOperation) {\n throw new Error('Invalid urlOrOperation');\n }\n const operation = urlOrOperation.split('/').pop();\n\n const infinite = options.infinite;\n // infinite query doesn't support optimistic updates\n const optimisticUpdate = options.infinite ? false : options.optimisticUpdate;\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return [QUERY_KEY_PREFIX, model, operation!, args, { infinite, optimisticUpdate }];\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;\ntype InvalidateFunc = (predicate: InvalidationPredicate) => Promise<void>;\ntype MutationOptions = {\n onMutate?: (...args: any[]) => any;\n onSuccess?: (...args: any[]) => any;\n onSettled?: (...args: any[]) => any;\n};\n\n// sets up invalidation hook for a mutation\nexport function setupInvalidation(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions,\n invalidate: InvalidateFunc,\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 [_, queryModel, , args] = queryKey as QueryKey;\n\n if (mutatedModels.includes(queryModel)) {\n // direct match\n if (logging) {\n console.log(`Invalidating query ${JSON.stringify(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(\n `Invalidating query ${JSON.stringify(queryKey)} due to mutation \"${model}.${operation}\"`\n );\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\ntype QueryCache = {\n queryKey: readonly unknown[];\n state: {\n data: unknown;\n error: unknown;\n };\n}[];\n\ntype SetCacheFunc = (queryKey: readonly unknown[], data: unknown) => void;\n\n/**\n * Sets up optimistic update and invalidation (after settled) for a mutation.\n */\nexport function setupOptimisticUpdate(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions & ExtraMutationOptions,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n invalidate?: InvalidateFunc,\n logging = false\n) {\n const origOnMutate = options?.onMutate;\n const origOnSettled = options?.onSettled;\n\n // optimistic update on mutate\n options.onMutate = async (...args: unknown[]) => {\n const [variables] = args;\n await optimisticUpdate(\n model,\n operation as PrismaWriteActionType,\n variables,\n options,\n modelMeta,\n queryCache,\n setCache,\n logging\n );\n return origOnMutate?.(...args);\n };\n\n // invalidate on settled\n options.onSettled = async (...args: unknown[]) => {\n if (invalidate) {\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 }\n return origOnSettled?.(...args);\n };\n}\n\n// optimistically updates query cache\nasync function optimisticUpdate(\n mutationModel: string,\n mutationOp: string,\n mutationArgs: any,\n options: MutationOptions & ExtraMutationOptions,\n modelMeta: ModelMeta,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n logging = false\n) {\n for (const cacheItem of queryCache) {\n const {\n queryKey,\n state: { data, error },\n } = cacheItem;\n\n if (error) {\n if (logging) {\n console.warn(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to error:`, error);\n }\n continue;\n }\n\n const [_, queryModel, queryOperation, queryArgs, { optimisticUpdate }] = queryKey as QueryKey;\n if (!optimisticUpdate) {\n if (logging) {\n console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to opt-out`);\n }\n continue;\n }\n\n if (options.optimisticDataProvider) {\n const providerResult = await options.optimisticDataProvider({\n queryModel,\n queryOperation,\n queryArgs,\n currentData: data,\n mutationArgs,\n });\n\n if (providerResult?.kind === 'Skip') {\n // skip\n if (logging) {\n console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to provider`);\n }\n continue;\n } else if (providerResult?.kind === 'Update') {\n // update cache\n if (logging) {\n console.log(`Optimistically updating query ${JSON.stringify(queryKey)} due to provider`);\n }\n setCache(queryKey, providerResult.data);\n continue;\n }\n }\n\n // proceed with default optimistic update\n const mutatedData = await applyMutation(\n queryModel,\n queryOperation,\n data,\n mutationModel,\n mutationOp as PrismaWriteActionType,\n mutationArgs,\n modelMeta,\n logging\n );\n\n if (mutatedData !== undefined) {\n // mutation applicable to this query, update cache\n if (logging) {\n console.log(\n `Optimistically updating query ${JSON.stringify(\n queryKey\n )} due to mutation \"${mutationModel}.${mutationOp}\"`\n );\n }\n setCache(queryKey, mutatedData);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,qBAAuC;AACvC,mBAMO;AACP,iBAA4B;AAUrB,IAAM,mBAAmB;AAqKzB,SAAS,YACZ,OACA,gBACA,MACA,UAA4D,EAAE,UAAU,OAAO,kBAAkB,KAAK,GAC9F;AACR,MAAI,CAAC,gBAAgB;AACjB,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC5C;AACA,QAAM,YAAY,eAAe,MAAM,GAAG,EAAE,IAAI;AAEhD,QAAM,WAAW,QAAQ;AAEzB,QAAM,mBAAmB,QAAQ,WAAW,QAAQ,QAAQ;AAG5D,SAAO,CAAC,kBAAkB,OAAO,WAAY,MAAM,EAAE,UAAU,iBAAiB,CAAC;AACrF;","names":[]}
@@ -7,11 +7,13 @@ import {
7
7
  } from "@zenstackhq/runtime/cross";
8
8
  import * as crossFetch from "cross-fetch";
9
9
  var QUERY_KEY_PREFIX = "zenstack";
10
- function getQueryKey(model, urlOrOperation, args, infinite = false, optimisticUpdate = false) {
10
+ function getQueryKey(model, urlOrOperation, args, options = { infinite: false, optimisticUpdate: true }) {
11
11
  if (!urlOrOperation) {
12
12
  throw new Error("Invalid urlOrOperation");
13
13
  }
14
14
  const operation = urlOrOperation.split("/").pop();
15
+ const infinite = options.infinite;
16
+ const optimisticUpdate = options.infinite ? false : options.optimisticUpdate;
15
17
  return [QUERY_KEY_PREFIX, model, operation, args, { infinite, optimisticUpdate }];
16
18
  }
17
19
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/common.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport {\n applyMutation,\n getMutatedModels,\n getReadModels,\n type ModelMeta,\n type PrismaWriteActionType,\n} 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 * Type for query and mutation errors.\n */\nexport type QueryError = Error & {\n /**\n * Additional error information.\n */\n info?: unknown;\n\n /**\n * HTTP status code.\n */\n status?: number;\n};\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: QueryError = new Error('An error occurred while fetching the data.');\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 = [\n string /* prefix */,\n string /* model */,\n string /* operation */,\n unknown /* args */,\n {\n infinite: boolean;\n optimisticUpdate: boolean;\n } /* flags */\n];\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 * @param infinite Whether the query is infinite.\n * @param optimisticUpdate Whether the query is optimistically updated.\n * @returns Query key\n */\nexport function getQueryKey(\n model: string,\n urlOrOperation: string,\n args: unknown,\n infinite = false,\n optimisticUpdate = false\n): 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, { infinite, optimisticUpdate }];\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;\ntype InvalidateFunc = (predicate: InvalidationPredicate) => Promise<void>;\ntype MutationOptions = {\n onMutate?: (...args: any[]) => any;\n onSuccess?: (...args: any[]) => any;\n onSettled?: (...args: any[]) => any;\n};\n\n// sets up invalidation hook for a mutation\nexport function setupInvalidation(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions,\n invalidate: InvalidateFunc,\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 [_, queryModel, , args] = queryKey as QueryKey;\n\n if (mutatedModels.includes(queryModel)) {\n // direct match\n if (logging) {\n console.log(`Invalidating query ${JSON.stringify(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(\n `Invalidating query ${JSON.stringify(queryKey)} due to mutation \"${model}.${operation}\"`\n );\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\ntype QueryCache = {\n queryKey: readonly unknown[];\n state: {\n data: unknown;\n error: unknown;\n };\n}[];\n\ntype SetCacheFunc = (queryKey: readonly unknown[], data: unknown) => void;\n\nexport function setupOptimisticUpdate(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n invalidate?: InvalidateFunc,\n logging = false\n) {\n const origOnMutate = options?.onMutate;\n const origOnSettled = options?.onSettled;\n\n // optimistic update on mutate\n options.onMutate = async (...args: unknown[]) => {\n const [variables] = args;\n await optimisticUpdate(\n model,\n operation as PrismaWriteActionType,\n variables,\n modelMeta,\n queryCache,\n setCache,\n logging\n );\n return origOnMutate?.(...args);\n };\n\n // invalidate on settled\n options.onSettled = async (...args: unknown[]) => {\n if (invalidate) {\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 }\n return origOnSettled?.(...args);\n };\n}\n\n// optimistically updates query cache\nasync function optimisticUpdate(\n mutationModel: string,\n mutationOp: string,\n mutationArgs: any,\n modelMeta: ModelMeta,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n logging = false\n) {\n for (const cacheItem of queryCache) {\n const {\n queryKey,\n state: { data, error },\n } = cacheItem;\n\n if (error) {\n if (logging) {\n console.warn(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to error:`, error);\n }\n continue;\n }\n\n const [_, queryModel, queryOp, _queryArgs, { optimisticUpdate }] = queryKey as QueryKey;\n if (!optimisticUpdate) {\n if (logging) {\n console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to opt-out`);\n }\n continue;\n }\n\n const mutatedData = await applyMutation(\n queryModel,\n queryOp,\n data,\n mutationModel,\n mutationOp as PrismaWriteActionType,\n mutationArgs,\n modelMeta,\n logging\n );\n\n if (mutatedData !== undefined) {\n // mutation applicable to this query, update cache\n if (logging) {\n console.log(\n `Optimistically updating query ${JSON.stringify(\n queryKey\n )} due to mutation \"${mutationModel}.${mutationOp}\"`\n );\n }\n setCache(queryKey, mutatedData);\n }\n }\n}\n"],"mappings":";AAEA,SAAS,aAAa,iBAAiB;AACvC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OAGG;AACP,YAAY,gBAAgB;AAUrB,IAAM,mBAAmB;AAgGzB,SAAS,YACZ,OACA,gBACA,MACA,WAAW,OACX,mBAAmB,OACX;AACR,MAAI,CAAC,gBAAgB;AACjB,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC5C;AACA,QAAM,YAAY,eAAe,MAAM,GAAG,EAAE,IAAI;AAEhD,SAAO,CAAC,kBAAkB,OAAO,WAAY,MAAM,EAAE,UAAU,iBAAiB,CAAC;AACrF;","names":[]}
1
+ {"version":3,"sources":["../../src/runtime/common.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { deserialize, serialize } from '@zenstackhq/runtime/browser';\nimport {\n applyMutation,\n getMutatedModels,\n getReadModels,\n type ModelMeta,\n type PrismaWriteActionType,\n} 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 * Type for query and mutation errors.\n */\nexport type QueryError = Error & {\n /**\n * Additional error information.\n */\n info?: unknown;\n\n /**\n * HTTP status code.\n */\n status?: number;\n};\n\n/**\n * Result of optimistic data provider.\n */\nexport type OptimisticDataProviderResult = {\n /**\n * Kind of the result.\n * - Update: use the `data` field to update the query cache.\n * - Skip: skip the optimistic update for this query.\n * - ProceedDefault: proceed with the default optimistic update.\n */\n kind: 'Update' | 'Skip' | 'ProceedDefault';\n\n /**\n * Data to update the query cache. Only applicable if `kind` is 'Update'.\n *\n * If the data is an object with fields updated, it should have a `$optimistic`\n * field set to `true`. If it's an array and an element object is created or updated,\n * the element should have a `$optimistic` field set to `true`.\n */\n data?: any;\n};\n\n/**\n * Optimistic data provider.\n *\n * @param args Arguments.\n * @param args.queryModel The model of the query.\n * @param args.queryOperation The operation of the query, `findMany`, `count`, etc.\n * @param args.queryArgs The arguments of the query.\n * @param args.currentData The current cache data for the query.\n * @param args.mutationArgs The arguments of the mutation.\n */\nexport type OptimisticDataProvider = (args: {\n queryModel: string;\n queryOperation: string;\n queryArgs: any;\n currentData: any;\n mutationArgs: any;\n}) => OptimisticDataProviderResult | Promise<OptimisticDataProviderResult>;\n\n/**\n * Extra mutation options.\n */\nexport type ExtraMutationOptions = {\n /**\n * Whether to automatically invalidate queries potentially affected by the mutation. Defaults to `true`.\n */\n invalidateQueries?: boolean;\n\n /**\n * Whether to optimistically update queries potentially affected by the mutation. Defaults to `false`.\n */\n optimisticUpdate?: boolean;\n\n /**\n * A callback for computing optimistic update data for each query cache entry.\n */\n optimisticDataProvider?: OptimisticDataProvider;\n};\n\n/**\n * Extra query options.\n */\nexport type ExtraQueryOptions = {\n /**\n * Whether to opt-in to optimistic updates for this query. Defaults to `true`.\n */\n optimisticUpdate?: boolean;\n};\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: QueryError = new Error('An error occurred while fetching the data.');\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 = [\n string /* prefix */,\n string /* model */,\n string /* operation */,\n unknown /* args */,\n {\n infinite: boolean;\n optimisticUpdate: boolean;\n } /* flags */\n];\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 * @param options Query options, including `infinite` indicating if it's an infinite query (defaults to false), and `optimisticUpdate` indicating if optimistic updates are enabled (defaults to true).\n * @returns Query key\n */\nexport function getQueryKey(\n model: string,\n urlOrOperation: string,\n args: unknown,\n options: { infinite: boolean; optimisticUpdate: boolean } = { infinite: false, optimisticUpdate: true }\n): QueryKey {\n if (!urlOrOperation) {\n throw new Error('Invalid urlOrOperation');\n }\n const operation = urlOrOperation.split('/').pop();\n\n const infinite = options.infinite;\n // infinite query doesn't support optimistic updates\n const optimisticUpdate = options.infinite ? false : options.optimisticUpdate;\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return [QUERY_KEY_PREFIX, model, operation!, args, { infinite, optimisticUpdate }];\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;\ntype InvalidateFunc = (predicate: InvalidationPredicate) => Promise<void>;\ntype MutationOptions = {\n onMutate?: (...args: any[]) => any;\n onSuccess?: (...args: any[]) => any;\n onSettled?: (...args: any[]) => any;\n};\n\n// sets up invalidation hook for a mutation\nexport function setupInvalidation(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions,\n invalidate: InvalidateFunc,\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 [_, queryModel, , args] = queryKey as QueryKey;\n\n if (mutatedModels.includes(queryModel)) {\n // direct match\n if (logging) {\n console.log(`Invalidating query ${JSON.stringify(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(\n `Invalidating query ${JSON.stringify(queryKey)} due to mutation \"${model}.${operation}\"`\n );\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\ntype QueryCache = {\n queryKey: readonly unknown[];\n state: {\n data: unknown;\n error: unknown;\n };\n}[];\n\ntype SetCacheFunc = (queryKey: readonly unknown[], data: unknown) => void;\n\n/**\n * Sets up optimistic update and invalidation (after settled) for a mutation.\n */\nexport function setupOptimisticUpdate(\n model: string,\n operation: string,\n modelMeta: ModelMeta,\n options: MutationOptions & ExtraMutationOptions,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n invalidate?: InvalidateFunc,\n logging = false\n) {\n const origOnMutate = options?.onMutate;\n const origOnSettled = options?.onSettled;\n\n // optimistic update on mutate\n options.onMutate = async (...args: unknown[]) => {\n const [variables] = args;\n await optimisticUpdate(\n model,\n operation as PrismaWriteActionType,\n variables,\n options,\n modelMeta,\n queryCache,\n setCache,\n logging\n );\n return origOnMutate?.(...args);\n };\n\n // invalidate on settled\n options.onSettled = async (...args: unknown[]) => {\n if (invalidate) {\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 }\n return origOnSettled?.(...args);\n };\n}\n\n// optimistically updates query cache\nasync function optimisticUpdate(\n mutationModel: string,\n mutationOp: string,\n mutationArgs: any,\n options: MutationOptions & ExtraMutationOptions,\n modelMeta: ModelMeta,\n queryCache: QueryCache,\n setCache: SetCacheFunc,\n logging = false\n) {\n for (const cacheItem of queryCache) {\n const {\n queryKey,\n state: { data, error },\n } = cacheItem;\n\n if (error) {\n if (logging) {\n console.warn(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to error:`, error);\n }\n continue;\n }\n\n const [_, queryModel, queryOperation, queryArgs, { optimisticUpdate }] = queryKey as QueryKey;\n if (!optimisticUpdate) {\n if (logging) {\n console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to opt-out`);\n }\n continue;\n }\n\n if (options.optimisticDataProvider) {\n const providerResult = await options.optimisticDataProvider({\n queryModel,\n queryOperation,\n queryArgs,\n currentData: data,\n mutationArgs,\n });\n\n if (providerResult?.kind === 'Skip') {\n // skip\n if (logging) {\n console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to provider`);\n }\n continue;\n } else if (providerResult?.kind === 'Update') {\n // update cache\n if (logging) {\n console.log(`Optimistically updating query ${JSON.stringify(queryKey)} due to provider`);\n }\n setCache(queryKey, providerResult.data);\n continue;\n }\n }\n\n // proceed with default optimistic update\n const mutatedData = await applyMutation(\n queryModel,\n queryOperation,\n data,\n mutationModel,\n mutationOp as PrismaWriteActionType,\n mutationArgs,\n modelMeta,\n logging\n );\n\n if (mutatedData !== undefined) {\n // mutation applicable to this query, update cache\n if (logging) {\n console.log(\n `Optimistically updating query ${JSON.stringify(\n queryKey\n )} due to mutation \"${mutationModel}.${mutationOp}\"`\n );\n }\n setCache(queryKey, mutatedData);\n }\n }\n}\n"],"mappings":";AAEA,SAAS,aAAa,iBAAiB;AACvC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OAGG;AACP,YAAY,gBAAgB;AAUrB,IAAM,mBAAmB;AAqKzB,SAAS,YACZ,OACA,gBACA,MACA,UAA4D,EAAE,UAAU,OAAO,kBAAkB,KAAK,GAC9F;AACR,MAAI,CAAC,gBAAgB;AACjB,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC5C;AACA,QAAM,YAAY,eAAe,MAAM,GAAG,EAAE,IAAI;AAEhD,QAAM,WAAW,QAAQ;AAEzB,QAAM,mBAAmB,QAAQ,WAAW,QAAQ,QAAQ;AAG5D,SAAO,CAAC,kBAAkB,OAAO,WAAY,MAAM,EAAE,UAAU,iBAAiB,CAAC;AACrF;","names":[]}
@@ -2,7 +2,7 @@ import * as _tanstack_react_query_v5_build_legacy_types from '@tanstack/react-qu
2
2
  import * as react from 'react';
3
3
  import { UseQueryOptions, UseSuspenseQueryOptions, UseInfiniteQueryOptions, InfiniteData, UseSuspenseInfiniteQueryOptions, UseMutationOptions } from '@tanstack/react-query';
4
4
  import { ModelMeta } from '@zenstackhq/runtime/cross';
5
- import { A as APIContext, F as FetchFn } from './common-ZSgIGN1q.mjs';
5
+ import { A as APIContext, F as FetchFn, a as ExtraQueryOptions, E as ExtraMutationOptions } from './common--_wRp2eo.mjs';
6
6
 
7
7
  /**
8
8
  * Context for configuring react hooks.
@@ -28,10 +28,9 @@ declare const Provider: react.Provider<APIContext>;
28
28
  * @param args The request args object, URL-encoded and appended as "?q=" parameter
29
29
  * @param options The react-query options object
30
30
  * @param fetch The fetch function to use for sending the HTTP request
31
- * @param optimisticUpdate Whether to enable automatic optimistic update
32
31
  * @returns useQuery hook
33
32
  */
34
- declare function useModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'>, fetch?: FetchFn, optimisticUpdate?: boolean): _tanstack_react_query_v5_build_legacy_types.UseQueryResult<TData, TError>;
33
+ declare function useModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'> & ExtraQueryOptions, fetch?: FetchFn): _tanstack_react_query_v5_build_legacy_types.UseQueryResult<TData, TError>;
35
34
  /**
36
35
  * Creates a react-query suspense query.
37
36
  *
@@ -40,10 +39,9 @@ declare function useModelQuery<TQueryFnData, TData, TError>(model: string, url:
40
39
  * @param args The request args object, URL-encoded and appended as "?q=" parameter
41
40
  * @param options The react-query options object
42
41
  * @param fetch The fetch function to use for sending the HTTP request
43
- * @param optimisticUpdate Whether to enable automatic optimistic update
44
42
  * @returns useSuspenseQuery hook
45
43
  */
46
- declare function useSuspenseModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseSuspenseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'>, fetch?: FetchFn, optimisticUpdate?: boolean): _tanstack_react_query_v5_build_legacy_types.UseSuspenseQueryResult<TData, TError>;
44
+ declare function useSuspenseModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseSuspenseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'> & ExtraQueryOptions, fetch?: FetchFn): _tanstack_react_query_v5_build_legacy_types.UseSuspenseQueryResult<TData, TError>;
47
45
  /**
48
46
  * Creates a react-query infinite query.
49
47
  *
@@ -75,10 +73,8 @@ declare function useSuspenseInfiniteModelQuery<TQueryFnData, TData, TError>(mode
75
73
  * @param modelMeta The model metadata.
76
74
  * @param options The react-query options.
77
75
  * @param fetch The fetch function to use for sending the HTTP request
78
- * @param invalidateQueries Whether to invalidate queries after mutation.
79
76
  * @param checkReadBack Whether to check for read back errors and return undefined if found.
80
- * @param optimisticUpdate Whether to enable automatic optimistic update
81
77
  */
82
- declare function useModelMutation<TArgs, TError, 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<UseMutationOptions<Result, TError, TArgs>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C, optimisticUpdate?: boolean): _tanstack_react_query_v5_build_legacy_types.UseMutationResult<Result, TError, TArgs, unknown>;
78
+ declare function useModelMutation<TArgs, TError, 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<UseMutationOptions<Result, TError, TArgs>, 'mutationFn'> & ExtraMutationOptions, fetch?: FetchFn, checkReadBack?: C): _tanstack_react_query_v5_build_legacy_types.UseMutationResult<Result, TError, TArgs, unknown>;
83
79
 
84
80
  export { Provider, RequestHandlerContext, getHooksContext, useInfiniteModelQuery, useModelMutation, useModelQuery, useSuspenseInfiniteModelQuery, useSuspenseModelQuery };
@@ -2,7 +2,7 @@ import * as _tanstack_react_query_v5_build_legacy_types from '@tanstack/react-qu
2
2
  import * as react from 'react';
3
3
  import { UseQueryOptions, UseSuspenseQueryOptions, UseInfiniteQueryOptions, InfiniteData, UseSuspenseInfiniteQueryOptions, UseMutationOptions } from '@tanstack/react-query';
4
4
  import { ModelMeta } from '@zenstackhq/runtime/cross';
5
- import { A as APIContext, F as FetchFn } from './common-ZSgIGN1q.js';
5
+ import { A as APIContext, F as FetchFn, a as ExtraQueryOptions, E as ExtraMutationOptions } from './common--_wRp2eo.js';
6
6
 
7
7
  /**
8
8
  * Context for configuring react hooks.
@@ -28,10 +28,9 @@ declare const Provider: react.Provider<APIContext>;
28
28
  * @param args The request args object, URL-encoded and appended as "?q=" parameter
29
29
  * @param options The react-query options object
30
30
  * @param fetch The fetch function to use for sending the HTTP request
31
- * @param optimisticUpdate Whether to enable automatic optimistic update
32
31
  * @returns useQuery hook
33
32
  */
34
- declare function useModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'>, fetch?: FetchFn, optimisticUpdate?: boolean): _tanstack_react_query_v5_build_legacy_types.UseQueryResult<TData, TError>;
33
+ declare function useModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'> & ExtraQueryOptions, fetch?: FetchFn): _tanstack_react_query_v5_build_legacy_types.UseQueryResult<TData, TError>;
35
34
  /**
36
35
  * Creates a react-query suspense query.
37
36
  *
@@ -40,10 +39,9 @@ declare function useModelQuery<TQueryFnData, TData, TError>(model: string, url:
40
39
  * @param args The request args object, URL-encoded and appended as "?q=" parameter
41
40
  * @param options The react-query options object
42
41
  * @param fetch The fetch function to use for sending the HTTP request
43
- * @param optimisticUpdate Whether to enable automatic optimistic update
44
42
  * @returns useSuspenseQuery hook
45
43
  */
46
- declare function useSuspenseModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseSuspenseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'>, fetch?: FetchFn, optimisticUpdate?: boolean): _tanstack_react_query_v5_build_legacy_types.UseSuspenseQueryResult<TData, TError>;
44
+ declare function useSuspenseModelQuery<TQueryFnData, TData, TError>(model: string, url: string, args?: unknown, options?: Omit<UseSuspenseQueryOptions<TQueryFnData, TError, TData>, 'queryKey'> & ExtraQueryOptions, fetch?: FetchFn): _tanstack_react_query_v5_build_legacy_types.UseSuspenseQueryResult<TData, TError>;
47
45
  /**
48
46
  * Creates a react-query infinite query.
49
47
  *
@@ -75,10 +73,8 @@ declare function useSuspenseInfiniteModelQuery<TQueryFnData, TData, TError>(mode
75
73
  * @param modelMeta The model metadata.
76
74
  * @param options The react-query options.
77
75
  * @param fetch The fetch function to use for sending the HTTP request
78
- * @param invalidateQueries Whether to invalidate queries after mutation.
79
76
  * @param checkReadBack Whether to check for read back errors and return undefined if found.
80
- * @param optimisticUpdate Whether to enable automatic optimistic update
81
77
  */
82
- declare function useModelMutation<TArgs, TError, 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<UseMutationOptions<Result, TError, TArgs>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C, optimisticUpdate?: boolean): _tanstack_react_query_v5_build_legacy_types.UseMutationResult<Result, TError, TArgs, unknown>;
78
+ declare function useModelMutation<TArgs, TError, 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<UseMutationOptions<Result, TError, TArgs>, 'mutationFn'> & ExtraMutationOptions, fetch?: FetchFn, checkReadBack?: C): _tanstack_react_query_v5_build_legacy_types.UseMutationResult<Result, TError, TArgs, unknown>;
83
79
 
84
80
  export { Provider, RequestHandlerContext, getHooksContext, useInfiniteModelQuery, useModelMutation, useModelQuery, useSuspenseInfiniteModelQuery, useSuspenseModelQuery };
@@ -122,11 +122,13 @@ function fetcher(url, options, fetch2, checkReadBack) {
122
122
  }
123
123
  });
124
124
  }
125
- function getQueryKey(model, urlOrOperation, args, infinite = false, optimisticUpdate2 = false) {
125
+ function getQueryKey(model, urlOrOperation, args, options = { infinite: false, optimisticUpdate: true }) {
126
126
  if (!urlOrOperation) {
127
127
  throw new Error("Invalid urlOrOperation");
128
128
  }
129
129
  const operation = urlOrOperation.split("/").pop();
130
+ const infinite = options.infinite;
131
+ const optimisticUpdate2 = options.infinite ? false : options.optimisticUpdate;
130
132
  return [QUERY_KEY_PREFIX, model, operation, args, { infinite, optimisticUpdate: optimisticUpdate2 }];
131
133
  }
132
134
  function marshal(value) {
@@ -211,6 +213,7 @@ function setupOptimisticUpdate(model, operation, modelMeta, options, queryCache,
211
213
  model,
212
214
  operation,
213
215
  variables,
216
+ options,
214
217
  modelMeta,
215
218
  queryCache,
216
219
  setCache,
@@ -233,7 +236,7 @@ function setupOptimisticUpdate(model, operation, modelMeta, options, queryCache,
233
236
  return origOnSettled == null ? void 0 : origOnSettled(...args);
234
237
  });
235
238
  }
236
- function optimisticUpdate(mutationModel, mutationOp, mutationArgs, modelMeta, queryCache, setCache, logging = false) {
239
+ function optimisticUpdate(mutationModel, mutationOp, mutationArgs, options, modelMeta, queryCache, setCache, logging = false) {
237
240
  return __async(this, null, function* () {
238
241
  for (const cacheItem of queryCache) {
239
242
  const {
@@ -246,16 +249,37 @@ function optimisticUpdate(mutationModel, mutationOp, mutationArgs, modelMeta, qu
246
249
  }
247
250
  continue;
248
251
  }
249
- const [_, queryModel, queryOp, _queryArgs, { optimisticUpdate: optimisticUpdate2 }] = queryKey;
252
+ const [_, queryModel, queryOperation, queryArgs, { optimisticUpdate: optimisticUpdate2 }] = queryKey;
250
253
  if (!optimisticUpdate2) {
251
254
  if (logging) {
252
255
  console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to opt-out`);
253
256
  }
254
257
  continue;
255
258
  }
259
+ if (options.optimisticDataProvider) {
260
+ const providerResult = yield options.optimisticDataProvider({
261
+ queryModel,
262
+ queryOperation,
263
+ queryArgs,
264
+ currentData: data,
265
+ mutationArgs
266
+ });
267
+ if ((providerResult == null ? void 0 : providerResult.kind) === "Skip") {
268
+ if (logging) {
269
+ console.log(`Skipping optimistic update for ${JSON.stringify(queryKey)} due to provider`);
270
+ }
271
+ continue;
272
+ } else if ((providerResult == null ? void 0 : providerResult.kind) === "Update") {
273
+ if (logging) {
274
+ console.log(`Optimistically updating query ${JSON.stringify(queryKey)} due to provider`);
275
+ }
276
+ setCache(queryKey, providerResult.data);
277
+ continue;
278
+ }
279
+ }
256
280
  const mutatedData = yield (0, import_cross.applyMutation)(
257
281
  queryModel,
258
- queryOp,
282
+ queryOperation,
259
283
  data,
260
284
  mutationModel,
261
285
  mutationOp,
@@ -287,23 +311,29 @@ function getHooksContext() {
287
311
  return __spreadValues({ endpoint: endpoint != null ? endpoint : DEFAULT_QUERY_ENDPOINT }, rest);
288
312
  }
289
313
  var Provider = RequestHandlerContext.Provider;
290
- function useModelQuery(model, url, args, options, fetch2, optimisticUpdate2 = false) {
314
+ function useModelQuery(model, url, args, options, fetch2) {
291
315
  const reqUrl = makeUrl(url, args);
292
316
  return (0, import_react_query_v5.useQuery)(__spreadValues({
293
- queryKey: getQueryKey(model, url, args, false, optimisticUpdate2),
317
+ queryKey: getQueryKey(model, url, args, {
318
+ infinite: false,
319
+ optimisticUpdate: (options == null ? void 0 : options.optimisticUpdate) !== false
320
+ }),
294
321
  queryFn: () => fetcher(reqUrl, void 0, fetch2, false)
295
322
  }, options));
296
323
  }
297
- function useSuspenseModelQuery(model, url, args, options, fetch2, optimisticUpdate2 = false) {
324
+ function useSuspenseModelQuery(model, url, args, options, fetch2) {
298
325
  const reqUrl = makeUrl(url, args);
299
326
  return (0, import_react_query_v5.useSuspenseQuery)(__spreadValues({
300
- queryKey: getQueryKey(model, url, args, false, optimisticUpdate2),
327
+ queryKey: getQueryKey(model, url, args, {
328
+ infinite: false,
329
+ optimisticUpdate: (options == null ? void 0 : options.optimisticUpdate) !== false
330
+ }),
301
331
  queryFn: () => fetcher(reqUrl, void 0, fetch2, false)
302
332
  }, options));
303
333
  }
304
334
  function useInfiniteModelQuery(model, url, args, options, fetch2) {
305
335
  return (0, import_react_query_v5.useInfiniteQuery)(__spreadValues({
306
- queryKey: getQueryKey(model, url, args, true),
336
+ queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }),
307
337
  queryFn: ({ pageParam }) => {
308
338
  return fetcher(makeUrl(url, pageParam != null ? pageParam : args), void 0, fetch2, false);
309
339
  }
@@ -311,13 +341,13 @@ function useInfiniteModelQuery(model, url, args, options, fetch2) {
311
341
  }
312
342
  function useSuspenseInfiniteModelQuery(model, url, args, options, fetch2) {
313
343
  return (0, import_react_query_v5.useSuspenseInfiniteQuery)(__spreadValues({
314
- queryKey: getQueryKey(model, url, args, true),
344
+ queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }),
315
345
  queryFn: ({ pageParam }) => {
316
346
  return fetcher(makeUrl(url, pageParam != null ? pageParam : args), void 0, fetch2, false);
317
347
  }
318
348
  }, options));
319
349
  }
320
- function useModelMutation(model, method, url, modelMeta, options, fetch2, invalidateQueries = true, checkReadBack, optimisticUpdate2 = false) {
350
+ function useModelMutation(model, method, url, modelMeta, options, fetch2, checkReadBack) {
321
351
  const queryClient = (0, import_react_query_v5.useQueryClient)();
322
352
  const mutationFn = (data) => {
323
353
  const reqUrl = method === "DELETE" ? makeUrl(url, data) : url;
@@ -333,6 +363,8 @@ function useModelMutation(model, method, url, modelMeta, options, fetch2, invali
333
363
  };
334
364
  const finalOptions = __spreadProps(__spreadValues({}, options), { mutationFn });
335
365
  const operation = url.split("/").pop();
366
+ const invalidateQueries = (options == null ? void 0 : options.invalidateQueries) !== false;
367
+ const optimisticUpdate2 = !!(options == null ? void 0 : options.optimisticUpdate);
336
368
  if (operation) {
337
369
  const { logging } = (0, import_react.useContext)(RequestHandlerContext);
338
370
  if (invalidateQueries) {