nuxt-openapi-hyperfetch 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +26 -0
- package/.prettierignore +17 -0
- package/.prettierrc.json +12 -0
- package/CONTRIBUTING.md +292 -0
- package/INSTRUCTIONS.md +327 -0
- package/LICENSE +202 -0
- package/README.md +202 -0
- package/dist/cli/config.d.ts +57 -0
- package/dist/cli/config.js +85 -0
- package/dist/cli/logger.d.ts +44 -0
- package/dist/cli/logger.js +58 -0
- package/dist/cli/logo.d.ts +6 -0
- package/dist/cli/logo.js +21 -0
- package/dist/cli/messages.d.ts +65 -0
- package/dist/cli/messages.js +86 -0
- package/dist/cli/prompts.d.ts +30 -0
- package/dist/cli/prompts.js +118 -0
- package/dist/cli/types.d.ts +43 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli/utils.d.ts +26 -0
- package/dist/cli/utils.js +45 -0
- package/dist/generate.d.ts +6 -0
- package/dist/generate.js +48 -0
- package/dist/generators/nuxt-server/bff-templates.d.ts +25 -0
- package/dist/generators/nuxt-server/bff-templates.js +737 -0
- package/dist/generators/nuxt-server/generator.d.ts +7 -0
- package/dist/generators/nuxt-server/generator.js +206 -0
- package/dist/generators/nuxt-server/parser.d.ts +5 -0
- package/dist/generators/nuxt-server/parser.js +5 -0
- package/dist/generators/nuxt-server/templates.d.ts +35 -0
- package/dist/generators/nuxt-server/templates.js +412 -0
- package/dist/generators/nuxt-server/types.d.ts +5 -0
- package/dist/generators/nuxt-server/types.js +5 -0
- package/dist/generators/shared/parsers/heyapi-parser.d.ts +11 -0
- package/dist/generators/shared/parsers/heyapi-parser.js +248 -0
- package/dist/generators/shared/parsers/official-parser.d.ts +5 -0
- package/dist/generators/shared/parsers/official-parser.js +5 -0
- package/dist/generators/shared/runtime/apiHelpers.d.ts +183 -0
- package/dist/generators/shared/runtime/apiHelpers.js +268 -0
- package/dist/generators/shared/templates/api-callbacks-plugin.d.ts +178 -0
- package/dist/generators/shared/templates/api-callbacks-plugin.js +338 -0
- package/dist/generators/shared/types.d.ts +25 -0
- package/dist/generators/shared/types.js +4 -0
- package/dist/generators/tanstack-query/generator.d.ts +5 -0
- package/dist/generators/tanstack-query/generator.js +11 -0
- package/dist/generators/use-async-data/generator.d.ts +5 -0
- package/dist/generators/use-async-data/generator.js +156 -0
- package/dist/generators/use-async-data/parser.d.ts +5 -0
- package/dist/generators/use-async-data/parser.js +5 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncData.d.ts +38 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncData.js +122 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.d.ts +54 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +126 -0
- package/dist/generators/use-async-data/templates.d.ts +20 -0
- package/dist/generators/use-async-data/templates.js +191 -0
- package/dist/generators/use-async-data/types.d.ts +4 -0
- package/dist/generators/use-async-data/types.js +4 -0
- package/dist/generators/use-fetch/generator.d.ts +5 -0
- package/dist/generators/use-fetch/generator.js +131 -0
- package/dist/generators/use-fetch/parser.d.ts +9 -0
- package/dist/generators/use-fetch/parser.js +282 -0
- package/dist/generators/use-fetch/runtime/useApiRequest.d.ts +46 -0
- package/dist/generators/use-fetch/runtime/useApiRequest.js +158 -0
- package/dist/generators/use-fetch/templates.d.ts +16 -0
- package/dist/generators/use-fetch/templates.js +169 -0
- package/dist/generators/use-fetch/types.d.ts +5 -0
- package/dist/generators/use-fetch/types.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +213 -0
- package/docs/API-REFERENCE.md +887 -0
- package/docs/ARCHITECTURE.md +649 -0
- package/docs/DEVELOPMENT.md +918 -0
- package/docs/QUICK-START.md +323 -0
- package/docs/README.md +155 -0
- package/docs/TROUBLESHOOTING.md +881 -0
- package/eslint.config.js +72 -0
- package/package.json +65 -0
- package/src/cli/config.ts +140 -0
- package/src/cli/logger.ts +66 -0
- package/src/cli/logo.ts +25 -0
- package/src/cli/messages.ts +97 -0
- package/src/cli/prompts.ts +143 -0
- package/src/cli/types.ts +50 -0
- package/src/cli/utils.ts +49 -0
- package/src/generate.ts +57 -0
- package/src/generators/nuxt-server/bff-templates.ts +754 -0
- package/src/generators/nuxt-server/generator.ts +270 -0
- package/src/generators/nuxt-server/parser.ts +5 -0
- package/src/generators/nuxt-server/templates.ts +483 -0
- package/src/generators/nuxt-server/types.ts +5 -0
- package/src/generators/shared/parsers/heyapi-parser.ts +307 -0
- package/src/generators/shared/parsers/official-parser.ts +5 -0
- package/src/generators/shared/runtime/apiHelpers.ts +466 -0
- package/src/generators/shared/templates/api-callbacks-plugin.ts +352 -0
- package/src/generators/shared/types.ts +27 -0
- package/src/generators/tanstack-query/generator.ts +11 -0
- package/src/generators/use-async-data/generator.ts +204 -0
- package/src/generators/use-async-data/parser.ts +5 -0
- package/src/generators/use-async-data/runtime/useApiAsyncData.ts +220 -0
- package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +236 -0
- package/src/generators/use-async-data/templates.ts +250 -0
- package/src/generators/use-async-data/types.ts +4 -0
- package/src/generators/use-fetch/generator.ts +169 -0
- package/src/generators/use-fetch/parser.ts +341 -0
- package/src/generators/use-fetch/runtime/useApiRequest.ts +223 -0
- package/src/generators/use-fetch/templates.ts +214 -0
- package/src/generators/use-fetch/types.ts +5 -0
- package/src/index.ts +265 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Nuxt Runtime Helper - This file is copied to the generated output
|
|
4
|
+
* It requires Nuxt 3 to be installed in the target project
|
|
5
|
+
*/
|
|
6
|
+
import { watch } from 'vue';
|
|
7
|
+
import {
|
|
8
|
+
getGlobalHeaders,
|
|
9
|
+
applyPick,
|
|
10
|
+
applyRequestModifications,
|
|
11
|
+
mergeCallbacks,
|
|
12
|
+
type RequestContext,
|
|
13
|
+
type ModifiedRequestContext,
|
|
14
|
+
type FinishContext,
|
|
15
|
+
type ApiRequestOptions as BaseApiRequestOptions,
|
|
16
|
+
} from '../../shared/runtime/apiHelpers.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Helper type to infer transformed data type
|
|
20
|
+
* If transform is provided, infer its return type
|
|
21
|
+
* If pick is provided, return partial object (type inference for nested paths is complex)
|
|
22
|
+
* Otherwise, return original type
|
|
23
|
+
*/
|
|
24
|
+
type MaybeTransformed<T, Options> = Options extends { transform: (...args: any) => infer R }
|
|
25
|
+
? R
|
|
26
|
+
: Options extends { pick: ReadonlyArray<any> }
|
|
27
|
+
? any // With nested paths, type inference is complex, so we use any
|
|
28
|
+
: T;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extended options specific to useAsyncData
|
|
32
|
+
*/
|
|
33
|
+
export interface ApiAsyncDataOptions<T> extends BaseApiRequestOptions<T> {
|
|
34
|
+
/**
|
|
35
|
+
* Whether to fetch data immediately on mount (default: true)
|
|
36
|
+
*/
|
|
37
|
+
immediate?: boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Lazy mode: don't block navigation (default: false)
|
|
41
|
+
*/
|
|
42
|
+
lazy?: boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Server-side rendering mode (default: true)
|
|
46
|
+
*/
|
|
47
|
+
server?: boolean;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Deduplicate requests with the same key (default: 'cancel')
|
|
51
|
+
*/
|
|
52
|
+
dedupe?: 'cancel' | 'defer';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Disable automatic refresh when reactive params change.
|
|
56
|
+
* Set to false to prevent re-fetching when params/url refs update.
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
watch?: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generic wrapper for API calls using Nuxt's useAsyncData
|
|
64
|
+
* Supports:
|
|
65
|
+
* - Lifecycle callbacks (onRequest, onSuccess, onError, onFinish)
|
|
66
|
+
* - Request modification via onRequest return value
|
|
67
|
+
* - Transform and pick operations
|
|
68
|
+
* - Global headers from useApiHeaders or $getApiHeaders
|
|
69
|
+
* - Watch pattern for reactive parameters
|
|
70
|
+
*/
|
|
71
|
+
export function useApiAsyncData<T>(
|
|
72
|
+
key: string,
|
|
73
|
+
url: string | (() => string),
|
|
74
|
+
options?: ApiAsyncDataOptions<T>
|
|
75
|
+
) {
|
|
76
|
+
const {
|
|
77
|
+
method = 'GET',
|
|
78
|
+
body,
|
|
79
|
+
headers = {},
|
|
80
|
+
params,
|
|
81
|
+
transform,
|
|
82
|
+
pick,
|
|
83
|
+
onRequest,
|
|
84
|
+
onSuccess,
|
|
85
|
+
onError,
|
|
86
|
+
onFinish,
|
|
87
|
+
skipGlobalCallbacks,
|
|
88
|
+
immediate = true,
|
|
89
|
+
lazy = false,
|
|
90
|
+
server = true,
|
|
91
|
+
dedupe = 'cancel',
|
|
92
|
+
watch: watchOption = true,
|
|
93
|
+
...restOptions
|
|
94
|
+
} = options || {};
|
|
95
|
+
|
|
96
|
+
// Create reactive watch sources — use refs/computeds directly so Vue can track them
|
|
97
|
+
// watchOption: false disables auto-refresh entirely
|
|
98
|
+
const watchSources =
|
|
99
|
+
watchOption === false
|
|
100
|
+
? []
|
|
101
|
+
: [
|
|
102
|
+
...(typeof url === 'function' ? [url] : []),
|
|
103
|
+
...(body ? (isRef(body) ? [body] : typeof body === 'object' ? [() => body] : []) : []),
|
|
104
|
+
...(params
|
|
105
|
+
? isRef(params)
|
|
106
|
+
? [params]
|
|
107
|
+
: typeof params === 'object'
|
|
108
|
+
? [() => params]
|
|
109
|
+
: []
|
|
110
|
+
: []),
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
// Fetch function for useAsyncData
|
|
114
|
+
const fetchFn = async () => {
|
|
115
|
+
// Get URL value for merging callbacks
|
|
116
|
+
const finalUrl = typeof url === 'function' ? url() : url;
|
|
117
|
+
|
|
118
|
+
// Merge local and global callbacks
|
|
119
|
+
const mergedCallbacks = mergeCallbacks(
|
|
120
|
+
finalUrl,
|
|
121
|
+
{ onRequest, onSuccess, onError, onFinish },
|
|
122
|
+
skipGlobalCallbacks
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
// Get global headers
|
|
127
|
+
const globalHeaders = getGlobalHeaders();
|
|
128
|
+
|
|
129
|
+
// Prepare request context
|
|
130
|
+
const requestContext: RequestContext = {
|
|
131
|
+
url: finalUrl,
|
|
132
|
+
method: method as any,
|
|
133
|
+
headers: { ...globalHeaders, ...headers },
|
|
134
|
+
body,
|
|
135
|
+
params,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Execute merged onRequest callback and potentially modify request
|
|
139
|
+
const modifiedContext = { ...requestContext };
|
|
140
|
+
if (mergedCallbacks.onRequest) {
|
|
141
|
+
const result = await mergedCallbacks.onRequest(requestContext);
|
|
142
|
+
// If onRequest returns modifications, apply them
|
|
143
|
+
if (result && typeof result === 'object') {
|
|
144
|
+
const modifications = result as ModifiedRequestContext;
|
|
145
|
+
if (modifications.body !== undefined) {
|
|
146
|
+
modifiedContext.body = modifications.body;
|
|
147
|
+
}
|
|
148
|
+
if (modifications.headers !== undefined) {
|
|
149
|
+
modifiedContext.headers = {
|
|
150
|
+
...modifiedContext.headers,
|
|
151
|
+
...modifications.headers,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (modifications.params !== undefined) {
|
|
155
|
+
modifiedContext.params = {
|
|
156
|
+
...modifiedContext.params,
|
|
157
|
+
...modifications.params,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Make the request with $fetch — toValue() unrefs any Ref/ComputedRef
|
|
164
|
+
let data = await $fetch<T>(modifiedContext.url, {
|
|
165
|
+
method: modifiedContext.method,
|
|
166
|
+
headers: modifiedContext.headers,
|
|
167
|
+
body: toValue(modifiedContext.body),
|
|
168
|
+
params: toValue(modifiedContext.params),
|
|
169
|
+
...restOptions,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Apply pick if provided
|
|
173
|
+
if (pick) {
|
|
174
|
+
data = applyPick(data, pick) as T;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Apply transform if provided
|
|
178
|
+
if (transform) {
|
|
179
|
+
data = transform(data);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Call merged onSuccess callback
|
|
183
|
+
if (mergedCallbacks.onSuccess) {
|
|
184
|
+
await mergedCallbacks.onSuccess(data, {
|
|
185
|
+
url: finalUrl,
|
|
186
|
+
method,
|
|
187
|
+
headers: modifiedContext.headers,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return data;
|
|
192
|
+
} catch (error: any) {
|
|
193
|
+
// Call merged onError callback
|
|
194
|
+
if (mergedCallbacks.onError) {
|
|
195
|
+
await mergedCallbacks.onError(error, { url: finalUrl, method, headers });
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
} finally {
|
|
199
|
+
// Call merged onFinish callback
|
|
200
|
+
if (mergedCallbacks.onFinish) {
|
|
201
|
+
await mergedCallbacks.onFinish({
|
|
202
|
+
url: finalUrl,
|
|
203
|
+
method,
|
|
204
|
+
headers: { ...getGlobalHeaders(), ...headers },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Use Nuxt's useAsyncData
|
|
211
|
+
const result = useAsyncData<MaybeTransformed<T, ApiAsyncDataOptions<T>>>(key, fetchFn, {
|
|
212
|
+
immediate,
|
|
213
|
+
lazy,
|
|
214
|
+
server,
|
|
215
|
+
dedupe,
|
|
216
|
+
watch: watchSources.length > 0 ? watchSources : undefined,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Nuxt Runtime Helper - This file is copied to the generated output
|
|
4
|
+
* It requires Nuxt 3 to be installed in the target project
|
|
5
|
+
*
|
|
6
|
+
* RAW VERSION: Returns full response including headers, status, and statusText
|
|
7
|
+
*/
|
|
8
|
+
import { watch } from 'vue';
|
|
9
|
+
import {
|
|
10
|
+
getGlobalHeaders,
|
|
11
|
+
applyPick,
|
|
12
|
+
applyRequestModifications,
|
|
13
|
+
mergeCallbacks,
|
|
14
|
+
type RequestContext,
|
|
15
|
+
type ModifiedRequestContext,
|
|
16
|
+
type FinishContext,
|
|
17
|
+
type ApiRequestOptions as BaseApiRequestOptions,
|
|
18
|
+
} from '../../shared/runtime/apiHelpers.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Response structure for Raw version
|
|
22
|
+
* Includes data, headers, status, and statusText
|
|
23
|
+
*/
|
|
24
|
+
export interface RawResponse<T> {
|
|
25
|
+
data: T;
|
|
26
|
+
headers: Headers;
|
|
27
|
+
status: number;
|
|
28
|
+
statusText: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Helper type to infer transformed data type for Raw responses
|
|
33
|
+
* Transform only applies to the data property, not the entire response
|
|
34
|
+
*/
|
|
35
|
+
type MaybeTransformedRaw<T, Options> = Options extends { transform: (...args: any) => infer R }
|
|
36
|
+
? RawResponse<R>
|
|
37
|
+
: Options extends { pick: ReadonlyArray<any> }
|
|
38
|
+
? RawResponse<any> // With nested paths, type inference is complex
|
|
39
|
+
: RawResponse<T>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extended options specific to useAsyncData Raw version
|
|
43
|
+
*/
|
|
44
|
+
export interface ApiAsyncDataRawOptions<T> extends Omit<BaseApiRequestOptions<T>, 'onSuccess'> {
|
|
45
|
+
/**
|
|
46
|
+
* Success callback that receives both data and full response
|
|
47
|
+
*/
|
|
48
|
+
onSuccess?: (
|
|
49
|
+
data: T,
|
|
50
|
+
response: { headers: Headers; status: number; statusText: string; url: string }
|
|
51
|
+
) => void | Promise<void>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether to fetch data immediately on mount (default: true)
|
|
55
|
+
*/
|
|
56
|
+
immediate?: boolean;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Lazy mode: don't block navigation (default: false)
|
|
60
|
+
*/
|
|
61
|
+
lazy?: boolean;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Server-side rendering mode (default: true)
|
|
65
|
+
*/
|
|
66
|
+
server?: boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Deduplicate requests with the same key (default: 'cancel')
|
|
70
|
+
*/
|
|
71
|
+
dedupe?: 'cancel' | 'defer';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generic wrapper for API calls using Nuxt's useAsyncData - RAW VERSION
|
|
76
|
+
* Returns full response with headers and status information
|
|
77
|
+
*
|
|
78
|
+
* Supports:
|
|
79
|
+
* - Lifecycle callbacks (onRequest, onSuccess with response, onError, onFinish)
|
|
80
|
+
* - Request modification via onRequest return value
|
|
81
|
+
* - Transform (applies only to data, not full response)
|
|
82
|
+
* - Pick operations (applies only to data)
|
|
83
|
+
* - Global headers from useApiHeaders or $getApiHeaders
|
|
84
|
+
* - Watch pattern for reactive parameters
|
|
85
|
+
*/
|
|
86
|
+
export function useApiAsyncDataRaw<T>(
|
|
87
|
+
key: string,
|
|
88
|
+
url: string | (() => string),
|
|
89
|
+
options?: ApiAsyncDataRawOptions<T>
|
|
90
|
+
) {
|
|
91
|
+
const {
|
|
92
|
+
method = 'GET',
|
|
93
|
+
body,
|
|
94
|
+
headers = {},
|
|
95
|
+
params,
|
|
96
|
+
transform,
|
|
97
|
+
pick,
|
|
98
|
+
onRequest,
|
|
99
|
+
onSuccess,
|
|
100
|
+
onError,
|
|
101
|
+
onFinish,
|
|
102
|
+
skipGlobalCallbacks,
|
|
103
|
+
immediate = true,
|
|
104
|
+
lazy = false,
|
|
105
|
+
server = true,
|
|
106
|
+
dedupe = 'cancel',
|
|
107
|
+
...restOptions
|
|
108
|
+
} = options || {};
|
|
109
|
+
|
|
110
|
+
// Create reactive watch sources for callbacks
|
|
111
|
+
const watchSources = [
|
|
112
|
+
...(typeof url === 'function' ? [url] : []),
|
|
113
|
+
...(body && typeof body === 'object' ? [() => body] : []),
|
|
114
|
+
...(params && typeof params === 'object' ? [() => params] : []),
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
// Fetch function for useAsyncData
|
|
118
|
+
const fetchFn = async (): Promise<RawResponse<T>> => {
|
|
119
|
+
// Get URL value for merging callbacks
|
|
120
|
+
const finalUrl = typeof url === 'function' ? url() : url;
|
|
121
|
+
|
|
122
|
+
// Merge local and global callbacks
|
|
123
|
+
const mergedCallbacks = mergeCallbacks(
|
|
124
|
+
finalUrl,
|
|
125
|
+
{ onRequest, onSuccess, onError, onFinish },
|
|
126
|
+
skipGlobalCallbacks
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Get global headers
|
|
131
|
+
const globalHeaders = getGlobalHeaders();
|
|
132
|
+
|
|
133
|
+
// Prepare request context
|
|
134
|
+
const requestContext: RequestContext = {
|
|
135
|
+
url: finalUrl,
|
|
136
|
+
method: method as any,
|
|
137
|
+
headers: { ...globalHeaders, ...headers },
|
|
138
|
+
body,
|
|
139
|
+
params,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Execute merged onRequest callback and potentially modify request
|
|
143
|
+
const modifiedContext = { ...requestContext };
|
|
144
|
+
if (mergedCallbacks.onRequest) {
|
|
145
|
+
const result = await mergedCallbacks.onRequest(requestContext);
|
|
146
|
+
// If onRequest returns modifications, apply them
|
|
147
|
+
if (result && typeof result === 'object') {
|
|
148
|
+
const modifications = result as ModifiedRequestContext;
|
|
149
|
+
if (modifications.body !== undefined) {
|
|
150
|
+
modifiedContext.body = modifications.body;
|
|
151
|
+
}
|
|
152
|
+
if (modifications.headers !== undefined) {
|
|
153
|
+
modifiedContext.headers = {
|
|
154
|
+
...modifiedContext.headers,
|
|
155
|
+
...modifications.headers,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (modifications.params !== undefined) {
|
|
159
|
+
modifiedContext.params = {
|
|
160
|
+
...modifiedContext.params,
|
|
161
|
+
...modifications.params,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Make the request with $fetch.raw to get full response
|
|
168
|
+
const response = await $fetch.raw<T>(modifiedContext.url, {
|
|
169
|
+
method: modifiedContext.method,
|
|
170
|
+
headers: modifiedContext.headers,
|
|
171
|
+
body: modifiedContext.body,
|
|
172
|
+
params: modifiedContext.params,
|
|
173
|
+
...restOptions,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Extract data from response
|
|
177
|
+
let data = response._data as T;
|
|
178
|
+
|
|
179
|
+
// Apply pick if provided (only to data)
|
|
180
|
+
if (pick) {
|
|
181
|
+
data = applyPick(data, pick) as T;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Apply transform if provided (only to data)
|
|
185
|
+
if (transform) {
|
|
186
|
+
data = transform(data);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Construct the raw response object
|
|
190
|
+
const rawResponse: RawResponse<T> = {
|
|
191
|
+
data,
|
|
192
|
+
headers: response.headers,
|
|
193
|
+
status: response.status,
|
|
194
|
+
statusText: response.statusText,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Call merged onSuccess callback with data and response context
|
|
198
|
+
if (mergedCallbacks.onSuccess) {
|
|
199
|
+
await mergedCallbacks.onSuccess(data, {
|
|
200
|
+
headers: response.headers,
|
|
201
|
+
status: response.status,
|
|
202
|
+
statusText: response.statusText,
|
|
203
|
+
url: finalUrl,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return rawResponse;
|
|
208
|
+
} catch (error: any) {
|
|
209
|
+
// Call merged onError callback
|
|
210
|
+
if (mergedCallbacks.onError) {
|
|
211
|
+
await mergedCallbacks.onError(error, { url: finalUrl, method, headers });
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
} finally {
|
|
215
|
+
// Call merged onFinish callback
|
|
216
|
+
if (mergedCallbacks.onFinish) {
|
|
217
|
+
await mergedCallbacks.onFinish({
|
|
218
|
+
url: finalUrl,
|
|
219
|
+
method,
|
|
220
|
+
headers: { ...getGlobalHeaders(), ...headers },
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// Use Nuxt's useAsyncData
|
|
227
|
+
const result = useAsyncData<MaybeTransformedRaw<T, ApiAsyncDataRawOptions<T>>>(key, fetchFn, {
|
|
228
|
+
immediate,
|
|
229
|
+
lazy,
|
|
230
|
+
server,
|
|
231
|
+
dedupe,
|
|
232
|
+
watch: watchSources.length > 0 ? watchSources : undefined,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import type { MethodInfo } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate file header with auto-generation warning
|
|
5
|
+
*/
|
|
6
|
+
function generateFileHeader(): string {
|
|
7
|
+
return `/**
|
|
8
|
+
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
9
|
+
*
|
|
10
|
+
* This file was automatically generated by nuxt-openapi-generator.
|
|
11
|
+
* Any manual changes will be overwritten on the next generation.
|
|
12
|
+
*
|
|
13
|
+
* @generated by nuxt-openapi-generator
|
|
14
|
+
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/* eslint-disable */
|
|
18
|
+
// @ts-nocheck
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for code generation
|
|
24
|
+
*/
|
|
25
|
+
export interface GenerateOptions {
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
backend?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generate a useAsyncData composable function
|
|
32
|
+
*/
|
|
33
|
+
export function generateComposableFile(
|
|
34
|
+
method: MethodInfo,
|
|
35
|
+
apiImportPath: string,
|
|
36
|
+
options?: GenerateOptions
|
|
37
|
+
): string {
|
|
38
|
+
const header = generateFileHeader();
|
|
39
|
+
const imports = generateImports(method, apiImportPath, false);
|
|
40
|
+
const functionBody = generateFunctionBody(method, false, options);
|
|
41
|
+
|
|
42
|
+
return `${header}${imports}\n\n${functionBody}\n`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate a useAsyncData composable function (Raw version with headers)
|
|
47
|
+
*/
|
|
48
|
+
export function generateRawComposableFile(
|
|
49
|
+
method: MethodInfo,
|
|
50
|
+
apiImportPath: string,
|
|
51
|
+
options?: GenerateOptions
|
|
52
|
+
): string {
|
|
53
|
+
const header = generateFileHeader();
|
|
54
|
+
const imports = generateImports(method, apiImportPath, true);
|
|
55
|
+
const functionBody = generateFunctionBody(method, true, options);
|
|
56
|
+
|
|
57
|
+
return `${header}${imports}\n\n${functionBody}\n`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extract base type names from a type string
|
|
62
|
+
* Examples:
|
|
63
|
+
* Pet[] -> Pet
|
|
64
|
+
* Array<Pet> -> Pet
|
|
65
|
+
* Pet -> Pet
|
|
66
|
+
* { [key: string]: Pet } -> (empty, it's anonymous)
|
|
67
|
+
*/
|
|
68
|
+
function extractBaseTypes(type: string): string[] {
|
|
69
|
+
if (!type) {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Handle array syntax: Pet[]
|
|
74
|
+
const arrayMatch = type.match(/^(\w+)\[\]$/);
|
|
75
|
+
if (arrayMatch) {
|
|
76
|
+
return [arrayMatch[1]];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handle Array generic: Array<Pet>
|
|
80
|
+
const arrayGenericMatch = type.match(/^Array<(\w+)>$/);
|
|
81
|
+
if (arrayGenericMatch) {
|
|
82
|
+
return [arrayGenericMatch[1]];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If it's a simple named type (single word, PascalCase), include it
|
|
86
|
+
if (/^[A-Z][a-zA-Z0-9]*$/.test(type)) {
|
|
87
|
+
return [type];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// For complex types, don't extract anything
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Generate import statements
|
|
96
|
+
*/
|
|
97
|
+
function generateImports(method: MethodInfo, apiImportPath: string, isRaw: boolean): string {
|
|
98
|
+
const typeNames = new Set<string>();
|
|
99
|
+
|
|
100
|
+
// Extract base types from request type
|
|
101
|
+
if (method.requestType) {
|
|
102
|
+
const extracted = extractBaseTypes(method.requestType);
|
|
103
|
+
extracted.forEach((t) => typeNames.add(t));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Extract base types from response type
|
|
107
|
+
if (method.responseType && method.responseType !== 'void') {
|
|
108
|
+
const extracted = extractBaseTypes(method.responseType);
|
|
109
|
+
extracted.forEach((t) => typeNames.add(t));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let imports = '';
|
|
113
|
+
|
|
114
|
+
// Import types from API (only if we have named types to import)
|
|
115
|
+
if (typeNames.size > 0) {
|
|
116
|
+
imports += `import type { ${Array.from(typeNames).join(', ')} } from '${apiImportPath}';\n`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Import runtime helper (normal or raw)
|
|
120
|
+
if (isRaw) {
|
|
121
|
+
imports += `import { useApiAsyncDataRaw, type ApiAsyncDataRawOptions, type RawResponse } from '../runtime/useApiAsyncDataRaw';`;
|
|
122
|
+
} else {
|
|
123
|
+
imports += `import { useApiAsyncData, type ApiAsyncDataOptions } from '../runtime/useApiAsyncData';`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return imports;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generate the composable function body
|
|
131
|
+
*/
|
|
132
|
+
function generateFunctionBody(
|
|
133
|
+
method: MethodInfo,
|
|
134
|
+
isRaw: boolean,
|
|
135
|
+
generateOptions?: GenerateOptions
|
|
136
|
+
): string {
|
|
137
|
+
const hasParams = !!method.requestType;
|
|
138
|
+
const paramsArg = hasParams ? `params: MaybeRef<${method.requestType}>` : '';
|
|
139
|
+
|
|
140
|
+
// Determine the options type based on isRaw
|
|
141
|
+
const optionsType = isRaw
|
|
142
|
+
? `ApiAsyncDataRawOptions<${method.responseType}>`
|
|
143
|
+
: `ApiAsyncDataOptions<${method.responseType}>`;
|
|
144
|
+
const optionsArg = `options?: ${optionsType}`;
|
|
145
|
+
const args = hasParams ? `${paramsArg}, ${optionsArg}` : optionsArg;
|
|
146
|
+
|
|
147
|
+
// Determine the response type generic
|
|
148
|
+
const responseTypeGeneric = method.responseType !== 'void' ? `<${method.responseType}>` : '';
|
|
149
|
+
|
|
150
|
+
// Generate unique key for useAsyncData
|
|
151
|
+
const composableName =
|
|
152
|
+
isRaw && method.rawMethodName
|
|
153
|
+
? `useAsyncData${method.rawMethodName.replace(/Raw$/, '')}Raw`
|
|
154
|
+
: method.composableName.replace(/^useFetch/, 'useAsyncData');
|
|
155
|
+
|
|
156
|
+
const key = `'${composableName}'`;
|
|
157
|
+
|
|
158
|
+
const url = generateUrl(method);
|
|
159
|
+
const fetchOptions = generateFetchOptions(method, generateOptions);
|
|
160
|
+
|
|
161
|
+
const description = method.description ? `/**\n * ${method.description}\n */\n` : '';
|
|
162
|
+
|
|
163
|
+
// Choose the correct wrapper function
|
|
164
|
+
const wrapperFunction = isRaw ? 'useApiAsyncDataRaw' : 'useApiAsyncData';
|
|
165
|
+
|
|
166
|
+
const pInit = hasParams ? `\n const p = isRef(params) ? params : shallowRef(params)` : '';
|
|
167
|
+
|
|
168
|
+
return `${description}export const ${composableName} = (${args}) => {${pInit}
|
|
169
|
+
return ${wrapperFunction}${responseTypeGeneric}(${key}, ${url}, ${fetchOptions})
|
|
170
|
+
}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate URL (with path params if needed)
|
|
175
|
+
*/
|
|
176
|
+
function generateUrl(method: MethodInfo): string {
|
|
177
|
+
if (method.pathParams.length === 0) {
|
|
178
|
+
return `'${method.path}'`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let url = method.path;
|
|
182
|
+
for (const param of method.pathParams) {
|
|
183
|
+
const accessor = method.paramsShape === 'nested' ? `p.value.path.${param}` : `p.value.${param}`;
|
|
184
|
+
url = url.replace(`{${param}}`, `\${${accessor}}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return `() => \`${url}\``;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Generate fetch options object
|
|
192
|
+
*/
|
|
193
|
+
function generateFetchOptions(method: MethodInfo, generateOptions?: GenerateOptions): string {
|
|
194
|
+
const options: string[] = [];
|
|
195
|
+
|
|
196
|
+
// Method
|
|
197
|
+
options.push(`method: '${method.httpMethod}'`);
|
|
198
|
+
|
|
199
|
+
// Base URL (if provided in config)
|
|
200
|
+
if (generateOptions?.baseUrl) {
|
|
201
|
+
options.push(`baseURL: '${generateOptions.baseUrl}'`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Body
|
|
205
|
+
if (method.hasBody) {
|
|
206
|
+
if (method.paramsShape === 'nested') {
|
|
207
|
+
options.push(`body: computed(() => p.value.body)`);
|
|
208
|
+
} else if (method.bodyField) {
|
|
209
|
+
options.push(`body: computed(() => p.value.${method.bodyField})`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Query params (renamed to 'params' for $fetch)
|
|
214
|
+
if (method.hasQueryParams) {
|
|
215
|
+
if (method.paramsShape === 'nested') {
|
|
216
|
+
options.push(`params: computed(() => p.value.query)`);
|
|
217
|
+
} else if (method.queryParams.length > 0) {
|
|
218
|
+
const queryObj = method.queryParams
|
|
219
|
+
.map((param) => `${param}: p.value.${param}`)
|
|
220
|
+
.join(',\n ');
|
|
221
|
+
options.push(`params: computed(() => ({\n ${queryObj}\n }))`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Headers
|
|
226
|
+
if (Object.keys(method.headers).length > 0) {
|
|
227
|
+
const headersEntries = Object.entries(method.headers)
|
|
228
|
+
.map(([key, value]) => `'${key}': '${value}'`)
|
|
229
|
+
.join(',\n ');
|
|
230
|
+
options.push(`headers: {\n ${headersEntries},\n ...options?.headers\n }`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Spread options
|
|
234
|
+
options.push('...options');
|
|
235
|
+
|
|
236
|
+
const optionsStr = options.join(',\n ');
|
|
237
|
+
return `{\n ${optionsStr}\n }`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Generate index.ts that exports all composables
|
|
242
|
+
*/
|
|
243
|
+
export function generateIndexFile(composableNames: string[]): string {
|
|
244
|
+
const header = generateFileHeader();
|
|
245
|
+
const exports = composableNames
|
|
246
|
+
.map((name) => `export { ${name} } from './composables/${name}'`)
|
|
247
|
+
.join('\n');
|
|
248
|
+
|
|
249
|
+
return `${header}${exports}\n`;
|
|
250
|
+
}
|