nuxt-openapi-hyperfetch 0.3.8-beta → 1.0.0

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 (64) hide show
  1. package/README.md +218 -212
  2. package/dist/generators/components/connector-generator/templates.js +67 -17
  3. package/dist/generators/components/schema-analyzer/intent-detector.js +1 -12
  4. package/dist/generators/components/schema-analyzer/openapi-reader.js +10 -1
  5. package/dist/generators/components/schema-analyzer/resource-grouper.js +7 -0
  6. package/dist/generators/components/schema-analyzer/schema-field-mapper.js +1 -22
  7. package/dist/generators/components/schema-analyzer/types.d.ts +10 -0
  8. package/dist/generators/connectors/generator.d.ts +12 -0
  9. package/dist/generators/connectors/generator.js +115 -0
  10. package/dist/generators/connectors/runtime/connector-types.d.ts +147 -0
  11. package/dist/generators/connectors/runtime/connector-types.js +10 -0
  12. package/dist/generators/connectors/runtime/useCreateConnector.d.ts +26 -0
  13. package/dist/generators/connectors/runtime/useCreateConnector.js +156 -0
  14. package/dist/generators/connectors/runtime/useDeleteConnector.d.ts +30 -0
  15. package/dist/generators/connectors/runtime/useDeleteConnector.js +143 -0
  16. package/dist/generators/connectors/runtime/useGetAllConnector.d.ts +25 -0
  17. package/dist/generators/connectors/runtime/useGetAllConnector.js +127 -0
  18. package/dist/generators/connectors/runtime/useGetConnector.d.ts +15 -0
  19. package/dist/generators/connectors/runtime/useGetConnector.js +99 -0
  20. package/dist/generators/connectors/runtime/useUpdateConnector.d.ts +34 -0
  21. package/dist/generators/connectors/runtime/useUpdateConnector.js +211 -0
  22. package/dist/generators/connectors/runtime/zod-error-merger.d.ts +23 -0
  23. package/dist/generators/connectors/runtime/zod-error-merger.js +106 -0
  24. package/dist/generators/connectors/templates.d.ts +4 -0
  25. package/dist/generators/connectors/templates.js +376 -0
  26. package/dist/generators/connectors/types.d.ts +37 -0
  27. package/dist/generators/connectors/types.js +7 -0
  28. package/dist/generators/shared/runtime/useDeleteConnector.js +4 -2
  29. package/dist/generators/shared/runtime/useDetailConnector.d.ts +0 -1
  30. package/dist/generators/shared/runtime/useDetailConnector.js +9 -20
  31. package/dist/generators/shared/runtime/useFormConnector.js +4 -3
  32. package/dist/generators/use-async-data/runtime/useApiAsyncData.js +14 -5
  33. package/dist/generators/use-async-data/templates.js +20 -16
  34. package/dist/generators/use-fetch/templates.js +1 -1
  35. package/dist/index.js +1 -16
  36. package/dist/module/index.js +2 -3
  37. package/package.json +4 -3
  38. package/src/cli/prompts.ts +1 -7
  39. package/src/generators/components/connector-generator/templates.ts +97 -22
  40. package/src/generators/components/schema-analyzer/intent-detector.ts +1 -16
  41. package/src/generators/components/schema-analyzer/openapi-reader.ts +14 -1
  42. package/src/generators/components/schema-analyzer/resource-grouper.ts +9 -0
  43. package/src/generators/components/schema-analyzer/schema-field-mapper.ts +1 -26
  44. package/src/generators/components/schema-analyzer/types.ts +11 -0
  45. package/src/generators/connectors/generator.ts +137 -0
  46. package/src/generators/connectors/runtime/connector-types.ts +207 -0
  47. package/src/generators/connectors/runtime/useCreateConnector.ts +199 -0
  48. package/src/generators/connectors/runtime/useDeleteConnector.ts +179 -0
  49. package/src/generators/connectors/runtime/useGetAllConnector.ts +151 -0
  50. package/src/generators/connectors/runtime/useGetConnector.ts +120 -0
  51. package/src/generators/connectors/runtime/useUpdateConnector.ts +257 -0
  52. package/src/generators/connectors/runtime/zod-error-merger.ts +119 -0
  53. package/src/generators/connectors/templates.ts +481 -0
  54. package/src/generators/connectors/types.ts +39 -0
  55. package/src/generators/shared/runtime/useDeleteConnector.ts +4 -2
  56. package/src/generators/shared/runtime/useDetailConnector.ts +8 -19
  57. package/src/generators/shared/runtime/useFormConnector.ts +4 -3
  58. package/src/generators/use-async-data/runtime/useApiAsyncData.ts +16 -5
  59. package/src/generators/use-async-data/templates.ts +24 -16
  60. package/src/generators/use-fetch/templates.ts +1 -1
  61. package/src/index.ts +2 -19
  62. package/src/module/index.ts +2 -5
  63. package/docs/generated-components.md +0 -615
  64. package/docs/headless-composables-ui.md +0 -569
@@ -1,569 +0,0 @@
1
- # Headless UI Composables — Feature Spec & Architecture
2
-
3
- > **Status**: Planned — Capa 2 del sistema de generación de componentes Vue
4
- > **Dependencia**: Requiere que los composables `useAsyncData` estén generados previamente
5
- > **Objetivo**: Exponer lógica CRUD de forma headless y framework-agnostic para que cualquier desarrollador pueda construir sus propios componentes UI sobre ella
6
-
7
- ---
8
-
9
- ## Tabla de Contenidos
10
-
11
- - [Qué es esta feature](#qué-es-esta-feature)
12
- - [Motivación](#motivación)
13
- - [Arquitectura de 3 Capas](#arquitectura-de-3-capas)
14
- - [Diseño del Connector Unificado](#diseño-del-connector-unificado)
15
- - [Contratos de los Sub-Connectors](#contratos-de-los-sub-connectors)
16
- - [Validación con Zod](#validación-con-zod)
17
- - [Detección automática de campo tipo en formularios](#detección-automática-de-campo-tipo-en-formularios)
18
- - [Integración con paginación](#integración-con-paginación)
19
- - [Estructura de archivos a crear](#estructura-de-archivos-a-crear)
20
- - [Plan de implementación](#plan-de-implementación)
21
- - [Ejemplos de uso por el developer](#ejemplos-de-uso-por-el-developer)
22
-
23
- ---
24
-
25
- ## Qué es esta feature
26
-
27
- El **Connector Generator** genera, por cada recurso detectado en el OpenAPI spec, un composable Vue 3 headless llamado `use{Resource}Connector.ts`.
28
-
29
- Este composable:
30
- - Encapsula toda la lógica de negocio (fetch, submit, estado, paginación, errores)
31
- - No tiene ningún template HTML — es 100% lógica reutilizable
32
- - Expone una API clara y fuertemente tipada
33
- - Es el puente entre los composables generados (`useAsyncDataGetPets`) y cualquier componente UI
34
-
35
- El developer puede:
36
- 1. Usar el connector directamente para construir sus propios componentes
37
- 2. Dejar que el generador de componentes (Capa 3) genere componentes `.vue` de NuxtUI/PrimeVue sobre él
38
- 3. Usar solo una parte del connector (ej: solo `table`, ignorando los formularios)
39
-
40
- ---
41
-
42
- ## Motivación
43
-
44
- Sin esta capa, cada adaptador UI (NuxtUI, PrimeVue, Vuetify…) tendría que reimplementar:
45
- - La lógica de fetch y re-fetch
46
- - El estado de loading, errores, datos
47
- - La gestión de paginación
48
- - La apertura de modales y estado de formularios
49
- - El pre-relleno de formulario de update con datos del get
50
-
51
- Con el Connector, esa lógica se escribe **una sola vez**. Los adaptadores solo añaden el template.
52
-
53
- ---
54
-
55
- ## Arquitectura de 3 Capas
56
-
57
- ```
58
- ┌─────────────────────────────────────────────────────────────┐
59
- │ CAPA 1 — Schema Analyzer (src/generators/components/) │
60
- │ Lee OpenAPI YAML/JSON directo │
61
- │ Detecta intents por HTTP method + path pattern + schema │
62
- │ Agrupa en recursos por tag (prioridad) o path prefix │
63
- │ Produce un ResourceMap tipado │
64
- └─────────────────────────────────────────────────────────────┘
65
- ↓ alimenta
66
- ┌─────────────────────────────────────────────────────────────┐
67
- │ CAPA 2 — Connector Generator (esta feature) │
68
- │ Genera use{Resource}Connector.ts por recurso │
69
- │ Composables headless — lógica sin template │
70
- │ Usa los composables useAsyncData ya generados │
71
- │ Es consumido por Capa 3 o directamente por el developer │
72
- └─────────────────────────────────────────────────────────────┘
73
- ↓ consume
74
- ┌─────────────────────────────────────────────────────────────┐
75
- │ CAPA 3 — Component Generator (ver generated-components.md)│
76
- │ Genera .vue por recurso (NuxtUI, PrimeVue, etc.) │
77
- │ Importa use{Resource}Connector.ts │
78
- │ Solo contiene template + binding — cero lógica propia │
79
- └─────────────────────────────────────────────────────────────┘
80
- ```
81
-
82
- ---
83
-
84
- ## Diseño del Connector Unificado
85
-
86
- Cada recurso produce **un único composable** con secciones independientes:
87
-
88
- ```ts
89
- // composables/connectors/usePetsConnector.ts
90
- // AUTO-GENERATED — DO NOT EDIT
91
- // Generated by nxh — https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
92
-
93
- import { useAsyncDataGetPets } from '../use-async-data/use-async-data-get-pets'
94
- import { useAsyncDataGetPet } from '../use-async-data/use-async-data-get-pet'
95
- import { useAsyncDataCreatePet } from '../use-async-data/use-async-data-create-pet'
96
- import { useAsyncDataUpdatePet } from '../use-async-data/use-async-data-update-pet'
97
- import { useAsyncDataDeletePet } from '../use-async-data/use-async-data-delete-pet'
98
- import { useListConnector } from '#nxh/runtime/useListConnector'
99
- import { useDetailConnector } from '#nxh/runtime/useDetailConnector'
100
- import { useFormConnector } from '#nxh/runtime/useFormConnector'
101
- import { useDeleteConnector } from '#nxh/runtime/useDeleteConnector'
102
-
103
- export function usePetsConnector() {
104
- const table = useListConnector(useAsyncDataGetPets, {
105
- paginated: true,
106
- })
107
-
108
- const detail = useDetailConnector(useAsyncDataGetPet)
109
-
110
- const createForm = useFormConnector(useAsyncDataCreatePet, {
111
- schema: PetCreateSchema, // inferido del request body del POST
112
- })
113
-
114
- const updateForm = useFormConnector(useAsyncDataUpdatePet, {
115
- schema: PetUpdateSchema, // inferido del request body del PUT
116
- loadWith: detail, // pre-rellena con GET /pets/{id} si existe
117
- })
118
-
119
- const deleteAction = useDeleteConnector(useAsyncDataDeletePet)
120
-
121
- return {
122
- table,
123
- detail,
124
- createForm,
125
- updateForm,
126
- deleteAction,
127
- }
128
- }
129
- ```
130
-
131
- El connector **solo incluye las secciones que existen en el spec**. Si no hay `DELETE`, no se genera `deleteAction`. Si no hay `PUT`, no se genera `updateForm`.
132
-
133
- ---
134
-
135
- ## Contratos de los Sub-Connectors
136
-
137
- ### `useListConnector(composable, options)`
138
-
139
- Wrappea un composable GET que devuelve array:
140
-
141
- ```ts
142
- interface ListConnectorReturn<T> {
143
- // Estado
144
- rows: ComputedRef<T[]>
145
- columns: ComputedRef<ColumnDef[]> // generado de response schema
146
- loading: ComputedRef<boolean>
147
- error: ComputedRef<any>
148
-
149
- // Paginación (si paginated: true)
150
- pagination: ComputedRef<PaginationState>
151
- goToPage: (page: number) => void
152
- nextPage: () => void
153
- prevPage: () => void
154
-
155
- // Acciones
156
- refresh: () => void
157
- selected: Ref<T[]> // filas seleccionadas
158
- onRowSelect: (row: T) => void
159
-
160
- // Para apertura de modales (coordinación con PetsCrud.vue)
161
- openCreate: () => void
162
- openUpdate: (row: T) => void
163
- openDelete: (row: T) => void
164
- }
165
-
166
- interface ColumnDef {
167
- key: string
168
- label: string // viene de index.ts del developer (o key capitalizado)
169
- sortable?: boolean
170
- type: 'text' | 'number' | 'date' | 'boolean' | 'badge'
171
- }
172
- ```
173
-
174
- ### `useDetailConnector(composable)`
175
-
176
- Wrappea un composable GET que devuelve un objeto:
177
-
178
- ```ts
179
- interface DetailConnectorReturn<T> {
180
- item: Ref<T | null>
181
- loading: ComputedRef<boolean>
182
- error: ComputedRef<any>
183
- load: (id: string | number) => Promise<void>
184
- fields: ComputedRef<FieldDef[]> // para vistas de detalle
185
- }
186
- ```
187
-
188
- ### `useFormConnector(composable, options)`
189
-
190
- ```ts
191
- import type { ZodSchema } from 'zod'
192
-
193
- interface FormConnectorOptions<T> {
194
- schema: ZodSchema // Zod schema generado del request body OpenAPI
195
- loadWith?: DetailConnectorReturn<T> // pre-rellena desde detail si se pasa
196
- }
197
-
198
- interface FormConnectorReturn<T> {
199
- // Estado del formulario
200
- model: Ref<Partial<T>>
201
- fields: ComputedRef<FormFieldDef[]>
202
- loading: ComputedRef<boolean>
203
- errors: ComputedRef<Record<string, string>> // { name: 'Name is required' }
204
- isValid: ComputedRef<boolean>
205
-
206
- // Acciones
207
- submit: () => Promise<void> // valida con Zod antes de llamar al composable
208
- reset: () => void
209
- setValues: (data: Partial<T>) => void // llamado por loadWith automáticamente
210
-
211
- // Callbacks
212
- onSuccess: Ref<(result: T) => void>
213
- onError: Ref<(error: any) => void>
214
- }
215
-
216
- interface FormFieldDef {
217
- key: string
218
- label: string
219
- type: 'input' | 'textarea' | 'select' | 'checkbox' | 'datepicker' | 'number'
220
- required: boolean
221
- options?: { label: string; value: any }[] // para type: 'select' (enum en OpenAPI)
222
- placeholder?: string
223
- hidden?: boolean // readOnly: true del schema → hidden por defecto
224
- }
225
- ```
226
-
227
- **Inferencia automática de tipo de campo** (del schema OpenAPI):
228
-
229
- | OpenAPI schema | `FormFieldDef.type` generado |
230
- |-------------------------|------------------------------|
231
- | `type: string` | `input` |
232
- | `type: string, format: date` | `datepicker` |
233
- | `type: string, format: date-time` | `datepicker` |
234
- | `type: string, maxLength > 200` | `textarea` |
235
- | `type: integer / number` | `number` |
236
- | `type: boolean` | `checkbox` |
237
- | `enum: [...]` | `select` |
238
- | `readOnly: true` | campo omitido del form (configurable) |
239
-
240
- El developer puede sobreescribir cualquier campo en `components/nxh/pets/index.ts`:
241
-
242
- ```ts
243
- export const config = {
244
- fields: {
245
- bio: { type: 'textarea' }, // override de tipo de input
246
- status: { type: 'select', options: [
247
- { label: 'Active', value: 'active' },
248
- { label: 'Inactive', value: 'inactive' },
249
- ]},
250
- id: { hidden: true }, // forzar oculto aunque no sea readOnly
251
- }
252
- }
253
- ```
254
-
255
- ### `useDeleteConnector(composable)`
256
-
257
- ```ts
258
- interface DeleteConnectorReturn<T> {
259
- loading: ComputedRef<boolean>
260
- error: ComputedRef<any>
261
- target: Ref<T | null> // el item que se va a borrar
262
- setTarget: (item: T) => void
263
- confirm: () => Promise<void>
264
- cancel: () => void
265
- onSuccess: Ref<(item: T) => void>
266
- onError: Ref<(error: any) => void>
267
- }
268
- ```
269
-
270
- ---
271
-
272
- ## Validación con Zod
273
-
274
- La validación ocurre en **tiempo de ejecución dentro de `useFormConnector`**, usando schemas Zod que fueron generados en **code-gen time** por el Schema Analyzer a partir del request body del OpenAPI spec.
275
-
276
- ### Cómo funciona internamente `useFormConnector`
277
-
278
- ```ts
279
- // src/generators/shared/runtime/useFormConnector.ts (runtime — se copia al proyecto)
280
- import { ref, computed } from 'vue'
281
- import type { ZodSchema } from 'zod'
282
-
283
- export function useFormConnector<T>(composable: Function, options: FormConnectorOptions<T>) {
284
- const model = ref<Partial<T>>({})
285
- const errors = ref<Record<string, string>>({})
286
- const loading = ref(false)
287
-
288
- async function submit() {
289
- // 1. Validar con Zod antes de cualquier llamada HTTP
290
- const result = options.schema.safeParse(model.value)
291
-
292
- if (!result.success) {
293
- // Zod devuelve errores por campo: { name: ['Required'], status: ['Invalid value'] }
294
- const fieldErrors = result.error.flatten().fieldErrors
295
- // Convertir array de mensajes a string único por campo
296
- errors.value = Object.fromEntries(
297
- Object.entries(fieldErrors).map(([key, msgs]) => [key, msgs?.[0] ?? 'Invalid'])
298
- )
299
- return // No se hace la llamada HTTP
300
- }
301
-
302
- errors.value = {}
303
- loading.value = true
304
- try {
305
- const response = await composable(result.data)
306
- options.onSuccess?.(response)
307
- } catch (err) {
308
- options.onError?.(err)
309
- } finally {
310
- loading.value = false
311
- }
312
- }
313
-
314
- return { model, errors, loading, submit, /* ... */ }
315
- }
316
- ```
317
-
318
- ### Cómo el Schema Analyzer genera el schema Zod
319
-
320
- El `schema-field-mapper.ts` convierte cada propiedad del request body OpenAPI a su equivalente Zod:
321
-
322
- | OpenAPI | Zod generado |
323
- |------------------------------------|----------------------------------------------|
324
- | `type: string` + required | `z.string().min(1)` |
325
- | `type: string` + opcional | `z.string().optional()` |
326
- | `type: string, minLength: 3` | `z.string().min(3)` |
327
- | `type: string, maxLength: 100` | `z.string().max(100)` |
328
- | `type: string, format: email` | `z.string().email()` |
329
- | `type: string, format: uri` | `z.string().url()` |
330
- | `type: integer / number` + req. | `z.number().int()` / `z.number()` |
331
- | `type: boolean` | `z.boolean()` |
332
- | `enum: ['a','b']` + required | `z.enum(['a', 'b'])` |
333
- | `enum: ['a','b']` + opcional | `z.enum(['a', 'b']).optional()` |
334
- | `type: array, items: { type: string }` | `z.array(z.string())` |
335
- | `type: array, minItems: 1` | `z.array(z.string()).min(1)` |
336
- | `readOnly: true` | campo excluido del schema (no se valida) |
337
-
338
- ### Mensajes de error — 3 niveles
339
-
340
- **Nivel 1 — Mensajes por defecto de Zod** (en inglés, zero config):
341
- ```
342
- { name: 'String must contain at least 1 character(s)' }
343
- ```
344
-
345
- **Nivel 2 — `errorMap` global** (traducciones genéricas para todo el proyecto):
346
- ```ts
347
- // plugins/zod-i18n.ts (el developer crea esto en su proyecto Nuxt)
348
- import { z } from 'zod'
349
-
350
- z.setErrorMap((issue, ctx) => {
351
- switch (issue.code) {
352
- case 'too_small':
353
- if (issue.type === 'string' && issue.minimum === 1)
354
- return { message: 'Este campo es obligatorio' }
355
- return { message: `Mínimo ${issue.minimum} caracteres` }
356
- case 'invalid_enum_value':
357
- return { message: `Valor no válido` }
358
- case 'invalid_type':
359
- if (issue.received === 'undefined')
360
- return { message: 'Este campo es obligatorio' }
361
- default:
362
- return { message: ctx.defaultError }
363
- }
364
- })
365
- ```
366
-
367
- **Nivel 3 — Mensajes por campo** (en `components/nxh/pets/index.ts`, máximo control):
368
- ```ts
369
- export const config = {
370
- fields: {
371
- name: {
372
- label: 'Nombre',
373
- errors: {
374
- required: 'El nombre es obligatorio',
375
- min: 'El nombre debe tener al menos 1 carácter',
376
- max: 'El nombre no puede superar 100 caracteres',
377
- }
378
- },
379
- status: {
380
- label: 'Estado',
381
- errors: {
382
- invalid_enum_value: 'Selecciona un estado válido',
383
- }
384
- }
385
- }
386
- }
387
- ```
388
-
389
- `useFormConnector` merge los mensajes del config sobre los de Zod: si el campo tiene `errors.required`, ese mensaje reemplaza al de Zod para ese campo. Los adaptadores UI (NuxtUI, PrimeVue) solo leen `errors.value[campo]` — framework-agnostic.
390
-
391
- ---
392
-
393
- ## Integración con paginación
394
-
395
- Si el endpoint del list tiene respuesta paginada (detectada por el Schema Analyzer al ver campos como `total`, `totalPages` en la respuesta, o configuración en `nxh.config`), `useListConnector` activa automáticamente `paginated: true` en el composable `useAsyncData` correspondiente, reutilizando el sistema de paginación ya implementado en `src/generators/shared/runtime/pagination.ts`.
396
-
397
- ---
398
-
399
- ## Estructura de archivos a crear
400
-
401
- ### Archivos nuevos en `src/` (en el generador)
402
-
403
- ```
404
- src/
405
- generators/
406
- components/ ← NUEVO directorio
407
- schema-analyzer/
408
- index.ts ← entry point del analyzer
409
- types.ts ← ResourceMap, Intent, FieldDef, etc.
410
- intent-detector.ts ← detecta intent por HTTP+path+schema
411
- resource-grouper.ts ← agrupa endpoints en recursos por tag/path
412
- schema-field-mapper.ts ← OpenAPI schema → FormFieldDef[]
413
- openapi-reader.ts ← lee y parsea el YAML/JSON del spec
414
- connector-generator/
415
- index.ts ← entry point del connector generator
416
- types.ts ← ConnectorInfo, SubConnectorDef, etc.
417
- templates.ts ← plantillas de texto para los .ts generados
418
- generator.ts ← orquesta la generación de connectors
419
- shared/
420
- naming.ts ← convenciones de nombres (usePetsConnector, etc.)
421
- paths.ts ← resolución de paths de output
422
- ```
423
-
424
- ### Archivos runtime nuevos en `src/generators/shared/runtime/`
425
-
426
- ```
427
- src/generators/shared/runtime/
428
- useListConnector.ts ← NUEVO — conector genérico para listas
429
- useDetailConnector.ts ← NUEVO — conector genérico para detalle
430
- useFormConnector.ts ← NUEVO — conector genérico para formularios (usa Zod)
431
- useDeleteConnector.ts ← NUEVO — conector genérico para delete
432
- zod-error-merger.ts ← NUEVO — merge errores Zod con config.fields.errors
433
- ```
434
-
435
- Estos archivos son **runtime** — se copian al proyecto del usuario igual que `apiHelpers.ts` y `pagination.ts`.
436
-
437
- > **Dependencia de runtime:** `zod` debe estar instalado en el proyecto del usuario (`npm i zod`). Se añade como `peerDependency` en el package.json del generador.
438
-
439
- ### Archivos que se generan en el proyecto del usuario
440
-
441
- ```
442
- composables/
443
- connectors/
444
- usePetsConnector.ts ← AUTO-GENERATED, regenerable
445
- useCategoryConnector.ts
446
- useOrderConnector.ts
447
- ...
448
- ```
449
-
450
- ---
451
-
452
- ## Plan de implementación
453
-
454
- ### Fase 1 — Schema Analyzer
455
-
456
- **Archivos:** `src/generators/components/schema-analyzer/`
457
-
458
- 1. `openapi-reader.ts` — Leer YAML/JSON con `js-yaml` o `@apidevtools/swagger-parser`. Producir el spec como objeto tipado
459
- 2. `types.ts` — Definir `ResourceMap`, `ResourceInfo`, `EndpointInfo`, `Intent`, `FieldDef`, `ColumnDef`
460
- 3. `intent-detector.ts` — Lógica de detección:
461
- - Señal 1: HTTP method (`GET` → list/detail, `POST` → create, `PUT/PATCH` → update, `DELETE` → delete)
462
- - Señal 2: Path pattern (`/pets` sin path params → list candidate, `/pets/{id}` → detail/update/delete)
463
- - Señal 3: Response schema (array → `list`, object → `detail`)
464
- - Override: `nxh.config.js` `components['GET /pets/search'] = { intent: 'list' }`
465
- 4. `resource-grouper.ts` — Agrupa endpoints con el mismo tag (prioridad) o path prefix (fallback) en un `ResourceInfo`
466
- 5. `schema-field-mapper.ts` — Convierte OpenAPI property schema → `FormFieldDef` Y genera el **string de código Zod** correspondiente (ver tabla de mapeo en la sección [Validación con Zod](#validación-con-zod))
467
-
468
- ### Fase 2 — Runtime Connectors
469
-
470
- **Archivos:** `src/generators/shared/runtime/`
471
-
472
- 1. `useListConnector.ts` — Recibe composable `useAsyncData` + opciones, devuelve `ListConnectorReturn`
473
- 2. `useDetailConnector.ts` — Recibe composable `useAsyncData`, devuelve `DetailConnectorReturn`
474
- 3. `useFormConnector.ts` — Recibe composable `useAsyncData` mutación + Zod schema + `loadWith` opcional:
475
- - Llama `schema.safeParse(model.value)` en `submit()` antes de la llamada HTTP
476
- - Convierte `error.flatten().fieldErrors` a `Record<string, string>`
477
- - Merge con mensajes de `config.fields[key].errors` cuando existen
478
- - Expone `errors: Ref<Record<string, string>>` — framework-agnostic
479
- 4. `useDeleteConnector.ts` — Recibe composable `useAsyncData` delete, devuelve `DeleteConnectorReturn`
480
- 5. `zod-error-merger.ts` — Helper que merge errores Zod con overrides del `config.fields.errors`
481
-
482
- ### Fase 3 — Connector Generator
483
-
484
- **Archivos:** `src/generators/components/connector-generator/`
485
-
486
- 1. `types.ts` — `ConnectorInfo` (todo lo necesario para generar el `.ts`)
487
- 2. `templates.ts` — Funciones que producen el string del archivo `use{Resource}Connector.ts`
488
- 3. `generator.ts` — Toma el `ResourceMap` de Fase 1, genera un archivo por recurso
489
- 4. Integración en `src/index.ts` con nuevo comando/opción CLI
490
-
491
- ### Fase 4 — Integración en CLI
492
-
493
- Nuevo flag en `nxh generate`:
494
- ```
495
- --components <type> Generate UI components: connectors, nuxtui, primevue
496
- ```
497
-
498
- O nuevo comando separado:
499
- ```
500
- nxh components --input swagger.yaml --output ./components/nxh --ui nuxtui
501
- ```
502
-
503
- ---
504
-
505
- ## Ejemplos de uso por el developer
506
-
507
- ### Uso básico — tabla simple
508
-
509
- ```vue
510
- <!-- pages/pets.vue -->
511
- <script setup>
512
- import { usePetsConnector } from '~/composables/connectors/usePetsConnector'
513
-
514
- const { table } = usePetsConnector()
515
- </script>
516
-
517
- <template>
518
- <!-- Con NuxtUI -->
519
- <UTable :rows="table.rows.value" :columns="table.columns.value" :loading="table.loading.value" />
520
- </template>
521
- ```
522
-
523
- ### Uso avanzado — CRUD completo custom
524
-
525
- ```vue
526
- <!-- pages/pets-admin.vue -->
527
- <script setup>
528
- import { usePetsConnector } from '~/composables/connectors/usePetsConnector'
529
-
530
- const { table, createForm, updateForm, deleteAction } = usePetsConnector()
531
-
532
- // Hook personalizado
533
- createForm.onSuccess.value = () => {
534
- table.refresh()
535
- useToast().add({ title: 'Pet created!' })
536
- }
537
- </script>
538
-
539
- <template>
540
- <!-- El developer monta su propio HTML con la lógica del connector -->
541
- <MyCustomTable v-bind="table" @edit="updateForm.setValues" @delete="deleteAction.setTarget" />
542
- <MyCustomForm v-bind="createForm" />
543
- </template>
544
- ```
545
-
546
- ### Traducción de columnas mediante index.ts
547
-
548
- ```ts
549
- // components/nxh/pets/index.ts (generado una vez, editado por el developer)
550
- export const config = {
551
- columns: {
552
- name: { label: 'Nombre' },
553
- status: { label: 'Estado' },
554
- createdAt: { label: 'Fecha de creación' },
555
- },
556
- fields: {
557
- status: {
558
- type: 'select',
559
- options: [
560
- { label: 'Disponible', value: 'available' },
561
- { label: 'Vendido', value: 'sold' },
562
- ]
563
- }
564
- },
565
- showReadonlyFields: false,
566
- }
567
- ```
568
-
569
- El generated `usePetsConnector.ts` importa este config y lo aplica sobre los `ColumnDef` y `FormFieldDef` antes de exponerlos.