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.
Files changed (109) hide show
  1. package/.editorconfig +26 -0
  2. package/.prettierignore +17 -0
  3. package/.prettierrc.json +12 -0
  4. package/CONTRIBUTING.md +292 -0
  5. package/INSTRUCTIONS.md +327 -0
  6. package/LICENSE +202 -0
  7. package/README.md +202 -0
  8. package/dist/cli/config.d.ts +57 -0
  9. package/dist/cli/config.js +85 -0
  10. package/dist/cli/logger.d.ts +44 -0
  11. package/dist/cli/logger.js +58 -0
  12. package/dist/cli/logo.d.ts +6 -0
  13. package/dist/cli/logo.js +21 -0
  14. package/dist/cli/messages.d.ts +65 -0
  15. package/dist/cli/messages.js +86 -0
  16. package/dist/cli/prompts.d.ts +30 -0
  17. package/dist/cli/prompts.js +118 -0
  18. package/dist/cli/types.d.ts +43 -0
  19. package/dist/cli/types.js +4 -0
  20. package/dist/cli/utils.d.ts +26 -0
  21. package/dist/cli/utils.js +45 -0
  22. package/dist/generate.d.ts +6 -0
  23. package/dist/generate.js +48 -0
  24. package/dist/generators/nuxt-server/bff-templates.d.ts +25 -0
  25. package/dist/generators/nuxt-server/bff-templates.js +737 -0
  26. package/dist/generators/nuxt-server/generator.d.ts +7 -0
  27. package/dist/generators/nuxt-server/generator.js +206 -0
  28. package/dist/generators/nuxt-server/parser.d.ts +5 -0
  29. package/dist/generators/nuxt-server/parser.js +5 -0
  30. package/dist/generators/nuxt-server/templates.d.ts +35 -0
  31. package/dist/generators/nuxt-server/templates.js +412 -0
  32. package/dist/generators/nuxt-server/types.d.ts +5 -0
  33. package/dist/generators/nuxt-server/types.js +5 -0
  34. package/dist/generators/shared/parsers/heyapi-parser.d.ts +11 -0
  35. package/dist/generators/shared/parsers/heyapi-parser.js +248 -0
  36. package/dist/generators/shared/parsers/official-parser.d.ts +5 -0
  37. package/dist/generators/shared/parsers/official-parser.js +5 -0
  38. package/dist/generators/shared/runtime/apiHelpers.d.ts +183 -0
  39. package/dist/generators/shared/runtime/apiHelpers.js +268 -0
  40. package/dist/generators/shared/templates/api-callbacks-plugin.d.ts +178 -0
  41. package/dist/generators/shared/templates/api-callbacks-plugin.js +338 -0
  42. package/dist/generators/shared/types.d.ts +25 -0
  43. package/dist/generators/shared/types.js +4 -0
  44. package/dist/generators/tanstack-query/generator.d.ts +5 -0
  45. package/dist/generators/tanstack-query/generator.js +11 -0
  46. package/dist/generators/use-async-data/generator.d.ts +5 -0
  47. package/dist/generators/use-async-data/generator.js +156 -0
  48. package/dist/generators/use-async-data/parser.d.ts +5 -0
  49. package/dist/generators/use-async-data/parser.js +5 -0
  50. package/dist/generators/use-async-data/runtime/useApiAsyncData.d.ts +38 -0
  51. package/dist/generators/use-async-data/runtime/useApiAsyncData.js +122 -0
  52. package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.d.ts +54 -0
  53. package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +126 -0
  54. package/dist/generators/use-async-data/templates.d.ts +20 -0
  55. package/dist/generators/use-async-data/templates.js +191 -0
  56. package/dist/generators/use-async-data/types.d.ts +4 -0
  57. package/dist/generators/use-async-data/types.js +4 -0
  58. package/dist/generators/use-fetch/generator.d.ts +5 -0
  59. package/dist/generators/use-fetch/generator.js +131 -0
  60. package/dist/generators/use-fetch/parser.d.ts +9 -0
  61. package/dist/generators/use-fetch/parser.js +282 -0
  62. package/dist/generators/use-fetch/runtime/useApiRequest.d.ts +46 -0
  63. package/dist/generators/use-fetch/runtime/useApiRequest.js +158 -0
  64. package/dist/generators/use-fetch/templates.d.ts +16 -0
  65. package/dist/generators/use-fetch/templates.js +169 -0
  66. package/dist/generators/use-fetch/types.d.ts +5 -0
  67. package/dist/generators/use-fetch/types.js +5 -0
  68. package/dist/index.d.ts +2 -0
  69. package/dist/index.js +213 -0
  70. package/docs/API-REFERENCE.md +887 -0
  71. package/docs/ARCHITECTURE.md +649 -0
  72. package/docs/DEVELOPMENT.md +918 -0
  73. package/docs/QUICK-START.md +323 -0
  74. package/docs/README.md +155 -0
  75. package/docs/TROUBLESHOOTING.md +881 -0
  76. package/eslint.config.js +72 -0
  77. package/package.json +65 -0
  78. package/src/cli/config.ts +140 -0
  79. package/src/cli/logger.ts +66 -0
  80. package/src/cli/logo.ts +25 -0
  81. package/src/cli/messages.ts +97 -0
  82. package/src/cli/prompts.ts +143 -0
  83. package/src/cli/types.ts +50 -0
  84. package/src/cli/utils.ts +49 -0
  85. package/src/generate.ts +57 -0
  86. package/src/generators/nuxt-server/bff-templates.ts +754 -0
  87. package/src/generators/nuxt-server/generator.ts +270 -0
  88. package/src/generators/nuxt-server/parser.ts +5 -0
  89. package/src/generators/nuxt-server/templates.ts +483 -0
  90. package/src/generators/nuxt-server/types.ts +5 -0
  91. package/src/generators/shared/parsers/heyapi-parser.ts +307 -0
  92. package/src/generators/shared/parsers/official-parser.ts +5 -0
  93. package/src/generators/shared/runtime/apiHelpers.ts +466 -0
  94. package/src/generators/shared/templates/api-callbacks-plugin.ts +352 -0
  95. package/src/generators/shared/types.ts +27 -0
  96. package/src/generators/tanstack-query/generator.ts +11 -0
  97. package/src/generators/use-async-data/generator.ts +204 -0
  98. package/src/generators/use-async-data/parser.ts +5 -0
  99. package/src/generators/use-async-data/runtime/useApiAsyncData.ts +220 -0
  100. package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +236 -0
  101. package/src/generators/use-async-data/templates.ts +250 -0
  102. package/src/generators/use-async-data/types.ts +4 -0
  103. package/src/generators/use-fetch/generator.ts +169 -0
  104. package/src/generators/use-fetch/parser.ts +341 -0
  105. package/src/generators/use-fetch/runtime/useApiRequest.ts +223 -0
  106. package/src/generators/use-fetch/templates.ts +214 -0
  107. package/src/generators/use-fetch/types.ts +5 -0
  108. package/src/index.ts +265 -0
  109. package/tsconfig.json +15 -0
@@ -0,0 +1,887 @@
1
+ # 📖 API Reference - Complete Technical Reference
2
+
3
+ > **Purpose**: Complete technical documentation of all interfaces, functions, and configuration options.
4
+
5
+ ## Table of Contents
6
+
7
+ - [CLI Commands](#cli-commands)
8
+ - [Configuration](#configuration)
9
+ - [Interfaces & Types](#interfaces--types)
10
+ - [Parser API](#parser-api)
11
+ - [Template API](#template-api)
12
+ - [Runtime APIs](#runtime-apis)
13
+ - [Callback Interfaces](#callback-interfaces)
14
+ - [Generated Composables](#generated-composables)
15
+
16
+ ## CLI Commands
17
+
18
+ ### `generate`
19
+
20
+ Generate Nuxt composables from OpenAPI/Swagger specifications.
21
+
22
+ ```bash
23
+ nxh generate [options]
24
+ ```
25
+
26
+ **Options:**
27
+
28
+ | Option | Alias | Type | Description |
29
+ | ----------------------- | ----- | -------- | -------------------------- |
30
+ | `--input <file>` | `-i` | `string` | OpenAPI/Swagger file path |
31
+ | `--output <directory>` | `-o` | `string` | Output directory |
32
+ | `--composables <types>` | `-c` | `string` | Comma-separated types list |
33
+
34
+ **Composable Types:**
35
+
36
+ - `useFetch` - Nuxt 3 useFetch composables
37
+ - `useAsyncData` - Nuxt 3 useAsyncData composables (normal + raw)
38
+ - `@tanstack/vue-query` - TanStack Query composables (TODO)
39
+ - `nuxt-server` - Nuxt server routes with BFF pattern
40
+
41
+ **Interactive Mode:**
42
+
43
+ ```bash
44
+ nxh generate
45
+ # Prompts for input file, output directory, and composable types
46
+ ```
47
+
48
+ **Non-Interactive Mode:**
49
+
50
+ ```bash
51
+ nxh generate -i swagger.yaml -o ./swagger
52
+ # Still prompts for composable types
53
+ ```
54
+
55
+ **Fully Automated:**
56
+
57
+ ```bash
58
+ echo "useFetch" | nxh generate -i swagger.yaml -o ./swagger
59
+ ```
60
+
61
+ **Exit Codes:**
62
+
63
+ - `0` - Success
64
+ - `1` - Error (missing files, parsing errors, etc.)
65
+
66
+ ## Configuration
67
+
68
+ ### OpenAPI Generator Config
69
+
70
+ Located in: `openapitools.json` (auto-created if missing)
71
+
72
+ ```json
73
+ {
74
+ "generator-cli": {
75
+ "version": "7.14.0",
76
+ "generators": {
77
+ "typescript": {
78
+ "generatorName": "typescript-fetch",
79
+ "output": "./swagger"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ **Key Settings:**
87
+
88
+ - `generatorName`: Must be `typescript-fetch` (only supported target)
89
+ - `output`: Where OpenAPI Generator writes TypeScript files
90
+ - `version`: OpenAPI Generator CLI version
91
+
92
+ ### Prettier Config
93
+
94
+ Generated files are formatted with Prettier using these settings:
95
+
96
+ ```typescript
97
+ {
98
+ parser: 'typescript',
99
+ singleQuote: true,
100
+ semi: true,
101
+ trailingComma: 'es5',
102
+ printWidth: 100
103
+ }
104
+ ```
105
+
106
+ ## Interfaces & Types
107
+
108
+ ### MethodInfo
109
+
110
+ **File**: `src/generators/shared/types.ts`
111
+
112
+ **Purpose**: Represents a single API endpoint with all information needed for code generation.
113
+
114
+ ```typescript
115
+ export interface MethodInfo {
116
+ // Basic info
117
+ name: string; // e.g., 'addPet'
118
+ composableName: string; // e.g., 'useFetchAddPet'
119
+ path: string; // e.g., '/pet' or '/pet/{petId}'
120
+ httpMethod: string; // 'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH'
121
+
122
+ // Type information
123
+ requestType?: string; // e.g., 'AddPetRequest'
124
+ responseType: string; // e.g., 'Pet'
125
+
126
+ // Request details
127
+ pathParams: string[]; // e.g., ['petId']
128
+ queryParams: string[]; // e.g., ['status', 'limit']
129
+ headers: Record<string, string>; // Default headers to include
130
+ hasBody: boolean; // true if method uses a request body
131
+ bodyField?: string; // e.g., 'pet' (from requestParameters['pet'])
132
+ hasQueryParams: boolean; // true if has query parameters
133
+
134
+ // Metadata
135
+ description?: string; // JSDoc description from the method
136
+ hasRawMethod: boolean; // true if xxxRaw() method exists
137
+ rawMethodName?: string; // e.g., 'addPetRaw'
138
+ }
139
+ ```
140
+
141
+ **Example:**
142
+
143
+ ```typescript
144
+ {
145
+ name: 'addPet',
146
+ composableName: 'useFetchAddPet',
147
+ path: '/pet',
148
+ httpMethod: 'POST',
149
+ requestType: 'AddPetRequest',
150
+ responseType: 'Pet',
151
+ pathParams: [],
152
+ queryParams: [],
153
+ headers: {},
154
+ hasBody: true,
155
+ bodyField: 'pet',
156
+ hasQueryParams: false,
157
+ hasRawMethod: false
158
+ }
159
+ ```
160
+
161
+ ### ApiClassInfo
162
+
163
+ **File**: `src/generators/shared/types.ts`
164
+
165
+ **Purpose**: Groups all methods from a single API class.
166
+
167
+ ```typescript
168
+ export interface ApiClassInfo {
169
+ className: string; // e.g., 'PetApi'
170
+ methods: MethodInfo[]; // All methods from this API class
171
+ }
172
+ ```
173
+
174
+ ### RequestContext
175
+
176
+ **File**: `src/generators/shared/runtime/apiHelpers.ts`
177
+
178
+ **Purpose**: Context object passed to `onRequest` callback.
179
+
180
+ ```typescript
181
+ export interface RequestContext {
182
+ url: string; // Request URL
183
+ method: string; // HTTP method
184
+ body?: any; // Request body
185
+ headers?: Record<string, string>; // Headers
186
+ query?: Record<string, any>; // Query params
187
+ }
188
+ ```
189
+
190
+ **Usage:**
191
+
192
+ ```typescript
193
+ onRequest: (ctx: RequestContext) => {
194
+ console.log(`Making ${ctx.method} request to ${ctx.url}`);
195
+ };
196
+ ```
197
+
198
+ ### ModifiedRequestContext
199
+
200
+ **File**: `src/generators/shared/runtime/apiHelpers.ts`
201
+
202
+ **Purpose**: Returned by `onRequest` to modify the request.
203
+
204
+ ```typescript
205
+ export interface ModifiedRequestContext {
206
+ headers?: Record<string, string>; // Modified headers
207
+ query?: Record<string, any>; // Modified query params
208
+ body?: any; // Modified body
209
+ }
210
+ ```
211
+
212
+ **Usage:**
213
+
214
+ ```typescript
215
+ onRequest: (ctx) => {
216
+ return {
217
+ headers: {
218
+ ...ctx.headers,
219
+ Authorization: `Bearer ${token}`,
220
+ },
221
+ };
222
+ };
223
+ ```
224
+
225
+ ### SuccessContext
226
+
227
+ **File**: `src/generators/shared/runtime/apiHelpers.ts`
228
+
229
+ **Purpose**: Context passed to `onSuccess` callback. Different for normal vs raw.
230
+
231
+ **Normal Version:**
232
+
233
+ ```typescript
234
+ export interface SuccessContext {
235
+ url: string;
236
+ method: string;
237
+ }
238
+ ```
239
+
240
+ **Raw Version:**
241
+
242
+ ```typescript
243
+ export interface SuccessContext {
244
+ url: string;
245
+ method: string;
246
+ headers: Headers; // Response headers
247
+ status: number; // HTTP status code
248
+ statusText: string; // Status text
249
+ }
250
+ ```
251
+
252
+ **Usage:**
253
+
254
+ ```typescript
255
+ // Normal
256
+ onSuccess: (data: Pet, ctx: SuccessContext) => {
257
+ console.log(`Got pet from ${ctx.url}`);
258
+ };
259
+
260
+ // Raw
261
+ onSuccess: (data: Pet, ctx: SuccessContext) => {
262
+ const token = ctx.headers.get('X-Auth-Token');
263
+ console.log(`Status: ${ctx.status}`);
264
+ };
265
+ ```
266
+
267
+ ### ErrorContext
268
+
269
+ **File**: `src/generators/shared/runtime/apiHelpers.ts`
270
+
271
+ **Purpose**: Context passed to `onError` callback.
272
+
273
+ ```typescript
274
+ export interface ErrorContext {
275
+ url: string;
276
+ method: string;
277
+ status?: number; // HTTP status if available
278
+ message: string; // Error message
279
+ }
280
+ ```
281
+
282
+ ### FinishContext
283
+
284
+ **File**: `src/generators/shared/runtime/apiHelpers.ts`
285
+
286
+ **Purpose**: Context passed to `onFinish` callback.
287
+
288
+ ```typescript
289
+ export interface FinishContext {
290
+ success: boolean; // true if succeeded, false if error
291
+ data?: any; // Data if success
292
+ error?: any; // Error if failed
293
+ duration?: number; // Optional: request duration in ms
294
+ url: string;
295
+ method: string;
296
+ }
297
+ ```
298
+
299
+ ### ApiRequestOptions
300
+
301
+ **File**: `src/generators/shared/runtime/apiHelpers.ts`
302
+
303
+ **Purpose**: Options for all `useApiRequest` calls.
304
+
305
+ ```typescript
306
+ export interface ApiRequestOptions<T> {
307
+ // useFetch native options
308
+ method?: string;
309
+ body?: any;
310
+ headers?: Record<string, string>;
311
+ query?: Record<string, any>;
312
+ baseURL?: string;
313
+ server?: boolean;
314
+ lazy?: boolean;
315
+ immediate?: boolean;
316
+
317
+ // Custom features
318
+ pick?: string[]; // Dot notation field selection
319
+ transform?: (data: T) => any; // Transform response
320
+
321
+ // Lifecycle callbacks
322
+ onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
323
+ onSuccess?: (data: T, ctx: SuccessContext) => void | false;
324
+ onError?: (error: any, ctx: ErrorContext) => void | false;
325
+ onFinish?: (ctx: FinishContext) => void;
326
+
327
+ // Global callbacks control
328
+ skipGlobalCallbacks?: boolean | Array<'onRequest' | 'onSuccess' | 'onError' | 'onFinish'>;
329
+
330
+ // Any other useFetch options
331
+ [key: string]: any;
332
+ }
333
+ ```
334
+
335
+ ### ApiAsyncDataOptions
336
+
337
+ **File**: `src/generators/use-async-data/runtime/useApiAsyncData.ts`
338
+
339
+ **Purpose**: Options for `useAsyncData` composables (normal version).
340
+
341
+ ```typescript
342
+ export interface ApiAsyncDataOptions<T> {
343
+ // useAsyncData native options
344
+ server?: boolean;
345
+ lazy?: boolean;
346
+ immediate?: boolean;
347
+ dedupe?: 'cancel' | 'defer';
348
+
349
+ /**
350
+ * Disable automatic refresh when reactive params change.
351
+ * Set to false to prevent re-fetching when params/url refs update.
352
+ * @default true
353
+ */
354
+ watch?: boolean;
355
+
356
+ // Custom features
357
+ pick?: string[];
358
+ transform?: (data: T) => any;
359
+
360
+ // Lifecycle callbacks
361
+ onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
362
+ onSuccess?: (data: T) => void | false;
363
+ onError?: (error: any) => void | false;
364
+ onFinish?: (ctx: FinishContext) => void;
365
+
366
+ // Global callbacks control
367
+ skipGlobalCallbacks?: boolean | string[];
368
+ }
369
+ ```
370
+
371
+ ### ApiAsyncDataRawOptions
372
+
373
+ **File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
374
+
375
+ **Purpose**: Options for `useAsyncData` raw composables.
376
+
377
+ ```typescript
378
+ export interface ApiAsyncDataRawOptions<T> extends ApiAsyncDataOptions<T> {
379
+ // Same as ApiAsyncDataOptions
380
+ // But onSuccess signature is different:
381
+ // onSuccess?: (data: T, ctx: SuccessContext) => void | false
382
+ // where ctx includes: headers, status, statusText
383
+ }
384
+ ```
385
+
386
+ ### RawResponse
387
+
388
+ **File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
389
+
390
+ **Purpose**: Response structure for raw composables.
391
+
392
+ ```typescript
393
+ export interface RawResponse<T> {
394
+ data: T; // Actual response data
395
+ headers: Headers; // Response headers
396
+ status: number; // HTTP status code
397
+ statusText: string; // Status text
398
+ }
399
+ ```
400
+
401
+ **Usage:**
402
+
403
+ ```typescript
404
+ const { data, pending } = useAsyncDataGetPetRaw({ petId: 123 });
405
+ // data.value = { data: Pet, headers: Headers, status: 200, statusText: 'OK' }
406
+
407
+ const pet = data.value?.data;
408
+ const authToken = data.value?.headers.get('X-Auth-Token');
409
+ const wasCreated = data.value?.status === 201;
410
+ ```
411
+
412
+ ## Parser API
413
+
414
+ ### extractMethodInfo()
415
+
416
+ **File**: `src/generators/use-fetch/parser.ts`
417
+
418
+ **Signature:**
419
+
420
+ ```typescript
421
+ function extractMethodInfo(method: MethodDeclaration, sourceFile: SourceFile): MethodInfo | null;
422
+ ```
423
+
424
+ **Purpose**: Extract API information from a TypeScript method.
425
+
426
+ **Parameters:**
427
+
428
+ - `method`: ts-morph MethodDeclaration node
429
+ - `sourceFile`: ts-morph SourceFile for context
430
+
431
+ **Returns**: `MethodInfo` object or `null` if extraction fails
432
+
433
+ **Algorithm:**
434
+
435
+ 1. Extract method name
436
+ 2. Find `xxxRequestOpts()` method (contains HTTP details)
437
+ 3. Parse return statement to get path, method, body, headers
438
+ 4. Extract parameter types
439
+ 5. Extract return type (response type)
440
+ 6. Generate composable name and file name
441
+
442
+ **Error Handling**: Returns `null` on errors (logged to console)
443
+
444
+ ### getApiFiles()
445
+
446
+ **File**: `src/generators/use-fetch/parser.ts`
447
+
448
+ **Signature:**
449
+
450
+ ```typescript
451
+ function getApiFiles(inputDir: string): string[];
452
+ ```
453
+
454
+ **Purpose**: Find all API files in the `apis/` subdirectory.
455
+
456
+ **Parameters:**
457
+
458
+ - `inputDir`: Path to the root output directory (e.g., `./swagger`). The function appends `/apis` internally.
459
+
460
+ **Returns**: Array of absolute file paths
461
+
462
+ **Filters**: Only includes `*.ts` files that are not `index.ts`
463
+
464
+ ### parseApiFile()
465
+
466
+ **File**: `src/generators/use-fetch/parser.ts`
467
+
468
+ **Signature:**
469
+
470
+ ```typescript
471
+ function parseApiFile(filePath: string): ApiClassInfo;
472
+ ```
473
+
474
+ **Purpose**: Parse a single API file and extract all methods.
475
+
476
+ **Parameters:**
477
+
478
+ - `filePath`: Absolute path to API file
479
+
480
+ **Returns**: `ApiClassInfo` for the first class found in the file
481
+
482
+ **Algorithm:**
483
+
484
+ 1. Create ts-morph Project
485
+ 2. Add source file
486
+ 3. Find all classes
487
+ 4. For each class:
488
+ - Extract all methods
489
+ - Call `extractMethodInfo()` for each
490
+ - Filter out nulls
491
+ 5. Return array of ApiClassInfo
492
+
493
+ ## Template API
494
+
495
+ ### generateFileHeader()
496
+
497
+ **File**: `src/generators/use-fetch/templates.ts`
498
+
499
+ **Signature:**
500
+
501
+ ```typescript
502
+ function generateFileHeader(): string;
503
+ ```
504
+
505
+ **Returns**: TypeScript header comment
506
+
507
+ ```typescript
508
+ // @ts-nocheck - This file runs in user's Nuxt project with different tsconfig
509
+ ```
510
+
511
+ ### generateImports()
512
+
513
+ **File**: `src/generators/use-fetch/templates.ts`
514
+
515
+ **Signature:**
516
+
517
+ ```typescript
518
+ function generateImports(method: MethodInfo, apiImportPath: string): string;
519
+ ```
520
+
521
+ **Purpose**: Generate import statements for composable file.
522
+
523
+ **Returns:**
524
+
525
+ ```typescript
526
+ import type { AddPetRequest, Pet } from '../apis/PetApi';
527
+ import { useApiRequest, type ApiRequestOptions } from '../runtime/useApiRequest';
528
+ ```
529
+
530
+ ### generateFunctionBody()
531
+
532
+ **File**: `src/generators/use-fetch/templates.ts`
533
+
534
+ **Signature:**
535
+
536
+ ```typescript
537
+ function generateFunctionBody(method: MethodInfo): string;
538
+ ```
539
+
540
+ **Purpose**: Generate composable function code.
541
+
542
+ **Returns:**
543
+
544
+ ```typescript
545
+ export const useFetchAddPet = (
546
+ params: MaybeRef<AddPetRequest>,
547
+ options?: ApiRequestOptions<Pet>
548
+ ) => {
549
+ const p = isRef(params) ? params : shallowRef(params);
550
+ return useApiRequest<Pet>('/pet', {
551
+ method: 'POST',
552
+ body: computed(() => p.value.pet),
553
+ ...options,
554
+ });
555
+ };
556
+ ```
557
+
558
+ **Handles:**
559
+
560
+ - Static URLs (`'/pet'`)
561
+ - Reactive URLs with path params via `() => \`/pet/${p.value.petId}\``
562
+ - Query params as `computed(() => ({ ... }))` for reactivity
563
+ - Body field extraction
564
+ - `MaybeRef<T>` params — plain objects are auto-wrapped in `shallowRef`
565
+
566
+ ### generateComposableFile()
567
+
568
+ **File**: `src/generators/use-fetch/templates.ts`
569
+
570
+ **Signature:**
571
+
572
+ ```typescript
573
+ function generateComposableFile(method: MethodInfo, apiImportPath: string): string;
574
+ ```
575
+
576
+ **Purpose**: Generate complete file content for a composable.
577
+
578
+ **Returns**: Full file content (header + imports + function)
579
+
580
+ ## Runtime APIs
581
+
582
+ ### useApiRequest()
583
+
584
+ **File**: `src/generators/use-fetch/runtime/useApiRequest.ts`
585
+
586
+ **Signature:**
587
+
588
+ ```typescript
589
+ export function useApiRequest<T>(
590
+ url: string | (() => string),
591
+ options?: ApiRequestOptions<T>
592
+ ): UseFetchReturn<T>;
593
+ ```
594
+
595
+ **Purpose**: Wrapper for `useFetch` with lifecycle callbacks.
596
+
597
+ **Features:**
598
+
599
+ - ✅ Execute callbacks in correct order
600
+ - ✅ Apply `pick` for field selection
601
+ - ✅ Apply `transform` for data transformation
602
+ - ✅ Merge global and local callbacks
603
+ - ✅ Support request modification via `onRequest`
604
+ - ✅ Full TypeScript type safety
605
+
606
+ **Implementation Details:**
607
+
608
+ ```typescript
609
+ export function useApiRequest<T>(url: string, options?: ApiRequestOptions<T>) {
610
+ // 1. Extract skipGlobalCallbacks
611
+ const skipGlobalCallbacks = options?.skipGlobalCallbacks;
612
+
613
+ // 2. Merge callbacks
614
+ const mergedCallbacks = mergeCallbacks(
615
+ url,
616
+ {
617
+ onRequest: options?.onRequest,
618
+ onSuccess: options?.onSuccess,
619
+ onError: options?.onError,
620
+ onFinish: options?.onFinish,
621
+ },
622
+ skipGlobalCallbacks
623
+ );
624
+
625
+ // 3. Execute onRequest
626
+ let modifiedOptions = { ...options };
627
+ if (mergedCallbacks.onRequest) {
628
+ const ctx = { url, method: options?.method, ... };
629
+ const result = mergedCallbacks.onRequest(ctx);
630
+ if (result) {
631
+ modifiedOptions = applyRequestModifications(modifiedOptions, result);
632
+ }
633
+ }
634
+
635
+ // 4. Call useFetch
636
+ const result = useFetch<T>(url, modifiedOptions);
637
+
638
+ // 5. Watch for data, error, and pending state changes
639
+ watch(
640
+ () => [result.data.value, result.error.value, result.pending.value] as const,
641
+ async ([data, error, pending], [prevData, prevError, prevPending]) => {
642
+ if (data && data !== prevData) {
643
+ // Apply pick/transform
644
+ let processedData = data;
645
+ if (pick) processedData = applyPick(data, pick);
646
+ if (transform) processedData = transform(processedData);
647
+
648
+ // onSuccess
649
+ if (mergedCallbacks.onSuccess) {
650
+ await mergedCallbacks.onSuccess(processedData);
651
+ }
652
+ }
653
+
654
+ if (error && error !== prevError) {
655
+ // onError
656
+ if (mergedCallbacks.onError) {
657
+ await mergedCallbacks.onError(error);
658
+ }
659
+ }
660
+
661
+ // onFinish when request completes (was pending, now not)
662
+ if (prevPending && !pending && mergedCallbacks.onFinish) {
663
+ await mergedCallbacks.onFinish({ data: ..., error: ..., success: !error });
664
+ }
665
+ },
666
+ { immediate: true }
667
+ );
668
+
669
+ return result;
670
+ }
671
+ ```
672
+
673
+ ### useApiAsyncData()
674
+
675
+ **File**: `src/generators/use-async-data/runtime/useApiAsyncData.ts`
676
+
677
+ **Signature:**
678
+
679
+ ```typescript
680
+ export function useApiAsyncData<T>(
681
+ key: string,
682
+ url: string | (() => string),
683
+ options?: ApiAsyncDataOptions<T>
684
+ ): AsyncDataReturn<T>;
685
+ ```
686
+
687
+ **Purpose**: Wrapper for `useAsyncData` with callbacks (normal version).
688
+
689
+ **Key Differences from useApiRequest:**
690
+
691
+ - Uses `$fetch` inside a `useAsyncData` handler instead of `useFetch` directly
692
+ - Callbacks execute in try/catch inside the fetch function
693
+ - Reactive watching via explicit `watchSources` (URL functions + body/params refs)
694
+ - Requires unique `key` parameter
695
+ - `watch: false` disables all reactive re-fetching
696
+
697
+ ### useApiAsyncDataRaw()
698
+
699
+ **File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
700
+
701
+ **Signature:**
702
+
703
+ ```typescript
704
+ export function useApiAsyncDataRaw<T>(
705
+ key: string,
706
+ url: string,
707
+ options?: ApiAsyncDataRawOptions<T>
708
+ ): AsyncDataReturn<RawResponse<T>>;
709
+ ```
710
+
711
+ **Purpose**: Wrapper with full response (data + headers + status).
712
+
713
+ **Key Differences:**
714
+
715
+ - Uses `$fetch.raw` instead of `$fetch`
716
+ - Returns `RawResponse<T>` instead of `T`
717
+ - `onSuccess` receives `(data, responseContext)` with headers/status
718
+ - `pick`/`transform` apply only to data, not full response
719
+
720
+ ## Callback Interfaces
721
+
722
+ ### Global Callbacks Config
723
+
724
+ **File**: `plugins/api-callbacks.ts` (copied to user's project)
725
+
726
+ **Structure:**
727
+
728
+ ```typescript
729
+ export default defineNuxtPlugin(() => {
730
+ return {
731
+ provide: {
732
+ getGlobalApiCallbacks: () => ({
733
+ patterns: string[]; // Optional URL patterns
734
+ onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
735
+ onSuccess?: (data: any, ctx: SuccessContext) => void | false;
736
+ onError?: (error: any, ctx: ErrorContext) => void | false;
737
+ onFinish?: (ctx: FinishContext) => void;
738
+ }),
739
+ },
740
+ };
741
+ });
742
+ ```
743
+
744
+ **Pattern Syntax:**
745
+
746
+ - `**` - Matches any number of path segments
747
+ - `*` - Matches single path segment
748
+ - No pattern - Applies to all URLs
749
+
750
+ **Examples:**
751
+
752
+ ```typescript
753
+ patterns: [
754
+ '/api/auth/**', // All auth endpoints
755
+ '/api/users/**', // All user endpoints
756
+ '/api/admin/*/logs', // /api/admin/{id}/logs only
757
+ ];
758
+ ```
759
+
760
+ ### Helper Functions
761
+
762
+ #### getGlobalCallbacks()
763
+
764
+ ```typescript
765
+ function getGlobalCallbacks(): GlobalCallbacksConfig | null;
766
+ ```
767
+
768
+ **Purpose**: Read global callbacks from Nuxt plugin.
769
+
770
+ **Returns**: Global config or `null` if plugin doesn't exist
771
+
772
+ #### shouldApplyGlobalCallback()
773
+
774
+ ```typescript
775
+ function shouldApplyGlobalCallback(
776
+ callbackName: string,
777
+ skipConfig: SkipGlobalCallbacks | undefined,
778
+ url: string,
779
+ patterns?: string[]
780
+ ): boolean;
781
+ ```
782
+
783
+ **Purpose**: Determine if global callback should run.
784
+
785
+ **Checks:**
786
+
787
+ 1. `skipConfig === true` → false
788
+ 2. `skipConfig.includes(callbackName)` → false
789
+ 3. URL matches patterns (if patterns exist) → true/false
790
+ 4. Otherwise → true
791
+
792
+ #### mergeCallbacks()
793
+
794
+ ```typescript
795
+ function mergeCallbacks(
796
+ url: string,
797
+ localCallbacks: Partial<GlobalCallbacksConfig>,
798
+ skipGlobalCallbacks?: SkipGlobalCallbacks
799
+ ): Required<Omit<GlobalCallbacksConfig, 'patterns'>>;
800
+ ```
801
+
802
+ **Purpose**: Merge global and local callbacks.
803
+
804
+ **Returns**: Object with all 4 callbacks (onRequest, onSuccess, onError, onFinish)
805
+
806
+ **Execution Order:**
807
+
808
+ 1. Global callback runs first
809
+ 2. If global returns `false`, local is skipped (except onFinish)
810
+ 3. Local callback runs second
811
+
812
+ ## Generated Composables
813
+
814
+ ### useFetch Composables
815
+
816
+ **Pattern**: `useFetch[MethodName]`
817
+
818
+ **Example:**
819
+
820
+ ```typescript
821
+ export const useFetchAddPet = (params: AddPetRequest, options?: ApiRequestOptions<Pet>) => {
822
+ return useApiRequest<Pet>('/pet', {
823
+ method: 'POST',
824
+ body: params.pet,
825
+ ...options,
826
+ });
827
+ };
828
+ ```
829
+
830
+ **Usage:**
831
+
832
+ ```typescript
833
+ const { data, pending, error, refresh } = useFetchAddPet(
834
+ { pet: { name: 'Fluffy', status: 'available' } },
835
+ {
836
+ onSuccess: (pet) => console.log('Pet added:', pet),
837
+ onError: (error) => console.error('Failed:', error),
838
+ }
839
+ );
840
+ ```
841
+
842
+ ### useAsyncData Composables
843
+
844
+ **Pattern**: `useAsyncData[MethodName]` & `useAsyncData[MethodName]Raw`
845
+
846
+ **Normal Version:**
847
+
848
+ ```typescript
849
+ export const useAsyncDataGetPet = (params: GetPetRequest, options?: ApiAsyncDataOptions<Pet>) => {
850
+ return useApiAsyncData<Pet>('useAsyncDataGetPet', `/pet/${params.petId}`, {
851
+ method: 'GET',
852
+ ...options,
853
+ });
854
+ };
855
+ ```
856
+
857
+ **Raw Version:**
858
+
859
+ ```typescript
860
+ export const useAsyncDataGetPetRaw = (
861
+ params: GetPetRequest,
862
+ options?: ApiAsyncDataRawOptions<Pet>
863
+ ) => {
864
+ return useApiAsyncDataRaw<Pet>('useAsyncDataGetPetRaw', `/pet/${params.petId}`, {
865
+ method: 'GET',
866
+ ...options,
867
+ });
868
+ };
869
+ ```
870
+
871
+ **Usage:**
872
+
873
+ ```typescript
874
+ // Normal
875
+ const { data, pending } = useAsyncDataGetPet({ petId: 123 });
876
+ // data.value: Pet
877
+
878
+ // Raw
879
+ const { data: rawData } = useAsyncDataGetPetRaw({ petId: 123 });
880
+ // rawData.value: { data: Pet, headers: Headers, status: 200, statusText: 'OK' }
881
+ const pet = rawData.value?.data;
882
+ const token = rawData.value?.headers.get('X-Auth-Token');
883
+ ```
884
+
885
+ ---
886
+
887
+ **Next**: [Development Guide](./DEVELOPMENT.md) | [Troubleshooting](./TROUBLESHOOTING.md)