@qualisero/openapi-endpoint 0.13.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,64 +1,43 @@
1
1
  import { computed, ref } from 'vue';
2
2
  import { useMutation } from '@tanstack/vue-query';
3
- import { HttpMethod, } from './types.js';
3
+ import { HttpMethod, isMutationMethod, } from './types.js';
4
4
  import { isPathResolved, normalizeParamsOptions, useResolvedOperation, resolvePath, generateQueryKey, } from './openapi-utils.js';
5
5
  /**
6
- * Execute a type-safe mutation (POST/PUT/PATCH/DELETE) with automatic cache updates.
6
+ * Execute a type-safe mutation (POST/PUT/PATCH/DELETE) with automatic cache management.
7
7
  *
8
- * Ensures the operation is a mutation at runtime and returns a reactive mutation object
9
- * with helpers for path resolution and cache invalidation.
8
+ * This is a low-level primitive in normal usage it is called by the generated
9
+ * per-operation `useMutation` wrappers in `api-client.ts`, not directly.
10
10
  *
11
- * NOTE: By default, the mutation updates cache for PUT/PATCH and invalidates matching
12
- * GET queries for the same path.
11
+ * @template TResponse The response data type
12
+ * @template TPathParams The path parameters type (concrete, required values)
13
+ * @template TRequest The request body type (`never` if no body)
14
+ * @template TQueryParams The query parameters type
13
15
  *
14
- * @template Ops - The operations type from your OpenAPI specification
15
- * @template Op - The operation key from your operations type
16
- * @param operationId - The OpenAPI operation ID to mutate
17
- * @param h - OpenAPI helpers (internal), provided by useOpenApi
18
- * @param pathParams - Path parameters (can be reactive). Omit for operations without path params.
19
- * @param options - Mutation options (dontInvalidate, refetchEndpoints, etc.)
20
- * - `dontUpdateCache`: Skip cache update for PUT/PATCH responses
21
- * - `dontInvalidate`: Skip invalidating matching queries
22
- * - `invalidateOperations`: Additional operation IDs to invalidate (array or map of params)
23
- * - `refetchEndpoints`: Additional query results to refetch
24
- * - `queryParams`: Query string parameters (operation-specific)
25
- * - `axiosOptions`: Custom axios request options (headers, params, etc.)
26
- * - Plus all {@link UseMutationOptions} from @tanstack/vue-query
27
- * @throws Error if the operation is not a mutation operation
28
- * @returns Mutation object with
29
- * - `mutate(vars)` / `mutateAsync(vars)` to trigger the mutation
30
- * - `data`: ComputedRef of Axios response data
31
- * - `isEnabled`: ComputedRef indicating if mutation can execute (path resolved)
32
- * - `extraPathParams`: Ref to set additional path params at call time
33
- * - `pathParams`: Resolved path params as a computed ref
16
+ * @param config Endpoint config: axios instance, queryClient, path, method, listPath, operationsRegistry
17
+ * @param pathParams Path parameters (reactive). Pass `undefined` for operations without path params.
18
+ * @param options Mutation options (dontInvalidate, refetchEndpoints, etc.)
34
19
  */
35
- export function useEndpointMutation(operationId, h, // helpers
36
- pathParams, options) {
37
- // Runtime check to ensure this is actually a mutation operation
38
- if (!h.isMutationOperation(operationId)) {
39
- const { method } = h.getOperationInfo(operationId);
40
- throw new Error(`Operation '${String(operationId)}' uses method ${method} and cannot be used with useMutation(). ` +
20
+ export function useEndpointMutation(config, pathParams, options) {
21
+ if (!isMutationMethod(config.method)) {
22
+ throw new Error(`Operation at '${config.path}' uses method ${config.method} and cannot be used with useMutation(). ` +
41
23
  `Use useQuery() for GET/HEAD/OPTIONS operations.`);
42
24
  }
43
- const { path, method } = h.getOperationInfo(operationId);
44
25
  const { pathParams: resolvedPathParamsInput, options: resolvedOptions } = normalizeParamsOptions(pathParams, options);
45
26
  const { axiosOptions, dontInvalidate, dontUpdateCache, invalidateOperations, refetchEndpoints, queryParams, ...useMutationOptions } = resolvedOptions;
46
27
  const extraPathParams = ref({});
47
- // Use the consolidated operation resolver with extraPathParams support
48
- const { resolvedPath, queryKey, queryParams: resolvedQueryParams, pathParams: allPathParams, } = useResolvedOperation(path, resolvedPathParamsInput, queryParams, extraPathParams);
28
+ const { resolvedPath, queryKey, queryParams: resolvedQueryParams, pathParams: allPathParams, } = useResolvedOperation(config.path, resolvedPathParamsInput, queryParams, extraPathParams);
49
29
  const mutation = useMutation({
50
30
  mutationFn: async (vars) => {
51
- const { data, pathParams: pathParamsFromMutate, axiosOptions: axiosOptionsFromMutate, queryParams: queryParamsFromMutate, } = vars;
52
- extraPathParams.value = pathParamsFromMutate || {};
53
- // TODO: use typing to ensure all required path params are provided
31
+ const { pathParams: pathParamsFromMutate, axiosOptions: axiosOptionsFromMutate, queryParams: queryParamsFromMutate, } = (vars || {});
32
+ const data = vars?.data;
33
+ extraPathParams.value = (pathParamsFromMutate || {});
54
34
  if (!isPathResolved(resolvedPath.value)) {
55
- return Promise.reject(new Error(`Cannot execute mutation '${String(operationId)}': path parameters not resolved. ` +
35
+ return Promise.reject(new Error(`Cannot execute mutation at '${config.path}': path parameters not resolved. ` +
56
36
  `Path: '${resolvedPath.value}', provided params: ${JSON.stringify(allPathParams.value)}`));
57
37
  }
58
- // Cancel any ongoing queries for this path (prevent race conditions with refresh)
59
- await h.queryClient.cancelQueries({ queryKey: queryKey.value, exact: false });
60
- return h.axios({
61
- method: method.toLowerCase(),
38
+ await config.queryClient.cancelQueries({ queryKey: queryKey.value, exact: false });
39
+ return config.axios({
40
+ method: config.method.toLowerCase(),
62
41
  url: resolvedPath.value,
63
42
  ...(data !== undefined && { data }),
64
43
  ...axiosOptions,
@@ -70,43 +49,34 @@ pathParams, options) {
70
49
  },
71
50
  });
72
51
  },
73
- onSuccess: async (response, vars, _context) => {
52
+ onSuccess: async (response, vars) => {
74
53
  const data = response.data;
75
- const { dontInvalidate: dontInvalidateMutate, dontUpdateCache: dontUpdateCacheMutate, invalidateOperations: invalidateOperationsMutate, refetchEndpoints: refetchEndpointsMutate, } = vars || {};
76
- // update cache with returned data for PUT/PATCH requests
77
- if (
78
- // dontUpdateCacheMutate supersedes dontUpdateCache from options
79
- (dontUpdateCacheMutate !== undefined ? !dontUpdateCacheMutate : !dontUpdateCache) &&
54
+ const { dontInvalidate: dontInvalidateMutate, dontUpdateCache: dontUpdateCacheMutate, invalidateOperations: invalidateOperationsMutate, refetchEndpoints: refetchEndpointsMutate, } = (vars || {});
55
+ // Update cache for PUT/PATCH
56
+ if ((dontUpdateCacheMutate !== undefined ? !dontUpdateCacheMutate : !dontUpdateCache) &&
80
57
  data &&
81
- [HttpMethod.PUT, HttpMethod.PATCH].includes(method)) {
82
- await h.queryClient.setQueryData(queryKey.value, data);
58
+ [HttpMethod.PUT, HttpMethod.PATCH].includes(config.method)) {
59
+ await config.queryClient.setQueryData(queryKey.value, data);
83
60
  }
84
- // Invalidate queries for this path, and any additional specified operations
61
+ // Invalidate queries for this path
85
62
  if (dontInvalidateMutate !== undefined ? !dontInvalidateMutate : !dontInvalidate) {
86
- // Invalidate all queries for this path (exact for POST, prefix for others):
87
- await h.queryClient.invalidateQueries({ queryKey: queryKey.value, exact: method !== HttpMethod.POST });
88
- const listPath = h.getListOperationPath(operationId);
89
- if (listPath) {
90
- const listResolvedPath = resolvePath(listPath, resolvedPathParamsInput);
63
+ await config.queryClient.invalidateQueries({
64
+ queryKey: queryKey.value,
65
+ exact: config.method !== HttpMethod.POST,
66
+ });
67
+ // Invalidate associated list path
68
+ if (config.listPath) {
69
+ const listResolvedPath = resolvePath(config.listPath, resolvedPathParamsInput);
91
70
  if (isPathResolved(listResolvedPath)) {
92
71
  const listQueryKey = generateQueryKey(listResolvedPath);
93
- // Invalidate list queries by comparing normalized query keys.
94
- // For queries with query parameters (objects), strip the last element before comparing.
95
- // This matches:
96
- // - List queries without params: ["api", "user"]
97
- // - List queries with params: ["api", "user", {filter}] → normalized to ["api", "user"]
98
- // But NOT single-item queries where last element is a primitive:
99
- // - Single-item: ["api", "user", "uuid"] → kept as ["api", "user", "uuid"]
100
- await h.queryClient.invalidateQueries({
72
+ await config.queryClient.invalidateQueries({
101
73
  predicate: (query) => {
102
74
  const qKey = query.queryKey;
103
75
  if (!qKey || qKey.length === 0)
104
76
  return false;
105
- // Normalize query key: strip last element if it's an object (query params)
106
77
  const normalizedKey = typeof qKey[qKey.length - 1] === 'object' && qKey[qKey.length - 1] !== null
107
78
  ? qKey.slice(0, -1)
108
79
  : qKey;
109
- // Compare with listQueryKey
110
80
  if (normalizedKey.length !== listQueryKey.length)
111
81
  return false;
112
82
  for (let i = 0; i < listQueryKey.length; i++) {
@@ -119,47 +89,61 @@ pathParams, options) {
119
89
  }
120
90
  }
121
91
  }
92
+ // Resolve invalidateOperations entries using the registry
93
+ const registry = config.operationsRegistry || {};
94
+ const allInvalidateOps = [invalidateOperations, invalidateOperationsMutate].filter(Boolean);
122
95
  const operationsWithPathParams = [];
123
- Array.from([invalidateOperations, invalidateOperationsMutate]).forEach((ops) => {
124
- operationsWithPathParams.push(...(typeof ops === 'object' && !Array.isArray(ops)
125
- ? Object.entries(ops)
126
- : ops?.map((opId) => [opId, {}]) || []));
127
- });
96
+ for (const ops of allInvalidateOps) {
97
+ if (!ops)
98
+ continue;
99
+ if (Array.isArray(ops)) {
100
+ operationsWithPathParams.push(...ops.map((id) => [id, {}]));
101
+ }
102
+ else {
103
+ operationsWithPathParams.push(...Object.entries(ops).map(([id, params]) => [id, params]));
104
+ }
105
+ }
128
106
  if (operationsWithPathParams.length > 0) {
129
107
  const promises = operationsWithPathParams.map(([opId, opParams]) => {
130
- const opInfo = h.getOperationInfo(opId);
108
+ const opInfo = registry[opId];
109
+ if (!opInfo) {
110
+ console.warn(`Cannot invalidate operation '${opId}': not found in operations registry`);
111
+ return Promise.resolve();
112
+ }
131
113
  const opPath = resolvePath(opInfo.path, {
132
114
  ...allPathParams.value,
133
115
  ...opParams,
134
116
  });
135
117
  if (isPathResolved(opPath)) {
136
118
  const opQueryKey = generateQueryKey(opPath);
137
- return h.queryClient.invalidateQueries({ queryKey: opQueryKey, exact: true });
119
+ return config.queryClient.invalidateQueries({ queryKey: opQueryKey, exact: true });
138
120
  }
139
121
  else {
140
- console.warn(`Cannot invalidate operation '${String(opId)}', path not resolved: ${opPath} (params: ${JSON.stringify({ ...allPathParams.value, ...opParams })})`);
141
- return Promise.reject();
122
+ console.warn(`Cannot invalidate operation '${opId}', path not resolved: ${opPath}`);
123
+ return Promise.resolve();
142
124
  }
143
125
  });
144
126
  await Promise.all(promises);
145
127
  }
146
- if (refetchEndpoints && refetchEndpoints.length > 0) {
147
- await Promise.all(refetchEndpoints.map((endpoint) => endpoint.refetch()));
148
- }
149
- if (refetchEndpointsMutate && refetchEndpointsMutate.length > 0) {
150
- await Promise.all(refetchEndpointsMutate.map((endpoint) => endpoint.refetch()));
128
+ const allRefetch = [
129
+ ...(refetchEndpoints || []),
130
+ ...(vars?.refetchEndpoints || []),
131
+ ...(refetchEndpointsMutate || []),
132
+ ];
133
+ if (allRefetch.length > 0) {
134
+ await Promise.all(allRefetch.map((ep) => ep.refetch()));
151
135
  }
152
136
  },
153
137
  onSettled: () => {
154
138
  extraPathParams.value = {};
155
139
  },
156
140
  ...useMutationOptions,
157
- }, h.queryClient);
141
+ }, config.queryClient);
158
142
  return {
159
143
  ...mutation,
160
144
  data: mutation.data,
161
145
  isEnabled: computed(() => isPathResolved(resolvedPath.value)),
162
- extraPathParams,
146
+ extraPathParams: extraPathParams,
163
147
  pathParams: allPathParams,
164
148
  };
165
149
  }
@@ -1,40 +1,22 @@
1
- import { type ComputedRef, type MaybeRefOrGetter, type Ref } from 'vue';
2
- import { Operations, type ApiPathParams, type ApiPathParamsInput, type ApiResponse, type QQueryOptions } from './types';
3
- import { type OpenApiHelpers } from './openapi-helpers';
1
+ import { type ComputedRef, type Ref } from 'vue';
2
+ import type { MaybeRefOrGetter } from '@vue/reactivity';
3
+ import { type EndpointConfig, type QueryOptions } from './types';
4
4
  /**
5
- * Return type of `useQuery` (created via `useOpenApi`).
5
+ * Return type of `useEndpointQuery` (the `useQuery` composable on a generated namespace).
6
6
  *
7
- * Reactive query result with automatic caching, error handling, and helpers.
8
- *
9
- * All properties are reactive (ComputedRef) and auto-unwrap in Vue templates.
10
- *
11
- * @template Ops - The operations type from your OpenAPI specification
12
- * @template Op - The operation key from your operations type
13
- *
14
- * @example
15
- * ```typescript
16
- * const query = api.useQuery('listPets', { queryParams: { limit: 10 } })
17
- *
18
- * // Reactive properties
19
- * if (query.isPending.value) console.log('Loading...')
20
- * if (query.isError.value) console.log('Error:', query.error.value)
21
- * if (query.isSuccess.value) console.log('Data:', query.data.value)
22
- *
23
- * // Helpers
24
- * query.refetch() // Manual refetch
25
- * query.onLoad((data) => console.log('First load:', data))
26
- * ```
7
+ * @template TResponse Response data type
8
+ * @template TPathParams Path parameters type (concrete, no undefined)
27
9
  *
28
10
  * @group Types
29
11
  */
30
- export interface EndpointQueryReturn<Ops extends Operations<Ops>, Op extends keyof Ops> {
12
+ export interface QueryReturn<TResponse, TPathParams extends Record<string, unknown> = Record<string, never>> {
31
13
  /** The response data (undefined until loaded). */
32
- data: ComputedRef<ApiResponse<Ops, Op> | undefined>;
14
+ data: ComputedRef<TResponse | undefined>;
33
15
  /** The error if the query failed. */
34
16
  error: Ref<Error | null>;
35
17
  /** True while the query is loading. */
36
18
  isPending: Ref<boolean>;
37
- /** True while loading (same as isPending, for compatibility). */
19
+ /** True while loading (alias for isPending). */
38
20
  isLoading: Ref<boolean>;
39
21
  /** True when the query succeeded. */
40
22
  isSuccess: Ref<boolean>;
@@ -44,36 +26,26 @@ export interface EndpointQueryReturn<Ops extends Operations<Ops>, Op extends key
44
26
  refetch: () => Promise<void>;
45
27
  /** Whether the query is currently enabled. */
46
28
  isEnabled: ComputedRef<boolean>;
47
- /** The resolved query key for manual cache access. */
29
+ /** The resolved query key. */
48
30
  queryKey: ComputedRef<string[] | (string | unknown)[]>;
49
31
  /** The resolved path parameters. */
50
- pathParams: ComputedRef<ApiPathParams<Ops, Op>>;
32
+ pathParams: ComputedRef<TPathParams>;
51
33
  /** Register a callback for when data loads successfully for the first time. */
52
- onLoad: (callback: (data: ApiResponse<Ops, Op>) => void) => void;
34
+ onLoad: (callback: (data: TResponse) => void) => void;
53
35
  }
54
36
  /**
55
37
  * Execute a type-safe query (GET/HEAD/OPTIONS) with automatic caching.
56
38
  *
57
- * Ensures the operation is a query at runtime and returns a reactive query object,
58
- * including helpers for query key, enabled state, and an `onLoad` callback.
39
+ * This is a low-level primitive in normal usage it is called by the generated
40
+ * per-operation `useQuery` wrappers in `api-client.ts`, not directly.
41
+ *
42
+ * @template TResponse The response data type
43
+ * @template TPathParams The path parameters type (concrete, required values)
44
+ * @template TQueryParams The query parameters type
59
45
  *
60
- * @template Ops - The operations type from your OpenAPI specification
61
- * @template Op - The operation key from your operations type
62
- * @param operationId - The OpenAPI operation ID to query
63
- * @param h - OpenAPI helpers (internal), provided by useOpenApi
64
- * @param pathParams - Path parameters (can be reactive). Omit for operations without path params.
65
- * @param options - Query options (enabled, staleTime, queryParams, etc.)
66
- * - `enabled`: Whether the query should auto-run (boolean or reactive)
67
- * - `queryParams`: Query string parameters (operation-specific)
68
- * - `onLoad`: Callback invoked once when data is loaded
69
- * - `axiosOptions`: Custom axios request options (headers, params, etc.)
70
- * - Plus all {@link UseQueryOptions} from @tanstack/vue-query
71
- * @throws Error if the operation is not a query operation
72
- * @returns Query object with strict typing and helpers:
73
- * - `data`: ComputedRef of response data
74
- * - `isEnabled`: ComputedRef indicating if query is enabled
75
- * - `queryKey`: ComputedRef of the query key
76
- * - `onLoad(callback)`: Register a callback for when data is loaded
46
+ * @param config Endpoint config: axios instance, queryClient, path, method, listPath
47
+ * @param pathParams Path parameters (reactive). Pass `undefined` for operations without path params.
48
+ * @param options Query options (enabled, staleTime, queryParams, onLoad, etc.)
77
49
  */
78
- export declare function useEndpointQuery<Ops extends Operations<Ops>, Op extends keyof Ops>(operationId: Op, h: OpenApiHelpers<Ops, Op>, pathParams?: MaybeRefOrGetter<ApiPathParamsInput<Ops, Op> | null | undefined>, options?: QQueryOptions<Ops, Op>): EndpointQueryReturn<Ops, Op>;
50
+ export declare function useEndpointQuery<TResponse, TPathParams extends Record<string, unknown> = Record<string, never>, TQueryParams extends Record<string, unknown> = Record<string, never>>(config: EndpointConfig, pathParams?: MaybeRefOrGetter<Record<string, string | number | undefined> | null | undefined>, options?: QueryOptions<TResponse, TQueryParams>): QueryReturn<TResponse, TPathParams>;
79
51
  //# sourceMappingURL=openapi-query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openapi-query.d.ts","sourceRoot":"","sources":["../src/openapi-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AAEjG,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAGvH,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,mBAAmB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG;IACpF,kDAAkD;IAClD,IAAI,EAAE,WAAW,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC,CAAA;IAEnD,qCAAqC;IACrC,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IAExB,uCAAuC;IACvC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAEvB,iEAAiE;IACjE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAEvB,qCAAqC;IACrC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAEvB,kCAAkC;IAClC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAErB,kCAAkC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAE5B,8CAA8C;IAC9C,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IAE/B,sDAAsD;IACtD,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;IAEtD,oCAAoC;IACpC,UAAU,EAAE,WAAW,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;IAE/C,+EAA+E;IAC/E,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,CAAA;CACjE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,EAChF,WAAW,EAAE,EAAE,EACf,CAAC,EAAE,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC,EAC1B,UAAU,CAAC,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAC7E,OAAO,CAAC,EAAE,aAAa,CAAC,GAAG,EAAE,EAAE,CAAC,GA+HhB,mBAAmB,CAAC,GAAG,EAAE,EAAE,CAAC,CAC7C"}
1
+ {"version":3,"file":"openapi-query.d.ts","sourceRoot":"","sources":["../src/openapi-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,WAAW,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAIvD,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAiB,MAAM,SAAS,CAAA;AAG/E;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW,CAAC,SAAS,EAAE,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IACzG,kDAAkD;IAClD,IAAI,EAAE,WAAW,CAAC,SAAS,GAAG,SAAS,CAAC,CAAA;IACxC,qCAAqC;IACrC,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IACxB,uCAAuC;IACvC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,gDAAgD;IAChD,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,qCAAqC;IACrC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,kCAAkC;IAClC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACrB,kCAAkC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,8CAA8C;IAC9C,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IAC/B,8BAA8B;IAC9B,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;IACtD,oCAAoC;IACpC,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;IACpC,+EAA+E;IAC/E,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,KAAK,IAAI,CAAA;CACtD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EACT,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACnE,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAEpE,MAAM,EAAE,cAAc,EACtB,UAAU,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAC7F,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,GAC9C,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAuGrC"}
@@ -1,44 +1,30 @@
1
1
  import { computed, watch, toValue } from 'vue';
2
2
  import { useQuery } from '@tanstack/vue-query';
3
- import { normalizeParamsOptions, useResolvedOperation } from './openapi-utils.js';
4
3
  import { isAxiosError } from 'axios';
4
+ import { isQueryMethod } from './types.js';
5
+ import { normalizeParamsOptions, useResolvedOperation } from './openapi-utils.js';
5
6
  /**
6
7
  * Execute a type-safe query (GET/HEAD/OPTIONS) with automatic caching.
7
8
  *
8
- * Ensures the operation is a query at runtime and returns a reactive query object,
9
- * including helpers for query key, enabled state, and an `onLoad` callback.
9
+ * This is a low-level primitive in normal usage it is called by the generated
10
+ * per-operation `useQuery` wrappers in `api-client.ts`, not directly.
11
+ *
12
+ * @template TResponse The response data type
13
+ * @template TPathParams The path parameters type (concrete, required values)
14
+ * @template TQueryParams The query parameters type
10
15
  *
11
- * @template Ops - The operations type from your OpenAPI specification
12
- * @template Op - The operation key from your operations type
13
- * @param operationId - The OpenAPI operation ID to query
14
- * @param h - OpenAPI helpers (internal), provided by useOpenApi
15
- * @param pathParams - Path parameters (can be reactive). Omit for operations without path params.
16
- * @param options - Query options (enabled, staleTime, queryParams, etc.)
17
- * - `enabled`: Whether the query should auto-run (boolean or reactive)
18
- * - `queryParams`: Query string parameters (operation-specific)
19
- * - `onLoad`: Callback invoked once when data is loaded
20
- * - `axiosOptions`: Custom axios request options (headers, params, etc.)
21
- * - Plus all {@link UseQueryOptions} from @tanstack/vue-query
22
- * @throws Error if the operation is not a query operation
23
- * @returns Query object with strict typing and helpers:
24
- * - `data`: ComputedRef of response data
25
- * - `isEnabled`: ComputedRef indicating if query is enabled
26
- * - `queryKey`: ComputedRef of the query key
27
- * - `onLoad(callback)`: Register a callback for when data is loaded
16
+ * @param config Endpoint config: axios instance, queryClient, path, method, listPath
17
+ * @param pathParams Path parameters (reactive). Pass `undefined` for operations without path params.
18
+ * @param options Query options (enabled, staleTime, queryParams, onLoad, etc.)
28
19
  */
29
- export function useEndpointQuery(operationId, h, pathParams, options) {
30
- // Runtime check to ensure this is actually a query operation
31
- if (!h.isQueryOperation(operationId)) {
32
- const { method } = h.getOperationInfo(operationId);
33
- throw new Error(`Operation '${String(operationId)}' uses method ${method} and cannot be used with useQuery(). ` +
20
+ export function useEndpointQuery(config, pathParams, options) {
21
+ if (!isQueryMethod(config.method)) {
22
+ throw new Error(`Operation at '${config.path}' uses method ${config.method} and cannot be used with useQuery(). ` +
34
23
  `Use useMutation() for POST/PUT/PATCH/DELETE operations.`);
35
24
  }
36
- const { path, method } = h.getOperationInfo(operationId);
37
25
  const { pathParams: resolvedPathParamsInput, options: resolvedOptions } = normalizeParamsOptions(pathParams, options);
38
26
  const { enabled: enabledInit, onLoad: onLoadInit, axiosOptions, errorHandler, queryParams, ...useQueryOptions } = resolvedOptions;
39
- // Use the consolidated operation resolver
40
- const { resolvedPath, queryKey, isResolved, queryParams: resolvedQueryParams, pathParams: resolvedPathParams, } = useResolvedOperation(path, resolvedPathParamsInput, queryParams);
41
- // Check if path is fully resolved for enabling the query
27
+ const { resolvedPath, queryKey, isResolved, queryParams: resolvedQueryParams, pathParams: resolvedPathParams, } = useResolvedOperation(config.path, resolvedPathParamsInput, queryParams);
42
28
  const isEnabled = computed(() => {
43
29
  const baseEnabled = enabledInit !== undefined ? toValue(enabledInit) : true;
44
30
  return baseEnabled && isResolved.value;
@@ -47,8 +33,8 @@ export function useEndpointQuery(operationId, h, pathParams, options) {
47
33
  queryKey: queryKey,
48
34
  queryFn: async () => {
49
35
  try {
50
- const response = await h.axios({
51
- method: method.toLowerCase(),
36
+ const response = await config.axios({
37
+ method: config.method.toLowerCase(),
52
38
  url: resolvedPath.value,
53
39
  ...axiosOptions,
54
40
  params: {
@@ -61,67 +47,48 @@ export function useEndpointQuery(operationId, h, pathParams, options) {
61
47
  catch (error) {
62
48
  if (errorHandler && isAxiosError(error)) {
63
49
  const result = await errorHandler(error);
64
- if (result !== undefined) {
50
+ if (result !== undefined)
65
51
  return result;
66
- }
67
- // If errorHandler returns undefined and doesn't throw,
68
- // we consider this a "recovered" state and return undefined
69
- // TanStack Query will handle this as a successful query with no data
70
52
  return undefined;
71
53
  }
72
- else {
73
- throw error;
74
- }
54
+ throw error;
75
55
  }
76
56
  },
77
57
  enabled: isEnabled,
78
58
  staleTime: 1000 * 60,
79
59
  retry: (_failureCount, error) => {
80
- // Don't retry 4xx errors if error is AxiosError
81
60
  if (isAxiosError(error) && error.response && error.response.status >= 400 && error.response.status < 500) {
82
61
  return false;
83
62
  }
84
- // Retry up to 3 times for other errors
85
63
  return _failureCount < 3;
86
64
  },
87
65
  ...useQueryOptions,
88
66
  };
89
- const query = useQuery(queryOptions, h.queryClient);
90
- // onLoad callback management using a Set for efficient tracking
67
+ const query = useQuery(queryOptions, config.queryClient);
91
68
  const onLoadCallbacks = new Set();
92
- // Add initial callback from options if provided
93
- if (onLoadInit) {
69
+ if (onLoadInit)
94
70
  onLoadCallbacks.add(onLoadInit);
95
- }
96
- // Single watch instance to handle all callbacks - stop after first successful data
97
71
  if (query.data.value !== undefined) {
98
- // Data already available - call all callbacks immediately
99
72
  onLoadCallbacks.forEach((cb) => cb(query.data.value));
100
73
  onLoadCallbacks.clear();
101
74
  }
102
75
  else {
103
- // Watch for data to become available - stop after first successful load
104
76
  const stopWatch = watch(query.data, (newData) => {
105
77
  if (newData !== undefined && onLoadCallbacks.size > 0) {
106
- // Call all pending callbacks
107
78
  onLoadCallbacks.forEach((cb) => cb(newData));
108
79
  onLoadCallbacks.clear();
109
- stopWatch(); // Stop watching after first successful load
80
+ stopWatch();
110
81
  }
111
82
  });
112
83
  }
113
- // Public onLoad method to register additional callbacks
114
84
  const onLoad = (callback) => {
115
85
  if (query.data.value !== undefined) {
116
- // Data already available - call immediately
117
86
  callback(query.data.value);
118
87
  }
119
88
  else {
120
- // Add to pending callbacks
121
89
  onLoadCallbacks.add(callback);
122
90
  }
123
91
  };
124
- // Return object spread with data wrapped as ComputedRef for Vue template unwrapping
125
92
  return {
126
93
  ...query,
127
94
  data: computed(() => query.data.value),
@@ -1,4 +1,5 @@
1
- import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
1
+ import { type ComputedRef } from 'vue';
2
+ import type { MaybeRefOrGetter } from '@vue/reactivity';
2
3
  export declare function resolvePath(path: string, pathParams?: MaybeRefOrGetter<Record<string, string | number | undefined> | null | undefined>): string;
3
4
  export declare function isPathResolved(path: string): boolean;
4
5
  export declare function generateQueryKey(resolvedPath: string): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"openapi-utils.d.ts","sourceRoot":"","sources":["../src/openapi-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAGhF,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAC5F,MAAM,CAaR;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAGD,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAE/D;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,UAAU,EAAE,WAAW;IACxD,+DAA+D;IAC/D,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,CAAA;IACnC,yDAAyD;IACzD,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,gEAAgE;IAChE,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;IACrC,4CAA4C;IAC5C,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;IAC1D,+DAA+D;IAC/D,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACtF,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAEnE,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,gBAAgB,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,EAC3D,WAAW,CAAC,EAAE,gBAAgB,CAAC,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,EAC9D,eAAe,CAAC,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,GAC/C,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,CAuC5C;AAED,wBAAgB,sBAAsB,CAAC,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EACxF,UAAU,CAAC,EAAE,gBAAgB,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,EAC5D,OAAO,CAAC,EAAE,OAAO,GAChB;IACD,UAAU,EAAE,gBAAgB,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;IAC3D,OAAO,EAAE,OAAO,CAAA;CACjB,CAKA"}
1
+ {"version":3,"file":"openapi-utils.d.ts","sourceRoot":"","sources":["../src/openapi-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAGvD,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAC5F,MAAM,CAaR;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAGD,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAE/D;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,UAAU,EAAE,WAAW;IACxD,+DAA+D;IAC/D,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,CAAA;IACnC,yDAAyD;IACzD,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,gEAAgE;IAChE,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;IACrC,4CAA4C;IAC5C,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;IAC1D,+DAA+D;IAC/D,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACtF,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAEnE,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,gBAAgB,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,EAC3D,WAAW,CAAC,EAAE,gBAAgB,CAAC,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,EAC9D,eAAe,CAAC,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,GAC/C,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,CAuC5C;AAED,wBAAgB,sBAAsB,CAAC,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EACxF,UAAU,CAAC,EAAE,gBAAgB,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,EAC5D,OAAO,CAAC,EAAE,OAAO,GAChB;IACD,UAAU,EAAE,gBAAgB,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;IAC3D,OAAO,EAAE,OAAO,CAAA;CACjB,CAKA"}