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,737 @@
1
+ import * as path from 'path';
2
+ import { pascalCase, camelCase } from 'change-case';
3
+ /**
4
+ * Generate the Auth Context stub file
5
+ * This file is generated ONCE and never overwritten
6
+ */
7
+ export function generateAuthContextStub() {
8
+ return `import type { H3Event } from 'h3';
9
+ import type { AuthContext } from './types.js';
10
+
11
+ /**
12
+ * Get authentication context from the current request
13
+ *
14
+ * TODO: Implement your authentication logic here
15
+ * This function is called automatically by all generated server routes.
16
+ *
17
+ * IMPORTANT: This file is NEVER regenerated - your changes are safe!
18
+ *
19
+ * Examples for popular auth modules:
20
+ *
21
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
22
+ * Option 1: @sidebase/nuxt-auth
23
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
24
+ *
25
+ * import { getServerSession } from '#auth';
26
+ *
27
+ * export async function getAuthContext(event: H3Event): Promise<AuthContext> {
28
+ * const session = await getServerSession(event);
29
+ *
30
+ * if (!session) {
31
+ * return {
32
+ * isAuthenticated: false,
33
+ * userId: null,
34
+ * roles: [],
35
+ * permissions: [],
36
+ * };
37
+ * }
38
+ *
39
+ * return {
40
+ * isAuthenticated: true,
41
+ * userId: session.user.id,
42
+ * email: session.user.email,
43
+ * roles: session.user.roles || [],
44
+ * permissions: session.user.permissions || [],
45
+ * };
46
+ * }
47
+ *
48
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
49
+ * Option 2: Custom JWT
50
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
51
+ *
52
+ * import { getCookie } from 'h3';
53
+ * import jwt from 'jsonwebtoken';
54
+ *
55
+ * export async function getAuthContext(event: H3Event): Promise<AuthContext> {
56
+ * const token = getCookie(event, 'auth-token');
57
+ *
58
+ * if (!token) {
59
+ * return {
60
+ * isAuthenticated: false,
61
+ * userId: null,
62
+ * roles: [],
63
+ * permissions: [],
64
+ * };
65
+ * }
66
+ *
67
+ * try {
68
+ * const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any;
69
+ * return {
70
+ * isAuthenticated: true,
71
+ * userId: decoded.sub,
72
+ * email: decoded.email,
73
+ * roles: decoded.roles || [],
74
+ * permissions: decoded.permissions || [],
75
+ * };
76
+ * } catch (error) {
77
+ * return {
78
+ * isAuthenticated: false,
79
+ * userId: null,
80
+ * roles: [],
81
+ * permissions: [],
82
+ * };
83
+ * }
84
+ * }
85
+ *
86
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
87
+ * Option 3: Session Cookies
88
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
89
+ *
90
+ * import { getCookie } from 'h3';
91
+ *
92
+ * export async function getAuthContext(event: H3Event): Promise<AuthContext> {
93
+ * const sessionId = getCookie(event, 'session-id');
94
+ *
95
+ * if (!sessionId) {
96
+ * return {
97
+ * isAuthenticated: false,
98
+ * userId: null,
99
+ * roles: [],
100
+ * permissions: [],
101
+ * };
102
+ * }
103
+ *
104
+ * // TODO: Look up session in your database/store
105
+ * const session = await db.sessions.findOne({ id: sessionId });
106
+ *
107
+ * if (!session || session.expiresAt < Date.now()) {
108
+ * return {
109
+ * isAuthenticated: false,
110
+ * userId: null,
111
+ * roles: [],
112
+ * permissions: [],
113
+ * };
114
+ * }
115
+ *
116
+ * return {
117
+ * isAuthenticated: true,
118
+ * userId: session.userId,
119
+ * roles: session.roles || [],
120
+ * permissions: session.permissions || [],
121
+ * };
122
+ * }
123
+ */
124
+
125
+ /**
126
+ * Default implementation - No authentication
127
+ * Replace this with your actual auth logic above
128
+ */
129
+ export async function getAuthContext(event: H3Event): Promise<AuthContext> {
130
+ // TODO: Implement your authentication logic
131
+ // See examples above for popular auth modules
132
+
133
+ return {
134
+ isAuthenticated: false,
135
+ userId: null,
136
+ roles: [],
137
+ permissions: [],
138
+ };
139
+ }
140
+ `;
141
+ }
142
+ /**
143
+ * Generate the Auth Types stub file
144
+ * This file is generated ONCE and never overwritten
145
+ */
146
+ export function generateAuthTypesStub() {
147
+ return `/**
148
+ * Authentication context type
149
+ *
150
+ * IMPORTANT: This file is NEVER regenerated - your changes are safe!
151
+ *
152
+ * You can extend this interface with any properties you need:
153
+ * - User information (email, name, avatar)
154
+ * - Permissions and roles
155
+ * - Organization/tenant context
156
+ * - Feature flags
157
+ * - Custom metadata
158
+ */
159
+ export interface AuthContext {
160
+ /** Whether the user is authenticated */
161
+ isAuthenticated: boolean;
162
+
163
+ /** User ID (null if not authenticated) */
164
+ userId: string | null;
165
+
166
+ /** User roles (e.g., ['admin', 'user']) */
167
+ roles: string[];
168
+
169
+ /** User permissions (e.g., ['pet:read', 'pet:write']) */
170
+ permissions: string[];
171
+
172
+ // Add more fields as needed:
173
+ // email?: string;
174
+ // name?: string;
175
+ // avatar?: string;
176
+ // organizationId?: string;
177
+ // tenantId?: string;
178
+ // features?: string[];
179
+ // metadata?: Record<string, any>;
180
+ }
181
+
182
+ /**
183
+ * Helper to check if user has a specific permission
184
+ */
185
+ export function hasPermission(auth: AuthContext, permission: string): boolean {
186
+ return auth.isAuthenticated && auth.permissions.includes(permission);
187
+ }
188
+
189
+ /**
190
+ * Helper to check if user has a specific role
191
+ */
192
+ export function hasRole(auth: AuthContext, role: string): boolean {
193
+ return auth.isAuthenticated && auth.roles.includes(role);
194
+ }
195
+
196
+ /**
197
+ * Helper to check if user has ANY of the specified roles
198
+ */
199
+ export function hasAnyRole(auth: AuthContext, roles: string[]): boolean {
200
+ return auth.isAuthenticated && roles.some(role => auth.roles.includes(role));
201
+ }
202
+
203
+ /**
204
+ * Helper to check if user has ALL of the specified roles
205
+ */
206
+ export function hasAllRoles(auth: AuthContext, roles: string[]): boolean {
207
+ return auth.isAuthenticated && roles.every(role => auth.roles.includes(role));
208
+ }
209
+ `;
210
+ }
211
+ /**
212
+ * Generate a transformer stub for a specific resource
213
+ * This file is generated ONCE and never overwritten
214
+ */
215
+ export function generateTransformerStub(resource, methods, inputDir) {
216
+ const resourcePascal = pascalCase(resource);
217
+ const resourceCamel = camelCase(resource);
218
+ // Compute ~/path for model types import
219
+ const projectRoot = process.cwd();
220
+ const relativeInputDir = path.relative(projectRoot, path.resolve(inputDir)).replace(/\\/g, '/');
221
+ const modelsImportPath = `~/${relativeInputDir}/models`;
222
+ // Extract unique type names from methods
223
+ const typeNames = new Set();
224
+ methods.forEach((method) => {
225
+ if (method.responseType) {
226
+ const baseType = extractBaseType(method.responseType);
227
+ if (baseType) {
228
+ typeNames.add(baseType);
229
+ }
230
+ }
231
+ });
232
+ const importTypes = Array.from(typeNames).join(', ');
233
+ return `import type { H3Event } from 'h3';
234
+ import type { AuthContext } from '~/server/auth/types';
235
+ ${importTypes ? `import type { ${importTypes} } from '${modelsImportPath}';\n` : ''}
236
+ /**
237
+ * Transformer for ${resource} endpoints
238
+ *
239
+ * IMPORTANT: This file is NEVER regenerated - your changes are safe!
240
+ *
241
+ * This transformer is automatically called by generated server routes.
242
+ * Add your business logic here:
243
+ * - Data transformation
244
+ * - Permission checks
245
+ * - Filtering sensitive data
246
+ * - Combining multiple sources
247
+ * - Caching logic
248
+ * - Rate limiting
249
+ *
250
+ * The transformer receives:
251
+ * - data: The raw response from the backend API
252
+ * - event: The h3 event (for accessing headers, query params, etc.)
253
+ * - auth: The authentication context
254
+ */
255
+
256
+ /**
257
+ * Transform ${resource} data
258
+ *
259
+ * TODO: Implement your transformation logic here
260
+ *
261
+ * Examples:
262
+ *
263
+ * 1. Add computed fields:
264
+ * return { ...data, fullName: \`\${data.firstName} \${data.lastName}\` }
265
+ *
266
+ * 2. Filter sensitive data:
267
+ * const { password, internalId, ...safe } = data
268
+ * return safe
269
+ *
270
+ * 3. Add permissions:
271
+ * return {
272
+ * ...data,
273
+ * canEdit: auth.permissions.includes('${resourceCamel}:write'),
274
+ * canDelete: auth.permissions.includes('${resourceCamel}:delete'),
275
+ * }
276
+ *
277
+ * 4. Filter based on permissions:
278
+ * if (!auth.permissions.includes('${resourceCamel}:read:all')) {
279
+ * return { ...data, sensitiveField: undefined }
280
+ * }
281
+ * return data
282
+ */
283
+ export async function transform${resourcePascal}<T = any>(
284
+ data: T,
285
+ event: H3Event,
286
+ auth: AuthContext | null
287
+ ): Promise<T> {
288
+ // TODO: Add your transformation logic here
289
+
290
+ // Example: Add permission flags
291
+ // if (typeof data === 'object' && data !== null) {
292
+ // return {
293
+ // ...data,
294
+ // canEdit: auth?.permissions.includes('${resourceCamel}:write') ?? false,
295
+ // canDelete: auth?.permissions.includes('${resourceCamel}:delete') ?? false,
296
+ // } as T;
297
+ // }
298
+
299
+ // Default: Return data unchanged
300
+ return data;
301
+ }
302
+ `;
303
+ }
304
+ /**
305
+ * Generate transformer examples file
306
+ * This file is ALWAYS regenerated as a reference
307
+ */
308
+ export function generateTransformerExamples() {
309
+ return `/**
310
+ * ⚠️ EXAMPLES ONLY - DO NOT EDIT
311
+ *
312
+ * This file contains examples of transformer patterns.
313
+ * Copy these examples to your actual transformer files.
314
+ *
315
+ * This file is regenerated on every generation - changes will be lost!
316
+ *
317
+ * @generated by nuxt-openapi-generator
318
+ */
319
+
320
+ import type { H3Event } from 'h3';
321
+ import type { AuthContext } from '~/server/auth/types';
322
+
323
+ /**
324
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
325
+ * Example 1: Basic Transformation
326
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
327
+ *
328
+ * Transform backend data to frontend format
329
+ */
330
+ export async function exampleBasicTransform<T>(
331
+ data: T,
332
+ event: H3Event,
333
+ auth: AuthContext | null
334
+ ): Promise<T> {
335
+ // Add computed fields
336
+ if (typeof data === 'object' && data !== null) {
337
+ return {
338
+ ...data,
339
+ // Example: Format dates
340
+ // createdAtFormatted: new Date(data.createdAt).toLocaleDateString(),
341
+
342
+ // Example: Add computed values
343
+ // fullName: \`\${data.firstName} \${data.lastName}\`,
344
+
345
+ // Example: Add metadata
346
+ // _metadata: {
347
+ // retrievedAt: Date.now(),
348
+ // userId: auth?.userId,
349
+ // },
350
+ } as T;
351
+ }
352
+
353
+ return data;
354
+ }
355
+
356
+ /**
357
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
358
+ * Example 2: Filter Sensitive Data
359
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
360
+ *
361
+ * Remove fields that shouldn't be exposed to the client
362
+ */
363
+ export async function exampleFilterSensitiveData(
364
+ data: any,
365
+ event: H3Event,
366
+ auth: AuthContext | null
367
+ ): Promise<any> {
368
+ // Define sensitive fields
369
+ const sensitiveFields = ['password', 'passwordHash', 'ssn', 'internalId', 'secretKey'];
370
+
371
+ if (Array.isArray(data)) {
372
+ return data.map(item => filterObject(item, sensitiveFields));
373
+ }
374
+
375
+ return filterObject(data, sensitiveFields);
376
+ }
377
+
378
+ function filterObject(obj: any, fieldsToRemove: string[]): any {
379
+ if (typeof obj !== 'object' || obj === null) return obj;
380
+
381
+ const filtered = { ...obj };
382
+ fieldsToRemove.forEach(field => delete filtered[field]);
383
+ return filtered;
384
+ }
385
+
386
+ /**
387
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
388
+ * Example 3: Add Permission Flags
389
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
390
+ *
391
+ * Enrich data with user-specific permissions
392
+ */
393
+ export async function exampleAddPermissions(
394
+ data: any,
395
+ event: H3Event,
396
+ auth: AuthContext | null
397
+ ): Promise<any> {
398
+ if (typeof data !== 'object' || data === null) return data;
399
+
400
+ return {
401
+ ...data,
402
+ // Add permission flags
403
+ canEdit: auth?.permissions.includes('resource:write') ?? false,
404
+ canDelete: auth?.permissions.includes('resource:delete') ?? false,
405
+ canShare: auth?.permissions.includes('resource:share') ?? false,
406
+
407
+ // Add ownership check
408
+ isOwner: auth?.userId === data.userId,
409
+
410
+ // Add role-based flags
411
+ isAdmin: auth?.roles.includes('admin') ?? false,
412
+ };
413
+ }
414
+
415
+ /**
416
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
417
+ * Example 4: Combine Multiple Sources
418
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
419
+ *
420
+ * Fetch additional data from other endpoints
421
+ */
422
+ export async function exampleCombineSources(
423
+ data: any,
424
+ event: H3Event,
425
+ auth: AuthContext | null
426
+ ): Promise<any> {
427
+ const config = useRuntimeConfig();
428
+
429
+ // Example: Fetch related data
430
+ // const reviews = await $fetch(\`\${config.apiBaseUrl}/reviews/\${data.id}\`);
431
+ // const availability = await $fetch(\`\${config.apiBaseUrl}/availability/\${data.id}\`);
432
+
433
+ return {
434
+ ...data,
435
+ // reviews,
436
+ // availability,
437
+ };
438
+ }
439
+
440
+ /**
441
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
442
+ * Example 5: Permission-Based Filtering
443
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
444
+ *
445
+ * Show/hide fields based on user permissions
446
+ */
447
+ export async function examplePermissionBasedFiltering(
448
+ data: any,
449
+ event: H3Event,
450
+ auth: AuthContext | null
451
+ ): Promise<any> {
452
+ if (typeof data !== 'object' || data === null) return data;
453
+
454
+ const result = { ...data };
455
+
456
+ // Hide sensitive fields for non-admins
457
+ if (!auth?.roles.includes('admin')) {
458
+ delete result.internalNotes;
459
+ delete result.costPrice;
460
+ delete result.supplierInfo;
461
+ }
462
+
463
+ // Show detailed info only for specific permission
464
+ if (!auth?.permissions.includes('resource:read:detailed')) {
465
+ delete result.analytics;
466
+ delete result.auditLog;
467
+ }
468
+
469
+ return result;
470
+ }
471
+
472
+ /**
473
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
474
+ * Example 6: Array Transformation
475
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
476
+ *
477
+ * Transform each item in an array
478
+ */
479
+ export async function exampleArrayTransform(
480
+ data: any[],
481
+ event: H3Event,
482
+ auth: AuthContext | null
483
+ ): Promise<any[]> {
484
+ if (!Array.isArray(data)) return data;
485
+
486
+ return data.map(item => ({
487
+ ...item,
488
+ // Add permission checks for each item
489
+ canEdit: auth?.userId === item.ownerId,
490
+ canDelete: auth?.roles.includes('admin') || auth?.userId === item.ownerId,
491
+ }));
492
+ }
493
+
494
+ /**
495
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
496
+ * Example 7: Error Handling
497
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
498
+ *
499
+ * Handle transformation errors gracefully
500
+ */
501
+ export async function exampleErrorHandling(
502
+ data: any,
503
+ event: H3Event,
504
+ auth: AuthContext | null
505
+ ): Promise<any> {
506
+ try {
507
+ // Attempt transformation
508
+ return {
509
+ ...data,
510
+ processed: true,
511
+ };
512
+ } catch (error) {
513
+ console.error('Transformation error:', error);
514
+
515
+ // Return original data on error
516
+ return data;
517
+ }
518
+ }
519
+ `;
520
+ }
521
+ /**
522
+ * Generate BFF README file
523
+ */
524
+ export function generateBffReadme() {
525
+ return `# Backend for Frontend (BFF) - Transformers
526
+
527
+ This directory contains **transformer functions** that add business logic to your API routes.
528
+
529
+ ## 🎯 What are Transformers?
530
+
531
+ Transformers allow you to add custom logic between the backend API and your Nuxt client:
532
+
533
+ \`\`\`
534
+ Client → Generated Route → Transformer → Backend API
535
+
536
+ Your Logic
537
+ \`\`\`
538
+
539
+ ## 📂 File Structure
540
+
541
+ \`\`\`
542
+ server/
543
+ auth/
544
+ context.ts ← Authentication logic (YOU implement once)
545
+ types.ts ← Auth types and helpers
546
+ api/ ← Generated routes (NEVER edit these)
547
+ pet/
548
+ [id].get.ts ← Calls transformPet() automatically
549
+ bff/
550
+ transformers/ ← Your business logic (SAFE to edit)
551
+ pet.ts ← Transform pet data
552
+ store.ts ← Transform store data
553
+ _transformers.example.ts ← Examples (regenerated for reference)
554
+ README.md ← This file
555
+ \`\`\`
556
+
557
+ ## 🔒 Which Files Are Safe to Edit?
558
+
559
+ | File | Safe to Edit? | Regenerated? |
560
+ |------|---------------|--------------|
561
+ | \`server/auth/context.ts\` | ✅ YES | ❌ NO |
562
+ | \`server/auth/types.ts\` | ✅ YES | ❌ NO |
563
+ | \`server/bff/transformers/*.ts\` | ✅ YES | ❌ NO |
564
+ | \`server/api/**/*.ts\` | ❌ NO | ✅ YES |
565
+ | \`server/bff/_transformers.example.ts\` | ❌ NO | ✅ YES |
566
+
567
+ ## 🚀 Quick Start
568
+
569
+ ### 1. Implement Authentication (Optional)
570
+
571
+ Edit \`server/auth/context.ts\` and implement \`getAuthContext()\`:
572
+
573
+ \`\`\`typescript
574
+ // server/auth/context.ts
575
+ import { getServerSession } from '#auth';
576
+
577
+ export async function getAuthContext(event: H3Event): Promise<AuthContext> {
578
+ const session = await getServerSession(event);
579
+
580
+ return {
581
+ isAuthenticated: !!session,
582
+ userId: session?.user.id ?? null,
583
+ roles: session?.user.roles ?? [],
584
+ permissions: session?.user.permissions ?? [],
585
+ };
586
+ }
587
+ \`\`\`
588
+
589
+ ### 2. Add Transformation Logic
590
+
591
+ Edit transformer files in \`server/bff/transformers/\`:
592
+
593
+ \`\`\`typescript
594
+ // server/bff/transformers/pet.ts
595
+ export async function transformPet(
596
+ data: any,
597
+ event: H3Event,
598
+ auth: AuthContext | null
599
+ ): Promise<any> {
600
+ // Add permission flags
601
+ return {
602
+ ...data,
603
+ canEdit: auth?.permissions.includes('pet:write') ?? false,
604
+ canDelete: auth?.permissions.includes('pet:delete') ?? false,
605
+ };
606
+ }
607
+ \`\`\`
608
+
609
+ ### 3. Use From Client
610
+
611
+ No changes needed - your generated routes automatically use transformers:
612
+
613
+ \`\`\`vue
614
+ <script setup>
615
+ const { data: pet } = await useFetch('/api/pet/123');
616
+
617
+ // pet.canEdit and pet.canDelete are now available!
618
+ </script>
619
+ \`\`\`
620
+
621
+ ## 📚 Common Use Cases
622
+
623
+ ### Filter Sensitive Data
624
+
625
+ \`\`\`typescript
626
+ export async function transformUser(data: any, event: H3Event, auth: AuthContext | null) {
627
+ const { password, ssn, internalId, ...safe } = data;
628
+ return safe;
629
+ }
630
+ \`\`\`
631
+
632
+ ### Add Permission Flags
633
+
634
+ \`\`\`typescript
635
+ export async function transformPost(data: any, event: H3Event, auth: AuthContext | null) {
636
+ return {
637
+ ...data,
638
+ canEdit: auth?.userId === data.authorId,
639
+ canDelete: auth?.roles.includes('admin'),
640
+ };
641
+ }
642
+ \`\`\`
643
+
644
+ ### Combine Multiple Sources
645
+
646
+ \`\`\`typescript
647
+ export async function transformProduct(data: any, event: H3Event, auth: AuthContext | null) {
648
+ const config = useRuntimeConfig();
649
+
650
+ const [reviews, inventory] = await Promise.all([
651
+ $fetch(\`\${config.apiBaseUrl}/reviews/\${data.id}\`),
652
+ $fetch(\`\${config.apiBaseUrl}/inventory/\${data.id}\`),
653
+ ]);
654
+
655
+ return {
656
+ ...data,
657
+ reviews,
658
+ inventory,
659
+ };
660
+ }
661
+ \`\`\`
662
+
663
+ ### Permission-Based Filtering
664
+
665
+ \`\`\`typescript
666
+ export async function transformReport(data: any, event: H3Event, auth: AuthContext | null) {
667
+ const result = { ...data };
668
+
669
+ // Hide sensitive data for non-admins
670
+ if (!auth?.roles.includes('admin')) {
671
+ delete result.financialDetails;
672
+ delete result.internalNotes;
673
+ }
674
+
675
+ return result;
676
+ }
677
+ \`\`\`
678
+
679
+ ## 🔄 Regeneration Safety
680
+
681
+ **IMPORTANT:** When you regenerate routes with the CLI:
682
+ - ✅ Transformer files are **PRESERVED**
683
+ - ✅ Auth context files are **PRESERVED**
684
+ - ❌ Generated routes are **OVERWRITTEN** (but this is OK!)
685
+
686
+ Your custom logic is always safe because it lives in separate files.
687
+
688
+ ## 🎓 Best Practices
689
+
690
+ 1. **Keep Transformers Pure**: Avoid side effects, focus on data transformation
691
+ 2. **One Transformer Per Resource**: \`pet.ts\`, \`store.ts\`, etc.
692
+ 3. **Use TypeScript**: Add proper types for better IntelliSense
693
+ 4. **Document Your Logic**: Add comments explaining business rules
694
+ 5. **Test Edge Cases**: Handle \`null\`, arrays, missing fields gracefully
695
+ 6. **Performance**: Use \`Promise.all()\` for parallel fetches
696
+ 7. **Security**: Always validate auth before accessing sensitive data
697
+
698
+ ## 🆘 Troubleshooting
699
+
700
+ **Transformer not being called?**
701
+ - Check that the transformer file exists
702
+ - Verify the function name matches: \`transform{Resource}\`
703
+ - Check console for import errors
704
+
705
+ **Auth context is null?**
706
+ - Verify \`getAuthContext()\` is implemented in \`server/auth/context.ts\`
707
+ - Check for errors in the auth implementation
708
+ - Auth is optional - transformers work without it
709
+
710
+ **Types not working?**
711
+ - Ensure types are exported from \`~/types/api\`
712
+ - Add explicit type parameters to transformer functions
713
+ - Check TypeScript errors in the console
714
+
715
+ ## 📖 Learn More
716
+
717
+ See \`_transformers.example.ts\` for more examples.
718
+ `;
719
+ }
720
+ /**
721
+ * Extract base type name from a type string
722
+ * Examples:
723
+ * "Pet" -> "Pet"
724
+ * "Pet[]" -> "Pet"
725
+ * "Array<Pet>" -> "Pet"
726
+ */
727
+ function extractBaseType(typeString) {
728
+ // Remove array brackets
729
+ let baseType = typeString.replace(/\[\]$/, '').replace(/^Array<(.+)>$/, '$1');
730
+ // Remove whitespace
731
+ baseType = baseType.trim();
732
+ // Only return if it looks like a custom type (starts with capital)
733
+ if (baseType && /^[A-Z]/.test(baseType)) {
734
+ return baseType;
735
+ }
736
+ return null;
737
+ }