@zenstackhq/tanstack-query 1.1.1 → 1.2.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 (54) hide show
  1. package/generator.js +70 -37
  2. package/generator.js.map +1 -1
  3. package/package.json +40 -9
  4. package/runtime/common-5e18c135.d.ts +32 -0
  5. package/runtime/index.d.mts +1 -1
  6. package/runtime/index.d.ts +1 -1
  7. package/runtime/index.js +34 -0
  8. package/runtime/index.js.map +1 -1
  9. package/runtime/index.mjs +15 -0
  10. package/runtime/index.mjs.map +1 -1
  11. package/runtime/react.d.mts +17 -26
  12. package/runtime/react.d.ts +17 -26
  13. package/runtime/react.js +105 -72
  14. package/runtime/react.js.map +1 -1
  15. package/runtime/react.mjs +102 -68
  16. package/runtime/react.mjs.map +1 -1
  17. package/runtime/svelte.d.mts +16 -25
  18. package/runtime/svelte.d.ts +16 -25
  19. package/runtime/svelte.js +97 -71
  20. package/runtime/svelte.js.map +1 -1
  21. package/runtime/svelte.mjs +93 -66
  22. package/runtime/svelte.mjs.map +1 -1
  23. package/runtime/vue.d.mts +17 -27
  24. package/runtime/vue.d.ts +17 -27
  25. package/runtime/vue.js +105 -77
  26. package/runtime/vue.js.map +1 -1
  27. package/runtime/vue.mjs +101 -71
  28. package/runtime/vue.mjs.map +1 -1
  29. package/runtime-v5/common-5e18c135.d.ts +32 -0
  30. package/runtime-v5/index.d.mts +20 -0
  31. package/runtime-v5/index.d.ts +20 -0
  32. package/runtime-v5/index.js +53 -0
  33. package/runtime-v5/index.js.map +1 -0
  34. package/runtime-v5/index.mjs +16 -0
  35. package/runtime-v5/index.mjs.map +1 -0
  36. package/runtime-v5/react.d.mts +45 -0
  37. package/runtime-v5/react.d.ts +45 -0
  38. package/runtime-v5/react.js +269 -0
  39. package/runtime-v5/react.js.map +1 -0
  40. package/runtime-v5/react.mjs +237 -0
  41. package/runtime-v5/react.mjs.map +1 -0
  42. package/runtime-v5/svelte.d.mts +56 -0
  43. package/runtime-v5/svelte.d.ts +56 -0
  44. package/runtime-v5/svelte.js +296 -0
  45. package/runtime-v5/svelte.js.map +1 -0
  46. package/runtime-v5/svelte.mjs +264 -0
  47. package/runtime-v5/svelte.mjs.map +1 -0
  48. package/runtime-v5/vue.d.mts +52 -0
  49. package/runtime-v5/vue.d.ts +52 -0
  50. package/runtime-v5/vue.js +274 -0
  51. package/runtime-v5/vue.js.map +1 -0
  52. package/runtime-v5/vue.mjs +240 -0
  53. package/runtime-v5/vue.mjs.map +1 -0
  54. package/runtime/common-83308e88.d.ts +0 -19
@@ -1,7 +1,8 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
2
  import { UseQueryOptions, UseInfiniteQueryOptions, UseMutationOptions } from '@tanstack/react-query';
3
3
  import * as react from 'react';
4
- import { A as APIContext, F as FetchFn } from './common-83308e88.js';
4
+ import { ModelMeta } from '@zenstackhq/runtime/cross';
5
+ import { A as APIContext, F as FetchFn } from './common-5e18c135.js';
5
6
 
6
7
  /**
7
8
  * Context for configuring react hooks.
@@ -11,6 +12,14 @@ declare const RequestHandlerContext: react.Context<APIContext>;
11
12
  * Context provider.
12
13
  */
13
14
  declare const Provider: react.Provider<APIContext>;
15
+ /**
16
+ * Hooks context.
17
+ */
18
+ declare function getHooksContext(): {
19
+ fetch?: FetchFn | undefined;
20
+ logging?: boolean | undefined;
21
+ endpoint: string;
22
+ };
14
23
  /**
15
24
  * Creates a react-query query.
16
25
  *
@@ -20,7 +29,7 @@ declare const Provider: react.Provider<APIContext>;
20
29
  * @param options The react-query options object
21
30
  * @returns useQuery hook
22
31
  */
23
- declare function query<R>(model: string, url: string, args?: unknown, options?: UseQueryOptions<R>, fetch?: FetchFn): _tanstack_react_query.UseQueryResult<R, unknown>;
32
+ declare function useModelQuery<R>(model: string, url: string, args?: unknown, options?: Omit<UseQueryOptions<R>, 'queryKey'>, fetch?: FetchFn): _tanstack_react_query.UseQueryResult<R, unknown>;
24
33
  /**
25
34
  * Creates a react-query infinite query.
26
35
  *
@@ -30,36 +39,18 @@ declare function query<R>(model: string, url: string, args?: unknown, options?:
30
39
  * @param options The react-query infinite query options object
31
40
  * @returns useInfiniteQuery hook
32
41
  */
33
- declare function infiniteQuery<R>(model: string, url: string, args?: unknown, options?: UseInfiniteQueryOptions<R>, fetch?: FetchFn): _tanstack_react_query.UseInfiniteQueryResult<R, unknown>;
34
- /**
35
- * Creates a POST mutation with react-query.
36
- *
37
- * @param model The name of the model under mutation.
38
- * @param url The request URL.
39
- * @param options The react-query options.
40
- * @param invalidateQueries Whether to invalidate queries after mutation.
41
- * @returns useMutation hooks
42
- */
43
- declare function postMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, url: string, options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_react_query.UseMutationResult<Result, unknown, T, unknown>;
44
- /**
45
- * Creates a PUT mutation with react-query.
46
- *
47
- * @param model The name of the model under mutation.
48
- * @param url The request URL.
49
- * @param options The react-query options.
50
- * @param invalidateQueries Whether to invalidate queries after mutation.
51
- * @returns useMutation hooks
52
- */
53
- declare function putMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, url: string, options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_react_query.UseMutationResult<Result, unknown, T, unknown>;
42
+ declare function useInfiniteModelQuery<R>(model: string, url: string, args?: unknown, options?: Omit<UseInfiniteQueryOptions<R>, 'queryKey'>, fetch?: FetchFn): _tanstack_react_query.UseInfiniteQueryResult<R, unknown>;
54
43
  /**
55
- * Creates a DELETE mutation with react-query.
44
+ * Creates a mutation with react-query.
56
45
  *
57
46
  * @param model The name of the model under mutation.
47
+ * @param method The HTTP method.
48
+ * @param modelMeta The model metadata.
58
49
  * @param url The request URL.
59
50
  * @param options The react-query options.
60
51
  * @param invalidateQueries Whether to invalidate queries after mutation.
61
52
  * @returns useMutation hooks
62
53
  */
63
- declare function deleteMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(model: string, url: string, options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_react_query.UseMutationResult<Result, unknown, T, unknown>;
54
+ 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<UseMutationOptions<Result, unknown, T>, 'mutationFn'>, fetch?: FetchFn, invalidateQueries?: boolean, checkReadBack?: C): _tanstack_react_query.UseMutationResult<Result, unknown, T, unknown>;
64
55
 
65
- export { Provider, RequestHandlerContext, deleteMutation, infiniteQuery, postMutation, putMutation, query };
56
+ export { Provider, RequestHandlerContext, getHooksContext, useInfiniteModelQuery, useModelMutation, useModelQuery };
package/runtime/react.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 });
@@ -69,11 +81,10 @@ var react_exports = {};
69
81
  __export(react_exports, {
70
82
  Provider: () => Provider,
71
83
  RequestHandlerContext: () => RequestHandlerContext,
72
- deleteMutation: () => deleteMutation,
73
- infiniteQuery: () => infiniteQuery,
74
- postMutation: () => postMutation,
75
- putMutation: () => putMutation,
76
- query: () => query
84
+ getHooksContext: () => getHooksContext,
85
+ useInfiniteModelQuery: () => useInfiniteModelQuery,
86
+ useModelMutation: () => useModelMutation,
87
+ useModelQuery: () => useModelQuery
77
88
  });
78
89
  module.exports = __toCommonJS(react_exports);
79
90
  var import_react_query = require("@tanstack/react-query");
@@ -81,9 +92,10 @@ var import_react = require("react");
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
97
  var DEFAULT_QUERY_ENDPOINT = "/api/model";
86
- var QUERY_KEY_PREFIX = "zenstack:";
98
+ var QUERY_KEY_PREFIX = "zenstack";
87
99
  function fetcher(url, options, fetch2, checkReadBack) {
88
100
  return __async(this, null, function* () {
89
101
  var _a, _b, _c;
@@ -110,6 +122,13 @@ function fetcher(url, options, fetch2, checkReadBack) {
110
122
  }
111
123
  });
112
124
  }
125
+ function getQueryKey(model, urlOrOperation, args) {
126
+ if (!urlOrOperation) {
127
+ throw new Error("Invalid urlOrOperation");
128
+ }
129
+ const operation = urlOrOperation.split("/").pop();
130
+ return [QUERY_KEY_PREFIX, model, operation, args];
131
+ }
113
132
  function marshal(value) {
114
133
  const { data, meta } = (0, import_browser.serialize)(value);
115
134
  if (meta) {
@@ -139,99 +158,113 @@ function makeUrl(url, args) {
139
158
  }
140
159
  return result;
141
160
  }
161
+ function setupInvalidation(model, operation, modelMeta, options, invalidate, logging = false) {
162
+ const origOnSuccess = options == null ? void 0 : options.onSuccess;
163
+ options.onSuccess = (...args) => __async(this, null, function* () {
164
+ const [_, variables] = args;
165
+ const predicate = yield getInvalidationPredicate(
166
+ model,
167
+ operation,
168
+ variables,
169
+ modelMeta,
170
+ logging
171
+ );
172
+ yield invalidate(predicate);
173
+ return origOnSuccess == null ? void 0 : origOnSuccess(...args);
174
+ });
175
+ }
176
+ function getInvalidationPredicate(model, operation, mutationArgs, modelMeta, logging = false) {
177
+ return __async(this, null, function* () {
178
+ const mutatedModels = yield (0, import_cross.getMutatedModels)(model, operation, mutationArgs, modelMeta);
179
+ return ({ queryKey }) => {
180
+ const [_model, queryModel, queryOp, args] = queryKey;
181
+ if (mutatedModels.includes(queryModel)) {
182
+ if (logging) {
183
+ console.log(`Invalidating query [${queryKey}] due to mutation "${model}.${operation}"`);
184
+ }
185
+ return true;
186
+ }
187
+ if (args) {
188
+ if (findNestedRead(queryModel, mutatedModels, modelMeta, args)) {
189
+ if (logging) {
190
+ console.log(`Invalidating query [${queryKey}] due to mutation "${model}.${operation}"`);
191
+ }
192
+ return true;
193
+ }
194
+ }
195
+ return false;
196
+ };
197
+ });
198
+ }
199
+ function findNestedRead(visitingModel, targetModels, modelMeta, args) {
200
+ const modelsRead = (0, import_cross.getReadModels)(visitingModel, modelMeta, args);
201
+ return targetModels.some((m) => modelsRead.includes(m));
202
+ }
142
203
 
143
204
  // src/runtime/react.ts
144
205
  var RequestHandlerContext = (0, import_react.createContext)({
145
206
  endpoint: DEFAULT_QUERY_ENDPOINT,
146
- fetch: void 0
207
+ fetch: void 0,
208
+ logging: false
147
209
  });
148
210
  var Provider = RequestHandlerContext.Provider;
149
- function query(model, url, args, options, fetch2) {
211
+ function getHooksContext() {
212
+ const _a = (0, import_react.useContext)(RequestHandlerContext), { endpoint } = _a, rest = __objRest(_a, ["endpoint"]);
213
+ return __spreadValues({ endpoint: endpoint != null ? endpoint : DEFAULT_QUERY_ENDPOINT }, rest);
214
+ }
215
+ function useModelQuery(model, url, args, options, fetch2) {
150
216
  const reqUrl = makeUrl(url, args);
151
217
  return (0, import_react_query.useQuery)(__spreadValues({
152
- queryKey: [QUERY_KEY_PREFIX + model, url, args],
218
+ queryKey: getQueryKey(model, url, args),
153
219
  queryFn: () => fetcher(reqUrl, void 0, fetch2, false)
154
220
  }, options));
155
221
  }
156
- function infiniteQuery(model, url, args, options, fetch2) {
222
+ function useInfiniteModelQuery(model, url, args, options, fetch2) {
157
223
  return (0, import_react_query.useInfiniteQuery)(__spreadValues({
158
- queryKey: [QUERY_KEY_PREFIX + model, url, args],
224
+ queryKey: getQueryKey(model, url, args),
159
225
  queryFn: ({ pageParam }) => {
160
226
  return fetcher(makeUrl(url, pageParam != null ? pageParam : args), void 0, fetch2, false);
161
227
  }
162
228
  }, options));
163
229
  }
164
- function postMutation(model, url, options, fetch2, invalidateQueries = true, checkReadBack) {
230
+ function useModelMutation(model, method, url, modelMeta, options, fetch2, invalidateQueries = true, checkReadBack) {
165
231
  const queryClient = (0, import_react_query.useQueryClient)();
166
- const mutationFn = (data) => fetcher(
167
- url,
168
- {
169
- method: "POST",
232
+ const mutationFn = (data) => {
233
+ const reqUrl = method === "DELETE" ? makeUrl(url, data) : url;
234
+ const fetchInit = __spreadValues({
235
+ method
236
+ }, method !== "DELETE" && {
170
237
  headers: {
171
238
  "content-type": "application/json"
172
239
  },
173
240
  body: marshal(data)
174
- },
175
- fetch2,
176
- checkReadBack
177
- );
178
- const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
179
- const mutation = (0, import_react_query.useMutation)(finalOptions);
180
- return mutation;
181
- }
182
- function putMutation(model, url, options, fetch2, invalidateQueries = true, checkReadBack) {
183
- const queryClient = (0, import_react_query.useQueryClient)();
184
- const mutationFn = (data) => fetcher(
185
- url,
186
- {
187
- method: "PUT",
188
- headers: {
189
- "content-type": "application/json"
190
- },
191
- body: marshal(data)
192
- },
193
- fetch2,
194
- checkReadBack
195
- );
196
- const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
197
- const mutation = (0, import_react_query.useMutation)(finalOptions);
198
- return mutation;
199
- }
200
- function deleteMutation(model, url, options, fetch2, invalidateQueries = true, checkReadBack) {
201
- const queryClient = (0, import_react_query.useQueryClient)();
202
- const mutationFn = (data) => fetcher(
203
- makeUrl(url, data),
204
- {
205
- method: "DELETE"
206
- },
207
- fetch2,
208
- checkReadBack
209
- );
210
- const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
211
- const mutation = (0, import_react_query.useMutation)(finalOptions);
212
- return mutation;
213
- }
214
- function mergeOptions(model, options, invalidateQueries, mutationFn, queryClient) {
215
- const result = __spreadProps(__spreadValues({}, options), { mutationFn });
216
- if ((options == null ? void 0 : options.onSuccess) || invalidateQueries) {
217
- result.onSuccess = (...args) => {
218
- var _a;
219
- if (invalidateQueries) {
220
- queryClient.invalidateQueries([QUERY_KEY_PREFIX + model]);
221
- }
222
- return (_a = options == null ? void 0 : options.onSuccess) == null ? void 0 : _a.call(options, ...args);
223
- };
241
+ });
242
+ return fetcher(reqUrl, fetchInit, fetch2, checkReadBack);
243
+ };
244
+ const finalOptions = __spreadProps(__spreadValues({}, options), { mutationFn });
245
+ if (invalidateQueries) {
246
+ const { logging } = (0, import_react.useContext)(RequestHandlerContext);
247
+ const operation = url.split("/").pop();
248
+ if (operation) {
249
+ setupInvalidation(
250
+ model,
251
+ operation,
252
+ modelMeta,
253
+ finalOptions,
254
+ (predicate) => queryClient.invalidateQueries({ predicate }),
255
+ logging
256
+ );
257
+ }
224
258
  }
225
- return result;
259
+ return (0, import_react_query.useMutation)(finalOptions);
226
260
  }
227
261
  // Annotate the CommonJS export names for ESM import in node:
228
262
  0 && (module.exports = {
229
263
  Provider,
230
264
  RequestHandlerContext,
231
- deleteMutation,
232
- infiniteQuery,
233
- postMutation,
234
- putMutation,
235
- query
265
+ getHooksContext,
266
+ useInfiniteModelQuery,
267
+ useModelMutation,
268
+ useModelQuery
236
269
  });
237
270
  //# sourceMappingURL=react.js.map
@@ -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/react.mjs CHANGED
@@ -17,6 +17,18 @@ var __spreadValues = (a, b) => {
17
17
  return a;
18
18
  };
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
20
32
  var __async = (__this, __arguments, generator) => {
21
33
  return new Promise((resolve, reject) => {
22
34
  var fulfilled = (value) => {
@@ -45,13 +57,14 @@ import {
45
57
  useQuery,
46
58
  useQueryClient
47
59
  } from "@tanstack/react-query";
48
- import { createContext } from "react";
60
+ import { createContext, useContext } from "react";
49
61
 
50
62
  // src/runtime/common.ts
51
63
  import { deserialize, serialize } from "@zenstackhq/runtime/browser";
64
+ import { getMutatedModels, getReadModels } from "@zenstackhq/runtime/cross";
52
65
  import * as crossFetch from "cross-fetch";
53
66
  var DEFAULT_QUERY_ENDPOINT = "/api/model";
54
- var QUERY_KEY_PREFIX = "zenstack:";
67
+ var QUERY_KEY_PREFIX = "zenstack";
55
68
  function fetcher(url, options, fetch2, checkReadBack) {
56
69
  return __async(this, null, function* () {
57
70
  var _a, _b, _c;
@@ -78,6 +91,13 @@ function fetcher(url, options, fetch2, checkReadBack) {
78
91
  }
79
92
  });
80
93
  }
94
+ function getQueryKey(model, urlOrOperation, args) {
95
+ if (!urlOrOperation) {
96
+ throw new Error("Invalid urlOrOperation");
97
+ }
98
+ const operation = urlOrOperation.split("/").pop();
99
+ return [QUERY_KEY_PREFIX, model, operation, args];
100
+ }
81
101
  function marshal(value) {
82
102
  const { data, meta } = serialize(value);
83
103
  if (meta) {
@@ -107,98 +127,112 @@ function makeUrl(url, args) {
107
127
  }
108
128
  return result;
109
129
  }
130
+ function setupInvalidation(model, operation, modelMeta, options, invalidate, logging = false) {
131
+ const origOnSuccess = options == null ? void 0 : options.onSuccess;
132
+ options.onSuccess = (...args) => __async(this, null, function* () {
133
+ const [_, variables] = args;
134
+ const predicate = yield getInvalidationPredicate(
135
+ model,
136
+ operation,
137
+ variables,
138
+ modelMeta,
139
+ logging
140
+ );
141
+ yield invalidate(predicate);
142
+ return origOnSuccess == null ? void 0 : origOnSuccess(...args);
143
+ });
144
+ }
145
+ function getInvalidationPredicate(model, operation, mutationArgs, modelMeta, logging = false) {
146
+ return __async(this, null, function* () {
147
+ const mutatedModels = yield getMutatedModels(model, operation, mutationArgs, modelMeta);
148
+ return ({ queryKey }) => {
149
+ const [_model, queryModel, queryOp, args] = queryKey;
150
+ if (mutatedModels.includes(queryModel)) {
151
+ if (logging) {
152
+ console.log(`Invalidating query [${queryKey}] due to mutation "${model}.${operation}"`);
153
+ }
154
+ return true;
155
+ }
156
+ if (args) {
157
+ if (findNestedRead(queryModel, mutatedModels, modelMeta, args)) {
158
+ if (logging) {
159
+ console.log(`Invalidating query [${queryKey}] due to mutation "${model}.${operation}"`);
160
+ }
161
+ return true;
162
+ }
163
+ }
164
+ return false;
165
+ };
166
+ });
167
+ }
168
+ function findNestedRead(visitingModel, targetModels, modelMeta, args) {
169
+ const modelsRead = getReadModels(visitingModel, modelMeta, args);
170
+ return targetModels.some((m) => modelsRead.includes(m));
171
+ }
110
172
 
111
173
  // src/runtime/react.ts
112
174
  var RequestHandlerContext = createContext({
113
175
  endpoint: DEFAULT_QUERY_ENDPOINT,
114
- fetch: void 0
176
+ fetch: void 0,
177
+ logging: false
115
178
  });
116
179
  var Provider = RequestHandlerContext.Provider;
117
- function query(model, url, args, options, fetch2) {
180
+ function getHooksContext() {
181
+ const _a = useContext(RequestHandlerContext), { endpoint } = _a, rest = __objRest(_a, ["endpoint"]);
182
+ return __spreadValues({ endpoint: endpoint != null ? endpoint : DEFAULT_QUERY_ENDPOINT }, rest);
183
+ }
184
+ function useModelQuery(model, url, args, options, fetch2) {
118
185
  const reqUrl = makeUrl(url, args);
119
186
  return useQuery(__spreadValues({
120
- queryKey: [QUERY_KEY_PREFIX + model, url, args],
187
+ queryKey: getQueryKey(model, url, args),
121
188
  queryFn: () => fetcher(reqUrl, void 0, fetch2, false)
122
189
  }, options));
123
190
  }
124
- function infiniteQuery(model, url, args, options, fetch2) {
191
+ function useInfiniteModelQuery(model, url, args, options, fetch2) {
125
192
  return useInfiniteQuery(__spreadValues({
126
- queryKey: [QUERY_KEY_PREFIX + model, url, args],
193
+ queryKey: getQueryKey(model, url, args),
127
194
  queryFn: ({ pageParam }) => {
128
195
  return fetcher(makeUrl(url, pageParam != null ? pageParam : args), void 0, fetch2, false);
129
196
  }
130
197
  }, options));
131
198
  }
132
- function postMutation(model, url, options, fetch2, invalidateQueries = true, checkReadBack) {
199
+ function useModelMutation(model, method, url, modelMeta, options, fetch2, invalidateQueries = true, checkReadBack) {
133
200
  const queryClient = useQueryClient();
134
- const mutationFn = (data) => fetcher(
135
- url,
136
- {
137
- method: "POST",
201
+ const mutationFn = (data) => {
202
+ const reqUrl = method === "DELETE" ? makeUrl(url, data) : url;
203
+ const fetchInit = __spreadValues({
204
+ method
205
+ }, method !== "DELETE" && {
138
206
  headers: {
139
207
  "content-type": "application/json"
140
208
  },
141
209
  body: marshal(data)
142
- },
143
- fetch2,
144
- checkReadBack
145
- );
146
- const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
147
- const mutation = useMutation(finalOptions);
148
- return mutation;
149
- }
150
- function putMutation(model, url, options, fetch2, invalidateQueries = true, checkReadBack) {
151
- const queryClient = useQueryClient();
152
- const mutationFn = (data) => fetcher(
153
- url,
154
- {
155
- method: "PUT",
156
- headers: {
157
- "content-type": "application/json"
158
- },
159
- body: marshal(data)
160
- },
161
- fetch2,
162
- checkReadBack
163
- );
164
- const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
165
- const mutation = useMutation(finalOptions);
166
- return mutation;
167
- }
168
- function deleteMutation(model, url, options, fetch2, invalidateQueries = true, checkReadBack) {
169
- const queryClient = useQueryClient();
170
- const mutationFn = (data) => fetcher(
171
- makeUrl(url, data),
172
- {
173
- method: "DELETE"
174
- },
175
- fetch2,
176
- checkReadBack
177
- );
178
- const finalOptions = mergeOptions(model, options, invalidateQueries, mutationFn, queryClient);
179
- const mutation = useMutation(finalOptions);
180
- return mutation;
181
- }
182
- function mergeOptions(model, options, invalidateQueries, mutationFn, queryClient) {
183
- const result = __spreadProps(__spreadValues({}, options), { mutationFn });
184
- if ((options == null ? void 0 : options.onSuccess) || invalidateQueries) {
185
- result.onSuccess = (...args) => {
186
- var _a;
187
- if (invalidateQueries) {
188
- queryClient.invalidateQueries([QUERY_KEY_PREFIX + model]);
189
- }
190
- return (_a = options == null ? void 0 : options.onSuccess) == null ? void 0 : _a.call(options, ...args);
191
- };
210
+ });
211
+ return fetcher(reqUrl, fetchInit, fetch2, checkReadBack);
212
+ };
213
+ const finalOptions = __spreadProps(__spreadValues({}, options), { mutationFn });
214
+ if (invalidateQueries) {
215
+ const { logging } = useContext(RequestHandlerContext);
216
+ const operation = url.split("/").pop();
217
+ if (operation) {
218
+ setupInvalidation(
219
+ model,
220
+ operation,
221
+ modelMeta,
222
+ finalOptions,
223
+ (predicate) => queryClient.invalidateQueries({ predicate }),
224
+ logging
225
+ );
226
+ }
192
227
  }
193
- return result;
228
+ return useMutation(finalOptions);
194
229
  }
195
230
  export {
196
231
  Provider,
197
232
  RequestHandlerContext,
198
- deleteMutation,
199
- infiniteQuery,
200
- postMutation,
201
- putMutation,
202
- query
233
+ getHooksContext,
234
+ useInfiniteModelQuery,
235
+ useModelMutation,
236
+ useModelQuery
203
237
  };
204
238
  //# sourceMappingURL=react.mjs.map