@qualisero/openapi-endpoint 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/bin/openapi-codegen.js +25 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +176 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/openapi-endpoint.d.ts +18 -0
- package/dist/openapi-endpoint.d.ts.map +1 -0
- package/dist/openapi-endpoint.js +39 -0
- package/dist/openapi-helpers.d.ts +10 -0
- package/dist/openapi-helpers.d.ts.map +1 -0
- package/dist/openapi-helpers.js +89 -0
- package/dist/openapi-mutation.d.ts +112 -0
- package/dist/openapi-mutation.d.ts.map +1 -0
- package/dist/openapi-mutation.js +135 -0
- package/dist/openapi-query.d.ts +209 -0
- package/dist/openapi-query.d.ts.map +1 -0
- package/dist/openapi-query.js +97 -0
- package/dist/openapi-utils.d.ts +10 -0
- package/dist/openapi-utils.d.ts.map +1 -0
- package/dist/openapi-utils.js +43 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/package.json +83 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { type ComputedRef, type Ref, type MaybeRefOrGetter } from 'vue';
|
|
2
|
+
import { type GetPathParameters, type MutationVars, type GetResponseData, type MutationOptions, Operations } from './types';
|
|
3
|
+
import { getHelpers } from './openapi-helpers';
|
|
4
|
+
export type EndpointMutationReturn<Ops extends Operations<Ops>, Op extends keyof Ops> = ReturnType<typeof useEndpointMutation<Ops, Op>>;
|
|
5
|
+
/**
|
|
6
|
+
* Composable for performing a strictly typed OpenAPI mutation operation using Vue Query.
|
|
7
|
+
* Ensures the operation is a mutation (POST/PUT/PATCH/DELETE) at runtime.
|
|
8
|
+
* Returns a reactive mutation object, including helpers for query key and enabled state.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: By default, the mutation will automatically update cache with returned data and reload
|
|
11
|
+
* any matching GET queries for the same path.
|
|
12
|
+
*
|
|
13
|
+
* @template T OperationId type representing the OpenAPI operation.
|
|
14
|
+
* @param operationId The OpenAPI operation ID to mutate.
|
|
15
|
+
* @param pathParams Optional path parameters for the endpoint, can be reactive.
|
|
16
|
+
* @param options Optional mutation options, including Vue Query options and custom axios options:
|
|
17
|
+
* - 'dontUpdateCache': If true, will not update cache with returned data (default: false)
|
|
18
|
+
* - 'dontInvalidate': If true, will not invalidate matching GET queries (default: false)
|
|
19
|
+
* - 'invalidateOperations': List of additional OperationIds to invalidate after mutation (can also be a map of OperationId to path parameters)
|
|
20
|
+
* - 'refetchEndpoints': List of additional EndpointQueryReturn objects to refetch after mutation
|
|
21
|
+
* - `axiosOptions`: Custom axios request options (e.g., headers, params)
|
|
22
|
+
* - All properties from {@link UseMutationOptions} (from @tanstack/vue-query)
|
|
23
|
+
* @throws Error if the operation is not a mutation operation.
|
|
24
|
+
* @returns Mutation object with
|
|
25
|
+
* - `data`: ComputedRef of response data.
|
|
26
|
+
* - `isEnabled`: ComputedRef indicating if mutation can be executed (path resolved).
|
|
27
|
+
* - `extraPathParams`: Ref to set of additional path parameters when calling mutate.
|
|
28
|
+
* - `mutate` and `mutateAsync`: Functions to trigger the mutation, taking an object with:
|
|
29
|
+
* - `data`: The request body data for the mutation.
|
|
30
|
+
* - `pathParams`: Optional additional path parameters for the mutation.
|
|
31
|
+
* - `dontUpdateCache`, `dontInvalidate`, `invalidateOperations`, `refetchEndpoints`: Same as options, but can be set per-mutation.
|
|
32
|
+
* - All other properties and methods from the underlying Vue Query mutation object.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useEndpointMutation<Ops extends Operations<Ops>, Op extends keyof Ops>(operationId: Op, h: ReturnType<typeof getHelpers<Ops, Op>>, // helpers
|
|
35
|
+
pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | MutationOptions<Ops, Op>, optionsOrNull?: MutationOptions<Ops, Op>): {
|
|
36
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
37
|
+
isEnabled: ComputedRef<boolean>;
|
|
38
|
+
extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
|
|
39
|
+
error: Ref<null, null>;
|
|
40
|
+
isError: Ref<false, false>;
|
|
41
|
+
isPending: Ref<false, false>;
|
|
42
|
+
isSuccess: Ref<false, false>;
|
|
43
|
+
status: Ref<"idle", "idle">;
|
|
44
|
+
failureCount: Ref<number, number>;
|
|
45
|
+
failureReason: Ref<Error | null, Error | null>;
|
|
46
|
+
isPaused: Ref<boolean, boolean>;
|
|
47
|
+
variables: Ref<undefined, undefined>;
|
|
48
|
+
isIdle: Ref<true, true>;
|
|
49
|
+
context: Ref<unknown, unknown>;
|
|
50
|
+
submittedAt: Ref<number, number>;
|
|
51
|
+
mutate: (variables: MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown> | undefined) => void;
|
|
52
|
+
mutateAsync: import("@tanstack/query-core").MutateFunction<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown>;
|
|
53
|
+
reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
|
|
54
|
+
} | {
|
|
55
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
56
|
+
isEnabled: ComputedRef<boolean>;
|
|
57
|
+
extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
|
|
58
|
+
error: Ref<null, null>;
|
|
59
|
+
isError: Ref<false, false>;
|
|
60
|
+
isPending: Ref<true, true>;
|
|
61
|
+
isSuccess: Ref<false, false>;
|
|
62
|
+
status: Ref<"pending", "pending">;
|
|
63
|
+
failureCount: Ref<number, number>;
|
|
64
|
+
failureReason: Ref<Error | null, Error | null>;
|
|
65
|
+
isPaused: Ref<boolean, boolean>;
|
|
66
|
+
variables: Ref<MutationVars<Ops, Op>, MutationVars<Ops, Op>>;
|
|
67
|
+
isIdle: Ref<false, false>;
|
|
68
|
+
context: Ref<unknown, unknown>;
|
|
69
|
+
submittedAt: Ref<number, number>;
|
|
70
|
+
mutate: (variables: MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown> | undefined) => void;
|
|
71
|
+
mutateAsync: import("@tanstack/query-core").MutateFunction<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown>;
|
|
72
|
+
reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
|
|
73
|
+
} | {
|
|
74
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
75
|
+
isEnabled: ComputedRef<boolean>;
|
|
76
|
+
extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
|
|
77
|
+
error: Ref<Error, Error>;
|
|
78
|
+
isError: Ref<true, true>;
|
|
79
|
+
isPending: Ref<false, false>;
|
|
80
|
+
isSuccess: Ref<false, false>;
|
|
81
|
+
status: Ref<"error", "error">;
|
|
82
|
+
failureCount: Ref<number, number>;
|
|
83
|
+
failureReason: Ref<Error | null, Error | null>;
|
|
84
|
+
isPaused: Ref<boolean, boolean>;
|
|
85
|
+
variables: Ref<MutationVars<Ops, Op>, MutationVars<Ops, Op>>;
|
|
86
|
+
isIdle: Ref<false, false>;
|
|
87
|
+
context: Ref<unknown, unknown>;
|
|
88
|
+
submittedAt: Ref<number, number>;
|
|
89
|
+
mutate: (variables: MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown> | undefined) => void;
|
|
90
|
+
mutateAsync: import("@tanstack/query-core").MutateFunction<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown>;
|
|
91
|
+
reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
|
|
92
|
+
} | {
|
|
93
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
94
|
+
isEnabled: ComputedRef<boolean>;
|
|
95
|
+
extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
|
|
96
|
+
error: Ref<null, null>;
|
|
97
|
+
isError: Ref<false, false>;
|
|
98
|
+
isPending: Ref<false, false>;
|
|
99
|
+
isSuccess: Ref<true, true>;
|
|
100
|
+
status: Ref<"success", "success">;
|
|
101
|
+
failureCount: Ref<number, number>;
|
|
102
|
+
failureReason: Ref<Error | null, Error | null>;
|
|
103
|
+
isPaused: Ref<boolean, boolean>;
|
|
104
|
+
variables: Ref<MutationVars<Ops, Op>, MutationVars<Ops, Op>>;
|
|
105
|
+
isIdle: Ref<false, false>;
|
|
106
|
+
context: Ref<unknown, unknown>;
|
|
107
|
+
submittedAt: Ref<number, number>;
|
|
108
|
+
mutate: (variables: MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown> | undefined) => void;
|
|
109
|
+
mutateAsync: import("@tanstack/query-core").MutateFunction<GetResponseData<Ops, Op>, Error, MutationVars<Ops, Op>, unknown>;
|
|
110
|
+
reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=openapi-mutation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-mutation.d.ts","sourceRoot":"","sources":["../src/openapi-mutation.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,WAAW,EAAE,KAAK,GAAG,EAAE,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAG/F,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,eAAe,EAEpB,UAAU,EACX,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,MAAM,sBAAsB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,IAAI,UAAU,CAChG,OAAO,mBAAmB,CAAC,GAAG,EAAE,EAAE,CAAC,CACpC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,EACnF,WAAW,EAAE,EAAE,EACf,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU;AACrD,mBAAmB,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,EAChH,aAAa,CAAC,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC;UAmIf,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;UAAjD,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;UAAjD,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;UAAjD,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;EAI3E"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { computed, ref, toValue } from 'vue';
|
|
2
|
+
import { useMutation } from '@tanstack/vue-query';
|
|
3
|
+
import { HttpMethod, } from './types';
|
|
4
|
+
import { resolvePath, generateQueryKey, isPathResolved, getParamsOptionsFrom } from './openapi-utils';
|
|
5
|
+
import { queryClient } from './index';
|
|
6
|
+
/**
|
|
7
|
+
* Composable for performing a strictly typed OpenAPI mutation operation using Vue Query.
|
|
8
|
+
* Ensures the operation is a mutation (POST/PUT/PATCH/DELETE) at runtime.
|
|
9
|
+
* Returns a reactive mutation object, including helpers for query key and enabled state.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: By default, the mutation will automatically update cache with returned data and reload
|
|
12
|
+
* any matching GET queries for the same path.
|
|
13
|
+
*
|
|
14
|
+
* @template T OperationId type representing the OpenAPI operation.
|
|
15
|
+
* @param operationId The OpenAPI operation ID to mutate.
|
|
16
|
+
* @param pathParams Optional path parameters for the endpoint, can be reactive.
|
|
17
|
+
* @param options Optional mutation options, including Vue Query options and custom axios options:
|
|
18
|
+
* - 'dontUpdateCache': If true, will not update cache with returned data (default: false)
|
|
19
|
+
* - 'dontInvalidate': If true, will not invalidate matching GET queries (default: false)
|
|
20
|
+
* - 'invalidateOperations': List of additional OperationIds to invalidate after mutation (can also be a map of OperationId to path parameters)
|
|
21
|
+
* - 'refetchEndpoints': List of additional EndpointQueryReturn objects to refetch after mutation
|
|
22
|
+
* - `axiosOptions`: Custom axios request options (e.g., headers, params)
|
|
23
|
+
* - All properties from {@link UseMutationOptions} (from @tanstack/vue-query)
|
|
24
|
+
* @throws Error if the operation is not a mutation operation.
|
|
25
|
+
* @returns Mutation object with
|
|
26
|
+
* - `data`: ComputedRef of response data.
|
|
27
|
+
* - `isEnabled`: ComputedRef indicating if mutation can be executed (path resolved).
|
|
28
|
+
* - `extraPathParams`: Ref to set of additional path parameters when calling mutate.
|
|
29
|
+
* - `mutate` and `mutateAsync`: Functions to trigger the mutation, taking an object with:
|
|
30
|
+
* - `data`: The request body data for the mutation.
|
|
31
|
+
* - `pathParams`: Optional additional path parameters for the mutation.
|
|
32
|
+
* - `dontUpdateCache`, `dontInvalidate`, `invalidateOperations`, `refetchEndpoints`: Same as options, but can be set per-mutation.
|
|
33
|
+
* - All other properties and methods from the underlying Vue Query mutation object.
|
|
34
|
+
*/
|
|
35
|
+
export function useEndpointMutation(operationId, h, // helpers
|
|
36
|
+
pathParamsOrOptions, optionsOrNull) {
|
|
37
|
+
// Runtime check to ensure this is actually a mutation operation
|
|
38
|
+
if (!h.isMutationOperation(operationId)) {
|
|
39
|
+
throw new Error(`Operation ${String(operationId)} is not a mutation operation (POST/PUT/PATCH/DELETE)`);
|
|
40
|
+
}
|
|
41
|
+
const { path, method } = h.getOperationInfo(operationId);
|
|
42
|
+
const { pathParams, options } = getParamsOptionsFrom(pathParamsOrOptions, optionsOrNull);
|
|
43
|
+
const { axiosOptions, dontInvalidate, dontUpdateCache, invalidateOperations, refetchEndpoints, ...useMutationOptions } = options;
|
|
44
|
+
const extraPathParams = ref({});
|
|
45
|
+
// Compute the resolved path
|
|
46
|
+
const allPathParams = computed(() => ({
|
|
47
|
+
...toValue(pathParams),
|
|
48
|
+
...extraPathParams.value,
|
|
49
|
+
}));
|
|
50
|
+
const resolvedPath = computed(() => resolvePath(path, allPathParams.value));
|
|
51
|
+
const queryKey = computed(() => generateQueryKey(resolvedPath.value));
|
|
52
|
+
const mutation = useMutation({
|
|
53
|
+
mutationFn: async (vars) => {
|
|
54
|
+
const { data, pathParams: pathParamsFromMutate } = vars;
|
|
55
|
+
extraPathParams.value = pathParamsFromMutate || {};
|
|
56
|
+
// TODO: use typing to ensure all required path params are provided
|
|
57
|
+
if (!isPathResolved(resolvedPath.value)) {
|
|
58
|
+
return Promise.reject(new Error(`Mutation for '${String(operationId)}' cannot be used, as path is not resolved: ${resolvedPath.value} (params: ${JSON.stringify(allPathParams.value)})`));
|
|
59
|
+
}
|
|
60
|
+
// Cancel any ongoing queries for this path (prevent race conditions with refresh)
|
|
61
|
+
await queryClient.cancelQueries({ queryKey: queryKey.value, exact: false });
|
|
62
|
+
const response = await h.axios({
|
|
63
|
+
method: method.toLowerCase(),
|
|
64
|
+
url: resolvedPath.value,
|
|
65
|
+
data: data,
|
|
66
|
+
...(axiosOptions || {}),
|
|
67
|
+
});
|
|
68
|
+
return response.data;
|
|
69
|
+
},
|
|
70
|
+
onSuccess: async (data, vars, _context) => {
|
|
71
|
+
const { dontInvalidate: dontInvalidateMutate, dontUpdateCache: dontUpdateCacheMutate, invalidateOperations: invalidateOperationsMutate, refetchEndpoints: refetchEndpointsMutate, } = vars;
|
|
72
|
+
// Optimistically update cache with returned data for PUT/PATCH requests
|
|
73
|
+
if (
|
|
74
|
+
// dontUpdateCacheMutate supersedes dontUpdateCache from options
|
|
75
|
+
(dontInvalidateMutate !== undefined ? !dontInvalidateMutate : !dontInvalidate) &&
|
|
76
|
+
data &&
|
|
77
|
+
[HttpMethod.PUT, HttpMethod.PATCH].includes(method)) {
|
|
78
|
+
await queryClient.setQueryData(queryKey.value, data);
|
|
79
|
+
}
|
|
80
|
+
// Invalidate queries for this path, and any additional specified operations
|
|
81
|
+
if (dontUpdateCacheMutate !== undefined ? !dontUpdateCacheMutate : !dontUpdateCache) {
|
|
82
|
+
// Invalidate all queries for this path (exact for POST, prefix for others):
|
|
83
|
+
await queryClient.invalidateQueries({ queryKey: queryKey.value, exact: method !== HttpMethod.POST });
|
|
84
|
+
const listPath = h.getListOperationPath(operationId);
|
|
85
|
+
if (listPath) {
|
|
86
|
+
const listResolvedPath = resolvePath(listPath, pathParams);
|
|
87
|
+
if (isPathResolved(listResolvedPath)) {
|
|
88
|
+
const listQueryKey = generateQueryKey(listResolvedPath);
|
|
89
|
+
await queryClient.invalidateQueries({ queryKey: listQueryKey, exact: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const operationsWithPathParams = [];
|
|
94
|
+
Array.from([invalidateOperations, invalidateOperationsMutate]).forEach((ops) => {
|
|
95
|
+
operationsWithPathParams.push(...(typeof ops === 'object' && !Array.isArray(ops)
|
|
96
|
+
? Object.entries(ops)
|
|
97
|
+
: ops?.map((opId) => [opId, {}]) || []));
|
|
98
|
+
});
|
|
99
|
+
if (operationsWithPathParams.length > 0) {
|
|
100
|
+
const promises = operationsWithPathParams.map(([opId, opParams]) => {
|
|
101
|
+
const opInfo = h.getOperationInfo(opId);
|
|
102
|
+
const opPath = resolvePath(opInfo.path, {
|
|
103
|
+
...allPathParams.value,
|
|
104
|
+
...opParams,
|
|
105
|
+
});
|
|
106
|
+
if (isPathResolved(opPath)) {
|
|
107
|
+
const opQueryKey = generateQueryKey(opPath);
|
|
108
|
+
return queryClient.invalidateQueries({ queryKey: opQueryKey, exact: true });
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.warn(`Cannot invalidate operation '${String(opId)}', path not resolved: ${opPath} (params: ${JSON.stringify({ ...allPathParams.value, ...opParams })})`);
|
|
112
|
+
return Promise.reject();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
await Promise.all(promises);
|
|
116
|
+
}
|
|
117
|
+
if (refetchEndpoints && refetchEndpoints.length > 0) {
|
|
118
|
+
await Promise.all(refetchEndpoints.map((endpoint) => endpoint.refetch()));
|
|
119
|
+
}
|
|
120
|
+
if (refetchEndpointsMutate && refetchEndpointsMutate.length > 0) {
|
|
121
|
+
await Promise.all(refetchEndpointsMutate.map((endpoint) => endpoint.refetch()));
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
onSettled: () => {
|
|
125
|
+
extraPathParams.value = {};
|
|
126
|
+
},
|
|
127
|
+
...useMutationOptions,
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
...mutation,
|
|
131
|
+
data: mutation.data,
|
|
132
|
+
isEnabled: computed(() => isPathResolved(resolvedPath.value)),
|
|
133
|
+
extraPathParams,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
|
|
2
|
+
import { Operations, type GetPathParameters, type GetResponseData, type QueryOptions } from './types';
|
|
3
|
+
import type { AxiosError } from 'axios';
|
|
4
|
+
import { getHelpers } from './openapi-helpers';
|
|
5
|
+
export type EndpointQueryReturn<Ops extends Operations<Ops>, Op extends keyof Ops> = ReturnType<typeof useEndpointQuery<Ops, Op>> & {
|
|
6
|
+
onLoad: (callback: (data: GetResponseData<Ops, Op>) => void) => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Composable for performing a strictly typed OpenAPI query operation using Vue Query.
|
|
10
|
+
* Ensures the operation is a query (GET/HEAD/OPTIONS) at runtime.
|
|
11
|
+
* Returns a reactive query object, including helpers for query key, enabled state, and an `onLoad` callback.
|
|
12
|
+
*
|
|
13
|
+
* @template T OperationId type representing the OpenAPI operation.
|
|
14
|
+
* @param operationId The OpenAPI operation ID to query.
|
|
15
|
+
* @param pathParams Optional path parameters for the endpoint, can be reactive.
|
|
16
|
+
* @param options Optional query options, including:
|
|
17
|
+
* - All properties from {@link UseQueryOptions} (from @tanstack/vue-query)
|
|
18
|
+
* - `enabled`: Whether the query should automatically run (boolean or reactive).
|
|
19
|
+
* - `onLoad`: Callback invoked once when data is loaded (immediately or after fetch).
|
|
20
|
+
* - `axiosOptions`: Custom axios request options (e.g., headers, params).
|
|
21
|
+
* @throws Error if the operation is not a query operation.
|
|
22
|
+
* @returns Query object with strict typing and helpers:
|
|
23
|
+
* - `data`: ComputedRef of response data.
|
|
24
|
+
* - `isEnabled`: ComputedRef indicating if query is enabled.
|
|
25
|
+
* - `queryKey`: ComputedRef of the query key.
|
|
26
|
+
* - `onLoad`: Method to register a callback for when data is loaded.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useEndpointQuery<Ops extends Operations<Ops>, Op extends keyof Ops>(operationId: Op, h: ReturnType<typeof getHelpers<Ops, Op>>, pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | QueryOptions<Ops, Op>, optionsOrNull?: QueryOptions<Ops, Op>): {
|
|
29
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
30
|
+
isEnabled: ComputedRef<boolean>;
|
|
31
|
+
queryKey: ComputedRef<string[]>;
|
|
32
|
+
onLoad: (callback: (data: GetResponseData<Ops, Op>) => void) => void;
|
|
33
|
+
error: import("vue").Ref<AxiosError<unknown, any>, AxiosError<unknown, any>>;
|
|
34
|
+
isError: import("vue").Ref<true, true>;
|
|
35
|
+
isPending: import("vue").Ref<false, false>;
|
|
36
|
+
isLoading: import("vue").Ref<false, false>;
|
|
37
|
+
isLoadingError: import("vue").Ref<false, false>;
|
|
38
|
+
isRefetchError: import("vue").Ref<true, true>;
|
|
39
|
+
isSuccess: import("vue").Ref<false, false>;
|
|
40
|
+
isPlaceholderData: import("vue").Ref<false, false>;
|
|
41
|
+
status: import("vue").Ref<"error", "error">;
|
|
42
|
+
dataUpdatedAt: import("vue").Ref<number, number>;
|
|
43
|
+
errorUpdatedAt: import("vue").Ref<number, number>;
|
|
44
|
+
failureCount: import("vue").Ref<number, number>;
|
|
45
|
+
failureReason: import("vue").Ref<AxiosError<unknown, any> | null, AxiosError<unknown, any> | null>;
|
|
46
|
+
errorUpdateCount: import("vue").Ref<number, number>;
|
|
47
|
+
isFetched: import("vue").Ref<boolean, boolean>;
|
|
48
|
+
isFetchedAfterMount: import("vue").Ref<boolean, boolean>;
|
|
49
|
+
isFetching: import("vue").Ref<boolean, boolean>;
|
|
50
|
+
isInitialLoading: import("vue").Ref<boolean, boolean>;
|
|
51
|
+
isPaused: import("vue").Ref<boolean, boolean>;
|
|
52
|
+
isRefetching: import("vue").Ref<boolean, boolean>;
|
|
53
|
+
isStale: import("vue").Ref<boolean, boolean>;
|
|
54
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
55
|
+
fetchStatus: import("vue").Ref<import("@tanstack/query-core").FetchStatus, import("@tanstack/query-core").FetchStatus>;
|
|
56
|
+
promise: import("vue").Ref<Promise<GetResponseData<Ops, Op>>, Promise<GetResponseData<Ops, Op>>>;
|
|
57
|
+
suspense: () => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
58
|
+
} | {
|
|
59
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
60
|
+
isEnabled: ComputedRef<boolean>;
|
|
61
|
+
queryKey: ComputedRef<string[]>;
|
|
62
|
+
onLoad: (callback: (data: GetResponseData<Ops, Op>) => void) => void;
|
|
63
|
+
error: import("vue").Ref<null, null>;
|
|
64
|
+
isError: import("vue").Ref<false, false>;
|
|
65
|
+
isPending: import("vue").Ref<false, false>;
|
|
66
|
+
isLoading: import("vue").Ref<false, false>;
|
|
67
|
+
isLoadingError: import("vue").Ref<false, false>;
|
|
68
|
+
isRefetchError: import("vue").Ref<false, false>;
|
|
69
|
+
isSuccess: import("vue").Ref<true, true>;
|
|
70
|
+
isPlaceholderData: import("vue").Ref<false, false>;
|
|
71
|
+
status: import("vue").Ref<"success", "success">;
|
|
72
|
+
dataUpdatedAt: import("vue").Ref<number, number>;
|
|
73
|
+
errorUpdatedAt: import("vue").Ref<number, number>;
|
|
74
|
+
failureCount: import("vue").Ref<number, number>;
|
|
75
|
+
failureReason: import("vue").Ref<AxiosError<unknown, any> | null, AxiosError<unknown, any> | null>;
|
|
76
|
+
errorUpdateCount: import("vue").Ref<number, number>;
|
|
77
|
+
isFetched: import("vue").Ref<boolean, boolean>;
|
|
78
|
+
isFetchedAfterMount: import("vue").Ref<boolean, boolean>;
|
|
79
|
+
isFetching: import("vue").Ref<boolean, boolean>;
|
|
80
|
+
isInitialLoading: import("vue").Ref<boolean, boolean>;
|
|
81
|
+
isPaused: import("vue").Ref<boolean, boolean>;
|
|
82
|
+
isRefetching: import("vue").Ref<boolean, boolean>;
|
|
83
|
+
isStale: import("vue").Ref<boolean, boolean>;
|
|
84
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
85
|
+
fetchStatus: import("vue").Ref<import("@tanstack/query-core").FetchStatus, import("@tanstack/query-core").FetchStatus>;
|
|
86
|
+
promise: import("vue").Ref<Promise<GetResponseData<Ops, Op>>, Promise<GetResponseData<Ops, Op>>>;
|
|
87
|
+
suspense: () => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
88
|
+
} | {
|
|
89
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
90
|
+
isEnabled: ComputedRef<boolean>;
|
|
91
|
+
queryKey: ComputedRef<string[]>;
|
|
92
|
+
onLoad: (callback: (data: GetResponseData<Ops, Op>) => void) => void;
|
|
93
|
+
error: import("vue").Ref<AxiosError<unknown, any>, AxiosError<unknown, any>>;
|
|
94
|
+
isError: import("vue").Ref<true, true>;
|
|
95
|
+
isPending: import("vue").Ref<false, false>;
|
|
96
|
+
isLoading: import("vue").Ref<false, false>;
|
|
97
|
+
isLoadingError: import("vue").Ref<true, true>;
|
|
98
|
+
isRefetchError: import("vue").Ref<false, false>;
|
|
99
|
+
isSuccess: import("vue").Ref<false, false>;
|
|
100
|
+
isPlaceholderData: import("vue").Ref<false, false>;
|
|
101
|
+
status: import("vue").Ref<"error", "error">;
|
|
102
|
+
dataUpdatedAt: import("vue").Ref<number, number>;
|
|
103
|
+
errorUpdatedAt: import("vue").Ref<number, number>;
|
|
104
|
+
failureCount: import("vue").Ref<number, number>;
|
|
105
|
+
failureReason: import("vue").Ref<AxiosError<unknown, any> | null, AxiosError<unknown, any> | null>;
|
|
106
|
+
errorUpdateCount: import("vue").Ref<number, number>;
|
|
107
|
+
isFetched: import("vue").Ref<boolean, boolean>;
|
|
108
|
+
isFetchedAfterMount: import("vue").Ref<boolean, boolean>;
|
|
109
|
+
isFetching: import("vue").Ref<boolean, boolean>;
|
|
110
|
+
isInitialLoading: import("vue").Ref<boolean, boolean>;
|
|
111
|
+
isPaused: import("vue").Ref<boolean, boolean>;
|
|
112
|
+
isRefetching: import("vue").Ref<boolean, boolean>;
|
|
113
|
+
isStale: import("vue").Ref<boolean, boolean>;
|
|
114
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
115
|
+
fetchStatus: import("vue").Ref<import("@tanstack/query-core").FetchStatus, import("@tanstack/query-core").FetchStatus>;
|
|
116
|
+
promise: import("vue").Ref<Promise<GetResponseData<Ops, Op>>, Promise<GetResponseData<Ops, Op>>>;
|
|
117
|
+
suspense: () => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
118
|
+
} | {
|
|
119
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
120
|
+
isEnabled: ComputedRef<boolean>;
|
|
121
|
+
queryKey: ComputedRef<string[]>;
|
|
122
|
+
onLoad: (callback: (data: GetResponseData<Ops, Op>) => void) => void;
|
|
123
|
+
error: import("vue").Ref<null, null>;
|
|
124
|
+
isError: import("vue").Ref<false, false>;
|
|
125
|
+
isPending: import("vue").Ref<true, true>;
|
|
126
|
+
isLoading: import("vue").Ref<true, true>;
|
|
127
|
+
isLoadingError: import("vue").Ref<false, false>;
|
|
128
|
+
isRefetchError: import("vue").Ref<false, false>;
|
|
129
|
+
isSuccess: import("vue").Ref<false, false>;
|
|
130
|
+
isPlaceholderData: import("vue").Ref<false, false>;
|
|
131
|
+
status: import("vue").Ref<"pending", "pending">;
|
|
132
|
+
dataUpdatedAt: import("vue").Ref<number, number>;
|
|
133
|
+
errorUpdatedAt: import("vue").Ref<number, number>;
|
|
134
|
+
failureCount: import("vue").Ref<number, number>;
|
|
135
|
+
failureReason: import("vue").Ref<AxiosError<unknown, any> | null, AxiosError<unknown, any> | null>;
|
|
136
|
+
errorUpdateCount: import("vue").Ref<number, number>;
|
|
137
|
+
isFetched: import("vue").Ref<boolean, boolean>;
|
|
138
|
+
isFetchedAfterMount: import("vue").Ref<boolean, boolean>;
|
|
139
|
+
isFetching: import("vue").Ref<boolean, boolean>;
|
|
140
|
+
isInitialLoading: import("vue").Ref<boolean, boolean>;
|
|
141
|
+
isPaused: import("vue").Ref<boolean, boolean>;
|
|
142
|
+
isRefetching: import("vue").Ref<boolean, boolean>;
|
|
143
|
+
isStale: import("vue").Ref<boolean, boolean>;
|
|
144
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
145
|
+
fetchStatus: import("vue").Ref<import("@tanstack/query-core").FetchStatus, import("@tanstack/query-core").FetchStatus>;
|
|
146
|
+
promise: import("vue").Ref<Promise<GetResponseData<Ops, Op>>, Promise<GetResponseData<Ops, Op>>>;
|
|
147
|
+
suspense: () => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
148
|
+
} | {
|
|
149
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
150
|
+
isEnabled: ComputedRef<boolean>;
|
|
151
|
+
queryKey: ComputedRef<string[]>;
|
|
152
|
+
onLoad: (callback: (data: GetResponseData<Ops, Op>) => void) => void;
|
|
153
|
+
error: import("vue").Ref<null, null>;
|
|
154
|
+
isError: import("vue").Ref<false, false>;
|
|
155
|
+
isPending: import("vue").Ref<true, true>;
|
|
156
|
+
isLoadingError: import("vue").Ref<false, false>;
|
|
157
|
+
isRefetchError: import("vue").Ref<false, false>;
|
|
158
|
+
isSuccess: import("vue").Ref<false, false>;
|
|
159
|
+
isPlaceholderData: import("vue").Ref<false, false>;
|
|
160
|
+
status: import("vue").Ref<"pending", "pending">;
|
|
161
|
+
dataUpdatedAt: import("vue").Ref<number, number>;
|
|
162
|
+
errorUpdatedAt: import("vue").Ref<number, number>;
|
|
163
|
+
failureCount: import("vue").Ref<number, number>;
|
|
164
|
+
failureReason: import("vue").Ref<AxiosError<unknown, any> | null, AxiosError<unknown, any> | null>;
|
|
165
|
+
errorUpdateCount: import("vue").Ref<number, number>;
|
|
166
|
+
isFetched: import("vue").Ref<boolean, boolean>;
|
|
167
|
+
isFetchedAfterMount: import("vue").Ref<boolean, boolean>;
|
|
168
|
+
isFetching: import("vue").Ref<boolean, boolean>;
|
|
169
|
+
isLoading: import("vue").Ref<boolean, boolean>;
|
|
170
|
+
isInitialLoading: import("vue").Ref<boolean, boolean>;
|
|
171
|
+
isPaused: import("vue").Ref<boolean, boolean>;
|
|
172
|
+
isRefetching: import("vue").Ref<boolean, boolean>;
|
|
173
|
+
isStale: import("vue").Ref<boolean, boolean>;
|
|
174
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
175
|
+
fetchStatus: import("vue").Ref<import("@tanstack/query-core").FetchStatus, import("@tanstack/query-core").FetchStatus>;
|
|
176
|
+
promise: import("vue").Ref<Promise<GetResponseData<Ops, Op>>, Promise<GetResponseData<Ops, Op>>>;
|
|
177
|
+
suspense: () => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
178
|
+
} | {
|
|
179
|
+
data: ComputedRef<GetResponseData<Ops, Op> | undefined>;
|
|
180
|
+
isEnabled: ComputedRef<boolean>;
|
|
181
|
+
queryKey: ComputedRef<string[]>;
|
|
182
|
+
onLoad: (callback: (data: GetResponseData<Ops, Op>) => void) => void;
|
|
183
|
+
isError: import("vue").Ref<false, false>;
|
|
184
|
+
error: import("vue").Ref<null, null>;
|
|
185
|
+
isPending: import("vue").Ref<false, false>;
|
|
186
|
+
isLoading: import("vue").Ref<false, false>;
|
|
187
|
+
isLoadingError: import("vue").Ref<false, false>;
|
|
188
|
+
isRefetchError: import("vue").Ref<false, false>;
|
|
189
|
+
isSuccess: import("vue").Ref<true, true>;
|
|
190
|
+
isPlaceholderData: import("vue").Ref<true, true>;
|
|
191
|
+
status: import("vue").Ref<"success", "success">;
|
|
192
|
+
dataUpdatedAt: import("vue").Ref<number, number>;
|
|
193
|
+
errorUpdatedAt: import("vue").Ref<number, number>;
|
|
194
|
+
failureCount: import("vue").Ref<number, number>;
|
|
195
|
+
failureReason: import("vue").Ref<AxiosError<unknown, any> | null, AxiosError<unknown, any> | null>;
|
|
196
|
+
errorUpdateCount: import("vue").Ref<number, number>;
|
|
197
|
+
isFetched: import("vue").Ref<boolean, boolean>;
|
|
198
|
+
isFetchedAfterMount: import("vue").Ref<boolean, boolean>;
|
|
199
|
+
isFetching: import("vue").Ref<boolean, boolean>;
|
|
200
|
+
isInitialLoading: import("vue").Ref<boolean, boolean>;
|
|
201
|
+
isPaused: import("vue").Ref<boolean, boolean>;
|
|
202
|
+
isRefetching: import("vue").Ref<boolean, boolean>;
|
|
203
|
+
isStale: import("vue").Ref<boolean, boolean>;
|
|
204
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
205
|
+
fetchStatus: import("vue").Ref<import("@tanstack/query-core").FetchStatus, import("@tanstack/query-core").FetchStatus>;
|
|
206
|
+
promise: import("vue").Ref<Promise<GetResponseData<Ops, Op>>, Promise<GetResponseData<Ops, Op>>>;
|
|
207
|
+
suspense: () => Promise<import("@tanstack/query-core").QueryObserverResult<GetResponseData<Ops, Op>, AxiosError<unknown, any>>>;
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=openapi-query.d.ts.map
|
|
@@ -0,0 +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,MAAM,KAAK,CAAA;AAEvF,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,YAAY,EAGlB,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAU9C,MAAM,MAAM,mBAAmB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,IAAI,UAAU,CAC7F,OAAO,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,CACjC,GAAG;IACF,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,CAAA;CACrE,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,EAChF,WAAW,EAAE,EAAE,EACf,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EACzC,mBAAmB,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,EAC7G,aAAa,CAAC,EAAE,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC;UAiFf,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;uBAN7C,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;UAM5C,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;uBAN7C,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;UAM5C,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;uBAN7C,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;UAM5C,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;uBAN7C,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;UAM5C,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;uBAN7C,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;UAM5C,WAAW,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;;;uBAN7C,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;EAWnE"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { computed, watch, toValue } from 'vue';
|
|
2
|
+
import { useQuery } from '@tanstack/vue-query';
|
|
3
|
+
import { resolvePath, generateQueryKey, isPathResolved, getParamsOptionsFrom } from './openapi-utils';
|
|
4
|
+
import { queryClient } from './index';
|
|
5
|
+
/**
|
|
6
|
+
* Composable for performing a strictly typed OpenAPI query operation using Vue Query.
|
|
7
|
+
* Ensures the operation is a query (GET/HEAD/OPTIONS) at runtime.
|
|
8
|
+
* Returns a reactive query object, including helpers for query key, enabled state, and an `onLoad` callback.
|
|
9
|
+
*
|
|
10
|
+
* @template T OperationId type representing the OpenAPI operation.
|
|
11
|
+
* @param operationId The OpenAPI operation ID to query.
|
|
12
|
+
* @param pathParams Optional path parameters for the endpoint, can be reactive.
|
|
13
|
+
* @param options Optional query options, including:
|
|
14
|
+
* - All properties from {@link UseQueryOptions} (from @tanstack/vue-query)
|
|
15
|
+
* - `enabled`: Whether the query should automatically run (boolean or reactive).
|
|
16
|
+
* - `onLoad`: Callback invoked once when data is loaded (immediately or after fetch).
|
|
17
|
+
* - `axiosOptions`: Custom axios request options (e.g., headers, params).
|
|
18
|
+
* @throws Error if the operation is not a query operation.
|
|
19
|
+
* @returns Query object with strict typing and helpers:
|
|
20
|
+
* - `data`: ComputedRef of response data.
|
|
21
|
+
* - `isEnabled`: ComputedRef indicating if query is enabled.
|
|
22
|
+
* - `queryKey`: ComputedRef of the query key.
|
|
23
|
+
* - `onLoad`: Method to register a callback for when data is loaded.
|
|
24
|
+
*/
|
|
25
|
+
export function useEndpointQuery(operationId, h, pathParamsOrOptions, optionsOrNull) {
|
|
26
|
+
// Runtime check to ensure this is actually a query operation
|
|
27
|
+
if (!h.isQueryOperation(operationId)) {
|
|
28
|
+
throw new Error(`Operation ${String(operationId)} is not a query operation (GET/HEAD/OPTIONS)`);
|
|
29
|
+
}
|
|
30
|
+
const { path, method } = h.getOperationInfo(operationId);
|
|
31
|
+
const { pathParams, options } = getParamsOptionsFrom(pathParamsOrOptions, optionsOrNull);
|
|
32
|
+
const { enabled: enabledInit, onLoad: onLoadInit, axiosOptions, ...useQueryOptions } = options;
|
|
33
|
+
const resolvedPath = computed(() => resolvePath(path, pathParams));
|
|
34
|
+
const queryKey = computed(() => generateQueryKey(resolvedPath.value));
|
|
35
|
+
// Check if path is fully resolved for enabling the query
|
|
36
|
+
const isEnabled = computed(() => {
|
|
37
|
+
// can be explicitly disabled via options
|
|
38
|
+
const baseEnabled = enabledInit !== undefined ? toValue(enabledInit) : true;
|
|
39
|
+
return baseEnabled && isPathResolved(resolvedPath.value);
|
|
40
|
+
});
|
|
41
|
+
const query = useQuery({
|
|
42
|
+
queryKey: queryKey,
|
|
43
|
+
queryFn: async () => {
|
|
44
|
+
const response = await h.axios({
|
|
45
|
+
method: method.toLowerCase(),
|
|
46
|
+
url: resolvedPath.value,
|
|
47
|
+
...(axiosOptions || {}),
|
|
48
|
+
});
|
|
49
|
+
return response.data;
|
|
50
|
+
},
|
|
51
|
+
enabled: isEnabled,
|
|
52
|
+
staleTime: 1000 * 60,
|
|
53
|
+
retry: (failureCount, error) => {
|
|
54
|
+
// Don't retry 4xx errors
|
|
55
|
+
if (error.response && error.response.status >= 400 && error.response.status < 500) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
// Retry up to 3 times for other errors
|
|
59
|
+
return failureCount < 3;
|
|
60
|
+
},
|
|
61
|
+
...useQueryOptions,
|
|
62
|
+
}, queryClient);
|
|
63
|
+
// onLoad callback is called once, as soon as data is available (immediately or when loading finishes)
|
|
64
|
+
// Shared onLoad handler setup
|
|
65
|
+
const setupOnLoadHandler = (callback) => {
|
|
66
|
+
// If data is already available, call immediately
|
|
67
|
+
if (query.data.value !== undefined) {
|
|
68
|
+
callback(query.data.value);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Watch for data to become available
|
|
72
|
+
let hasLoaded = false;
|
|
73
|
+
const stopWatch = watch(query.data, (newData) => {
|
|
74
|
+
if (newData !== undefined && !hasLoaded) {
|
|
75
|
+
hasLoaded = true;
|
|
76
|
+
callback(newData);
|
|
77
|
+
stopWatch(); // Stop watching after first load
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
// Handle onLoad callback from options
|
|
83
|
+
if (onLoadInit) {
|
|
84
|
+
setupOnLoadHandler(onLoadInit);
|
|
85
|
+
}
|
|
86
|
+
// Create onLoad method
|
|
87
|
+
const onLoad = (callback) => {
|
|
88
|
+
setupOnLoadHandler(callback);
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
...query,
|
|
92
|
+
data: query.data,
|
|
93
|
+
isEnabled,
|
|
94
|
+
queryKey,
|
|
95
|
+
onLoad,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type MaybeRefOrGetter } from 'vue';
|
|
2
|
+
import { type GetPathParameters, type MutationOptions, type QueryOptions, Operations } from './types';
|
|
3
|
+
export declare function resolvePath(path: string, pathParams?: MaybeRefOrGetter<Record<string, string | number | undefined> | null | undefined>): string;
|
|
4
|
+
export declare function isPathResolved(path: string): boolean;
|
|
5
|
+
export declare function generateQueryKey(resolvedPath: string): string[];
|
|
6
|
+
export declare function getParamsOptionsFrom<Ops extends Operations<Ops>, Op extends keyof Ops, Options extends MutationOptions<Ops, Op> | QueryOptions<Ops, Op>>(pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | Options, optionsOrNull?: Options): {
|
|
7
|
+
pathParams: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined>;
|
|
8
|
+
options: Options;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=openapi-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-utils.d.ts","sourceRoot":"","sources":["../src/openapi-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGrG,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,wBAAgB,oBAAoB,CAClC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAC3B,EAAE,SAAS,MAAM,GAAG,EACpB,OAAO,SAAS,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,EAEhE,mBAAmB,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,OAAO,EAC/F,aAAa,CAAC,EAAE,OAAO,GACtB;IACD,UAAU,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;IAC3E,OAAO,EAAE,OAAO,CAAA;CACjB,CAiBA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { toValue } from 'vue';
|
|
2
|
+
// Helper to resolve path parameters in a URL path
|
|
3
|
+
export function resolvePath(path, pathParams) {
|
|
4
|
+
if (pathParams === null || pathParams === undefined)
|
|
5
|
+
return path;
|
|
6
|
+
const pathParamsValue = toValue(pathParams);
|
|
7
|
+
if (!pathParamsValue)
|
|
8
|
+
return path;
|
|
9
|
+
let resolvedPath = path;
|
|
10
|
+
Object.entries(pathParamsValue).forEach(([key, value]) => {
|
|
11
|
+
if (value !== undefined && value !== null) {
|
|
12
|
+
resolvedPath = resolvedPath.replace(`{${key}}`, String(value));
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return resolvedPath;
|
|
16
|
+
}
|
|
17
|
+
// Helper to check if all required path parameters are provided
|
|
18
|
+
export function isPathResolved(path) {
|
|
19
|
+
return !/{[^}]+}/.test(path);
|
|
20
|
+
}
|
|
21
|
+
// Helper to generate query key from resolved path
|
|
22
|
+
export function generateQueryKey(resolvedPath) {
|
|
23
|
+
return resolvedPath.split('/').filter((segment) => segment.length > 0);
|
|
24
|
+
}
|
|
25
|
+
export function getParamsOptionsFrom(pathParamsOrOptions, optionsOrNull) {
|
|
26
|
+
// Check if pathParamsOrOptions is MutationOptions or QueryOptions by checking if options is undefined
|
|
27
|
+
// and pathParamsOrOptions has MutationOptions properties
|
|
28
|
+
let pathParams;
|
|
29
|
+
let options;
|
|
30
|
+
if (optionsOrNull === undefined && pathParamsOrOptions && typeof pathParamsOrOptions === 'object') {
|
|
31
|
+
// Called as: useEndpointQuery(operationId, options)
|
|
32
|
+
options = pathParamsOrOptions;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Called as: useEndpointQuery(operationId, pathParams, options)
|
|
36
|
+
pathParams = pathParamsOrOptions;
|
|
37
|
+
options = optionsOrNull;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
pathParams: pathParams ?? {},
|
|
41
|
+
options: options ?? {},
|
|
42
|
+
};
|
|
43
|
+
}
|