@qualisero/openapi-endpoint 0.12.3 → 0.13.2

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,118 +1,86 @@
1
1
  import { type ComputedRef, type Ref, type MaybeRefOrGetter } from 'vue';
2
- import { type GetPathParameters, type QMutationVars, type GetResponseData, type QMutationOptions, Operations, GetRequestBody } from './types';
2
+ import { type ApiPathParams, type ApiPathParamsInput, type ApiResponse, type QMutationOptions, type MutateFn, type MutateAsyncFn, Operations } from './types';
3
3
  import { type OpenApiHelpers } from './openapi-helpers';
4
4
  import { type AxiosResponse } from 'axios';
5
- export type EndpointMutationReturn<Ops extends Operations<Ops>, Op extends keyof Ops> = ReturnType<typeof useEndpointMutation<Ops, Op>>;
6
5
  /**
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.
6
+ * Return type of `useMutation` (created via `useOpenApi`).
10
7
  *
11
- * NOTE: By default, the mutation will automatically update cache with returned data and reload
12
- * any matching GET queries for the same path.
8
+ * Reactive mutation result with automatic cache management and helpers.
13
9
  *
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.
10
+ * All properties are reactive (ComputedRef/Ref) and auto-unwrap in Vue templates.
11
+ *
12
+ * @template Ops - The operations type from your OpenAPI specification
13
+ * @template Op - The operation key from your operations type
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const mutation = api.useMutation('createPet')
18
+ *
19
+ * // Reactive properties
20
+ * if (mutation.isPending.value) console.log('Saving...')
21
+ * if (mutation.isSuccess.value) console.log('Created:', mutation.data.value)
22
+ *
23
+ * // Execute
24
+ * mutation.mutate({ data: { name: 'Fluffy' } })
25
+ * await mutation.mutateAsync({ data: { name: 'Fluffy' } })
26
+ * ```
27
+ *
28
+ * @group Types
29
+ */
30
+ export interface EndpointMutationReturn<Ops extends Operations<Ops>, Op extends keyof Ops> {
31
+ /** The Axios response (undefined until mutation completes). */
32
+ data: ComputedRef<AxiosResponse<ApiResponse<Ops, Op>> | undefined>;
33
+ /** The error if the mutation failed. */
34
+ error: Ref<Error | null>;
35
+ /** True while the mutation is in progress. */
36
+ isPending: Ref<boolean>;
37
+ /** True when the mutation succeeded. */
38
+ isSuccess: Ref<boolean>;
39
+ /** True when the mutation failed. */
40
+ isError: Ref<boolean>;
41
+ /** Execute the mutation (non-blocking). */
42
+ mutate: MutateFn<Ops, Op>;
43
+ /** Execute the mutation and wait for the response. */
44
+ mutateAsync: MutateAsyncFn<Ops, Op>;
45
+ /** Reset the mutation state. */
46
+ reset: () => void;
47
+ /** Whether the mutation can execute (path parameters resolved). */
48
+ isEnabled: ComputedRef<boolean>;
49
+ /** The resolved path parameters. */
50
+ pathParams: ComputedRef<ApiPathParams<Ops, Op>>;
51
+ /** Additional path parameters that can be provided at mutation time. */
52
+ extraPathParams: Ref<ApiPathParams<Ops, Op>>;
53
+ }
54
+ /**
55
+ * Execute a type-safe mutation (POST/PUT/PATCH/DELETE) with automatic cache updates.
56
+ *
57
+ * Ensures the operation is a mutation at runtime and returns a reactive mutation object
58
+ * with helpers for path resolution and cache invalidation.
59
+ *
60
+ * NOTE: By default, the mutation updates cache for PUT/PATCH and invalidates matching
61
+ * GET queries for the same path.
62
+ *
63
+ * @template Ops - The operations type from your OpenAPI specification
64
+ * @template Op - The operation key from your operations type
65
+ * @param operationId - The OpenAPI operation ID to mutate
66
+ * @param h - OpenAPI helpers (internal), provided by useOpenApi
67
+ * @param pathParams - Path parameters (can be reactive). Omit for operations without path params.
68
+ * @param options - Mutation options (dontInvalidate, refetchEndpoints, etc.)
69
+ * - `dontUpdateCache`: Skip cache update for PUT/PATCH responses
70
+ * - `dontInvalidate`: Skip invalidating matching queries
71
+ * - `invalidateOperations`: Additional operation IDs to invalidate (array or map of params)
72
+ * - `refetchEndpoints`: Additional query results to refetch
73
+ * - `queryParams`: Query string parameters (operation-specific)
74
+ * - `axiosOptions`: Custom axios request options (headers, params, etc.)
75
+ * - Plus all {@link UseMutationOptions} from @tanstack/vue-query
76
+ * @throws Error if the operation is not a mutation operation
25
77
  * @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
- * - `axiosOptions`: Optional axios configuration overrides for this specific mutation call.
33
- * - `dontUpdateCache`, `dontInvalidate`, `invalidateOperations`, `refetchEndpoints`: Same as options, but can be set per-mutation.
34
- * - All other properties and methods from the underlying Vue Query mutation object.
78
+ * - `mutate(vars)` / `mutateAsync(vars)` to trigger the mutation
79
+ * - `data`: ComputedRef of Axios response data
80
+ * - `isEnabled`: ComputedRef indicating if mutation can execute (path resolved)
81
+ * - `extraPathParams`: Ref to set additional path params at call time
82
+ * - `pathParams`: Resolved path params as a computed ref
35
83
  */
36
84
  export declare function useEndpointMutation<Ops extends Operations<Ops>, Op extends keyof Ops>(operationId: Op, h: OpenApiHelpers<Ops, Op>, // helpers
37
- pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | QMutationOptions<Ops, Op>, optionsOrNull?: QMutationOptions<Ops, Op>): {
38
- data: ComputedRef<AxiosResponse<GetResponseData<Ops, Op>> | undefined>;
39
- isEnabled: ComputedRef<boolean>;
40
- extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
41
- pathParams: ComputedRef<GetPathParameters<Ops, Op>>;
42
- context: Ref<unknown, unknown>;
43
- error: Ref<null, null>;
44
- isError: Ref<false, false>;
45
- isPending: Ref<false, false>;
46
- isSuccess: Ref<false, false>;
47
- status: Ref<"idle", "idle">;
48
- failureCount: Ref<number, number>;
49
- failureReason: Ref<Error | null, Error | null>;
50
- isPaused: Ref<boolean, boolean>;
51
- variables: Ref<undefined, undefined>;
52
- isIdle: Ref<true, true>;
53
- submittedAt: Ref<number, number>;
54
- mutate: (variables: GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown> | undefined) => void;
55
- mutateAsync: import("@tanstack/query-core").MutateFunction<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown>;
56
- reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
57
- } | {
58
- data: ComputedRef<AxiosResponse<GetResponseData<Ops, Op>> | undefined>;
59
- isEnabled: ComputedRef<boolean>;
60
- extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
61
- pathParams: ComputedRef<GetPathParameters<Ops, Op>>;
62
- context: Ref<unknown, unknown>;
63
- error: Ref<null, null>;
64
- isError: Ref<false, false>;
65
- isPending: Ref<true, true>;
66
- isSuccess: Ref<false, false>;
67
- status: Ref<"pending", "pending">;
68
- failureCount: Ref<number, number>;
69
- failureReason: Ref<Error | null, Error | null>;
70
- isPaused: Ref<boolean, boolean>;
71
- variables: import("@vue/shared").IfAny<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, Ref<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>>, [GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>] extends [Ref<any, any>] ? Ref<any, any> & (GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>) : Ref<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>>>;
72
- isIdle: Ref<false, false>;
73
- submittedAt: Ref<number, number>;
74
- mutate: (variables: GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown> | undefined) => void;
75
- mutateAsync: import("@tanstack/query-core").MutateFunction<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown>;
76
- reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
77
- } | {
78
- data: ComputedRef<AxiosResponse<GetResponseData<Ops, Op>> | undefined>;
79
- isEnabled: ComputedRef<boolean>;
80
- extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
81
- pathParams: ComputedRef<GetPathParameters<Ops, Op>>;
82
- context: Ref<unknown, unknown>;
83
- error: Ref<Error, Error>;
84
- isError: Ref<true, true>;
85
- isPending: Ref<false, false>;
86
- isSuccess: Ref<false, false>;
87
- status: Ref<"error", "error">;
88
- failureCount: Ref<number, number>;
89
- failureReason: Ref<Error | null, Error | null>;
90
- isPaused: Ref<boolean, boolean>;
91
- variables: import("@vue/shared").IfAny<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, Ref<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>>, [GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>] extends [Ref<any, any>] ? Ref<any, any> & (GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>) : Ref<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>>>;
92
- isIdle: Ref<false, false>;
93
- submittedAt: Ref<number, number>;
94
- mutate: (variables: GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown> | undefined) => void;
95
- mutateAsync: import("@tanstack/query-core").MutateFunction<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown>;
96
- reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
97
- } | {
98
- data: ComputedRef<AxiosResponse<GetResponseData<Ops, Op>> | undefined>;
99
- isEnabled: ComputedRef<boolean>;
100
- extraPathParams: Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
101
- pathParams: ComputedRef<GetPathParameters<Ops, Op>>;
102
- context: Ref<unknown, unknown>;
103
- error: Ref<null, null>;
104
- isError: Ref<false, false>;
105
- isPending: Ref<false, false>;
106
- isSuccess: Ref<true, true>;
107
- status: Ref<"success", "success">;
108
- failureCount: Ref<number, number>;
109
- failureReason: Ref<Error | null, Error | null>;
110
- isPaused: Ref<boolean, boolean>;
111
- variables: import("@vue/shared").IfAny<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, Ref<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>>, [GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>] extends [Ref<any, any>] ? Ref<any, any> & (GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>) : Ref<GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>>>;
112
- isIdle: Ref<false, false>;
113
- submittedAt: Ref<number, number>;
114
- mutate: (variables: GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown> | undefined) => void;
115
- mutateAsync: import("@tanstack/query-core").MutateFunction<AxiosResponse<any, any, {}>, Error, GetRequestBody<Ops, Op> extends never ? void | QMutationVars<Ops, Op> : QMutationVars<Ops, Op>, unknown>;
116
- reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
117
- };
85
+ pathParams?: MaybeRefOrGetter<ApiPathParamsInput<Ops, Op> | null | undefined>, options?: QMutationOptions<Ops, Op>): EndpointMutationReturn<Ops, Op>;
118
86
  //# sourceMappingURL=openapi-mutation.d.ts.map
@@ -1 +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,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAErB,UAAU,EACV,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,EACnF,WAAW,EAAE,EAAE,EACf,CAAC,EAAE,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,UAAU;AACtC,mBAAmB,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,EACjH,aAAa,CAAC,EAAE,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC;UA2LhB,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;UAAhE,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;UAAhE,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;UAAhE,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;EAK1F"}
1
+ {"version":3,"file":"openapi-mutation.d.ts","sourceRoot":"","sources":["../src/openapi-mutation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,WAAW,EAAE,KAAK,GAAG,EAAE,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAGtF,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EAErB,KAAK,QAAQ,EACb,KAAK,aAAa,EAElB,UAAU,EACX,MAAM,SAAS,CAAA;AAQhB,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,sBAAsB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG;IACvF,+DAA+D;IAC/D,IAAI,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IAElE,wCAAwC;IACxC,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IAExB,8CAA8C;IAC9C,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAEvB,wCAAwC;IACxC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAEvB,qCAAqC;IACrC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAErB,2CAA2C;IAC3C,MAAM,EAAE,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAEzB,sDAAsD;IACtD,WAAW,EAAE,aAAa,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAEnC,gCAAgC;IAChC,KAAK,EAAE,MAAM,IAAI,CAAA;IAEjB,mEAAmE;IACnE,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IAE/B,oCAAoC;IACpC,UAAU,EAAE,WAAW,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;IAE/C,wEAAwE;IACxE,eAAe,EAAE,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;CAC7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,EACnF,WAAW,EAAE,EAAE,EACf,CAAC,EAAE,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,UAAU;AACtC,UAAU,CAAC,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAC7E,OAAO,CAAC,EAAE,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,GA4LnB,sBAAsB,CAAC,GAAG,EAAE,EAAE,CAAC,CAChD"}
@@ -1,72 +1,59 @@
1
- import { computed, ref, toValue } from 'vue';
1
+ import { computed, ref } from 'vue';
2
2
  import { useMutation } from '@tanstack/vue-query';
3
3
  import { HttpMethod, } from './types.js';
4
- import { resolvePath, generateQueryKey, isPathResolved, getParamsOptionsFrom } from './openapi-utils.js';
4
+ import { isPathResolved, normalizeParamsOptions, useResolvedOperation, resolvePath, generateQueryKey, } from './openapi-utils.js';
5
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.
6
+ * Execute a type-safe mutation (POST/PUT/PATCH/DELETE) with automatic cache updates.
9
7
  *
10
- * NOTE: By default, the mutation will automatically update cache with returned data and reload
11
- * any matching GET queries for the same path.
8
+ * Ensures the operation is a mutation at runtime and returns a reactive mutation object
9
+ * with helpers for path resolution and cache invalidation.
12
10
  *
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.
11
+ * NOTE: By default, the mutation updates cache for PUT/PATCH and invalidates matching
12
+ * GET queries for the same path.
13
+ *
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
24
28
  * @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
- * - `axiosOptions`: Optional axios configuration overrides for this specific mutation call.
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.
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
34
34
  */
35
35
  export function useEndpointMutation(operationId, h, // helpers
36
- pathParamsOrOptions, optionsOrNull) {
36
+ pathParams, options) {
37
37
  // Runtime check to ensure this is actually a mutation operation
38
38
  if (!h.isMutationOperation(operationId)) {
39
- throw new Error(`Operation ${String(operationId)} is not a mutation operation (POST/PUT/PATCH/DELETE)`);
39
+ const { method } = h.getOperationInfo(operationId);
40
+ throw new Error(`Operation '${String(operationId)}' uses method ${method} and cannot be used with useMutation(). ` +
41
+ `Use useQuery() for GET/HEAD/OPTIONS operations.`);
40
42
  }
41
43
  const { path, method } = h.getOperationInfo(operationId);
42
- const { pathParams, options } = getParamsOptionsFrom(path, pathParamsOrOptions, optionsOrNull);
43
- const { axiosOptions, dontInvalidate, dontUpdateCache, invalidateOperations, refetchEndpoints, queryParams, ...useMutationOptions } = options;
44
+ const { pathParams: resolvedPathParamsInput, options: resolvedOptions } = normalizeParamsOptions(pathParams, options);
45
+ const { axiosOptions, dontInvalidate, dontUpdateCache, invalidateOperations, refetchEndpoints, queryParams, ...useMutationOptions } = resolvedOptions;
44
46
  const extraPathParams = ref({});
45
- // Compute the resolved path - same pattern as query
46
- // This ensures that when pathParams is a function, it gets called within the computed
47
- // so Vue can track dependencies of variables referenced inside the function
48
- const basePathParams = computed(() => {
49
- const result = toValue(pathParams);
50
- return result;
51
- });
52
- const allPathParams = computed(() => ({
53
- ...basePathParams.value,
54
- ...extraPathParams.value,
55
- }));
56
- const resolvedPath = computed(() => resolvePath(path, allPathParams.value));
57
- const queryKey = computed(() => generateQueryKey(resolvedPath.value));
58
- // Make query parameters reactive
59
- const allQueryParams = computed(() => {
60
- const result = toValue(queryParams);
61
- return result;
62
- });
47
+ // Use the consolidated operation resolver with extraPathParams support
48
+ const { resolvedPath, queryKey, queryParams: resolvedQueryParams, pathParams: allPathParams, } = useResolvedOperation(path, resolvedPathParamsInput, queryParams, extraPathParams);
63
49
  const mutation = useMutation({
64
50
  mutationFn: async (vars) => {
65
51
  const { data, pathParams: pathParamsFromMutate, axiosOptions: axiosOptionsFromMutate, queryParams: queryParamsFromMutate, } = vars;
66
52
  extraPathParams.value = pathParamsFromMutate || {};
67
53
  // TODO: use typing to ensure all required path params are provided
68
54
  if (!isPathResolved(resolvedPath.value)) {
69
- return Promise.reject(new Error(`Mutation for '${String(operationId)}' cannot be used, as path is not resolved: ${resolvedPath.value} (params: ${JSON.stringify(allPathParams.value)})`));
55
+ return Promise.reject(new Error(`Cannot execute mutation '${String(operationId)}': path parameters not resolved. ` +
56
+ `Path: '${resolvedPath.value}', provided params: ${JSON.stringify(allPathParams.value)}`));
70
57
  }
71
58
  // Cancel any ongoing queries for this path (prevent race conditions with refresh)
72
59
  await h.queryClient.cancelQueries({ queryKey: queryKey.value, exact: false });
@@ -78,7 +65,7 @@ pathParamsOrOptions, optionsOrNull) {
78
65
  ...axiosOptionsFromMutate,
79
66
  params: {
80
67
  ...(axiosOptions?.params || {}),
81
- ...(allQueryParams.value || {}),
68
+ ...(resolvedQueryParams.value || {}),
82
69
  ...(queryParamsFromMutate || {}),
83
70
  },
84
71
  });
@@ -100,7 +87,7 @@ pathParamsOrOptions, optionsOrNull) {
100
87
  await h.queryClient.invalidateQueries({ queryKey: queryKey.value, exact: method !== HttpMethod.POST });
101
88
  const listPath = h.getListOperationPath(operationId);
102
89
  if (listPath) {
103
- const listResolvedPath = resolvePath(listPath, pathParams);
90
+ const listResolvedPath = resolvePath(listPath, resolvedPathParamsInput);
104
91
  if (isPathResolved(listResolvedPath)) {
105
92
  const listQueryKey = generateQueryKey(listResolvedPath);
106
93
  // Invalidate list queries by comparing normalized query keys.