nuxt-openapi-hyperfetch 0.1.6-alpha.1 → 0.1.8-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 (79) hide show
  1. package/CONTRIBUTING.md +291 -292
  2. package/INSTRUCTIONS.md +327 -327
  3. package/LICENSE +202 -202
  4. package/README.md +231 -227
  5. package/dist/cli/logger.d.ts +26 -0
  6. package/dist/cli/logger.js +36 -0
  7. package/dist/cli/logo.js +6 -6
  8. package/dist/generators/components/connector-generator/generator.d.ts +12 -0
  9. package/dist/generators/components/connector-generator/generator.js +116 -0
  10. package/dist/generators/components/connector-generator/templates.d.ts +18 -0
  11. package/dist/generators/components/connector-generator/templates.js +222 -0
  12. package/dist/generators/components/connector-generator/types.d.ts +32 -0
  13. package/dist/generators/components/connector-generator/types.js +7 -0
  14. package/dist/generators/components/schema-analyzer/index.d.ts +17 -0
  15. package/dist/generators/components/schema-analyzer/index.js +20 -0
  16. package/dist/generators/components/schema-analyzer/intent-detector.d.ts +17 -0
  17. package/dist/generators/components/schema-analyzer/intent-detector.js +143 -0
  18. package/dist/generators/components/schema-analyzer/openapi-reader.d.ts +11 -0
  19. package/dist/generators/components/schema-analyzer/openapi-reader.js +76 -0
  20. package/dist/generators/components/schema-analyzer/resource-grouper.d.ts +6 -0
  21. package/dist/generators/components/schema-analyzer/resource-grouper.js +132 -0
  22. package/dist/generators/components/schema-analyzer/schema-field-mapper.d.ts +35 -0
  23. package/dist/generators/components/schema-analyzer/schema-field-mapper.js +220 -0
  24. package/dist/generators/components/schema-analyzer/types.d.ts +156 -0
  25. package/dist/generators/components/schema-analyzer/types.js +7 -0
  26. package/dist/generators/nuxt-server/generator.d.ts +2 -1
  27. package/dist/generators/nuxt-server/generator.js +21 -21
  28. package/dist/generators/shared/runtime/apiHelpers.d.ts +98 -41
  29. package/dist/generators/shared/runtime/apiHelpers.js +97 -104
  30. package/dist/generators/shared/runtime/pagination.d.ts +168 -0
  31. package/dist/generators/shared/runtime/pagination.js +179 -0
  32. package/dist/generators/shared/runtime/useDeleteConnector.d.ts +16 -0
  33. package/dist/generators/shared/runtime/useDeleteConnector.js +93 -0
  34. package/dist/generators/shared/runtime/useDetailConnector.d.ts +14 -0
  35. package/dist/generators/shared/runtime/useDetailConnector.js +50 -0
  36. package/dist/generators/shared/runtime/useFormConnector.d.ts +19 -0
  37. package/dist/generators/shared/runtime/useFormConnector.js +113 -0
  38. package/dist/generators/shared/runtime/useListConnector.d.ts +25 -0
  39. package/dist/generators/shared/runtime/useListConnector.js +125 -0
  40. package/dist/generators/shared/runtime/zod-error-merger.d.ts +23 -0
  41. package/dist/generators/shared/runtime/zod-error-merger.js +106 -0
  42. package/dist/generators/shared/templates/api-callbacks-plugin.js +54 -11
  43. package/dist/generators/shared/templates/api-pagination-plugin.d.ts +51 -0
  44. package/dist/generators/shared/templates/api-pagination-plugin.js +152 -0
  45. package/dist/generators/use-async-data/generator.d.ts +2 -1
  46. package/dist/generators/use-async-data/generator.js +14 -14
  47. package/dist/generators/use-async-data/runtime/useApiAsyncData.js +130 -17
  48. package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +103 -13
  49. package/dist/generators/use-async-data/templates.js +20 -14
  50. package/dist/generators/use-fetch/generator.d.ts +2 -1
  51. package/dist/generators/use-fetch/generator.js +12 -12
  52. package/dist/generators/use-fetch/runtime/useApiRequest.js +149 -40
  53. package/dist/generators/use-fetch/templates.js +14 -14
  54. package/dist/index.js +25 -0
  55. package/dist/module/index.d.ts +4 -0
  56. package/dist/module/index.js +93 -0
  57. package/dist/module/types.d.ts +27 -0
  58. package/dist/module/types.js +1 -0
  59. package/docs/API-REFERENCE.md +886 -887
  60. package/eslint.config.js +13 -0
  61. package/package.json +23 -2
  62. package/src/cli/config.ts +140 -140
  63. package/src/cli/logger.ts +124 -66
  64. package/src/cli/logo.ts +25 -25
  65. package/src/cli/types.ts +50 -50
  66. package/src/generators/nuxt-server/generator.ts +272 -270
  67. package/src/generators/shared/runtime/apiHelpers.ts +507 -481
  68. package/src/generators/shared/templates/api-callbacks-plugin.ts +352 -352
  69. package/src/generators/use-async-data/generator.ts +205 -204
  70. package/src/generators/use-async-data/runtime/useApiAsyncData.ts +229 -214
  71. package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +245 -230
  72. package/src/generators/use-async-data/templates.ts +257 -250
  73. package/src/generators/use-fetch/generator.ts +170 -169
  74. package/src/generators/use-fetch/runtime/useApiRequest.ts +234 -234
  75. package/src/generators/use-fetch/templates.ts +214 -214
  76. package/src/index.ts +265 -265
  77. package/src/module/index.ts +133 -0
  78. package/src/module/types.ts +31 -0
  79. package/src/generators/tanstack-query/generator.ts +0 -11
@@ -1,4 +1,11 @@
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 { ref, computed } from 'vue';
1
7
  import { getGlobalHeaders, getGlobalBaseUrl, applyPick, mergeCallbacks, } from '../../shared/runtime/apiHelpers.js';
8
+ import { getGlobalApiPagination, buildPaginationRequest, extractPaginationMetaFromBody, extractPaginationMetaFromHeaders, unwrapDataKey, } from '../../shared/runtime/pagination.js';
2
9
  /**
3
10
  * Generic wrapper for API calls using Nuxt's useAsyncData
4
11
  * Supports:
@@ -9,9 +16,21 @@ import { getGlobalHeaders, getGlobalBaseUrl, applyPick, mergeCallbacks, } from '
9
16
  * - Watch pattern for reactive parameters
10
17
  */
11
18
  export function useApiAsyncData(key, url, options) {
12
- const { method = 'GET', body, headers = {}, params, transform, pick, onRequest, onSuccess, onError, onFinish, skipGlobalCallbacks, immediate = true, lazy = false, server = true, dedupe = 'cancel', watch: watchOption = true, ...restOptions } = options || {};
19
+ const { method = 'GET', body, headers = {}, params, baseURL, cacheKey, transform, pick, onRequest, onSuccess, onError, onFinish, skipGlobalCallbacks, immediate = true, lazy = false, server = true, dedupe = 'cancel', watch: watchOption = true, paginated, initialPage, initialPerPage, paginationConfig, ...restOptions } = options || {};
20
+ // ---------------------------------------------------------------------------
21
+ // Pagination setup
22
+ // ---------------------------------------------------------------------------
23
+ const activePaginationConfig = paginationConfig ?? (paginated ? getGlobalApiPagination() : null);
24
+ const page = ref(initialPage ?? activePaginationConfig?.request.defaults.page ?? 1);
25
+ const perPage = ref(initialPerPage ?? activePaginationConfig?.request.defaults.perPage ?? 20);
26
+ const paginationState = ref({
27
+ currentPage: page.value,
28
+ totalPages: 0,
29
+ total: 0,
30
+ perPage: perPage.value,
31
+ });
13
32
  // Resolve base URL once at setup time (not inside fetchFn to avoid warning on every request)
14
- const resolvedBaseURL = restOptions.baseURL || getGlobalBaseUrl();
33
+ const resolvedBaseURL = baseURL || getGlobalBaseUrl();
15
34
  if (!resolvedBaseURL) {
16
35
  console.warn('[nuxt-openapi-hyperfetch] No baseURL configured. Set runtimeConfig.public.apiBaseUrl in nuxt.config.ts or pass baseURL in options.');
17
36
  }
@@ -29,13 +48,27 @@ export function useApiAsyncData(key, url, options) {
29
48
  ? [() => params]
30
49
  : []
31
50
  : []),
51
+ // Add pagination refs so page/perPage changes trigger re-fetch
52
+ ...(paginated ? [page, perPage] : []),
32
53
  ];
54
+ // Build a reactive cache key: composableName + resolved URL + serialized query params
55
+ // This ensures distinct params produce distinct keys — preventing cache collisions
56
+ const computedKey = () => {
57
+ if (cacheKey)
58
+ return cacheKey;
59
+ const resolvedUrl = typeof url === 'function' ? url() : url;
60
+ const resolvedParams = toValue(params);
61
+ const paramsSuffix = resolvedParams && typeof resolvedParams === 'object' && Object.keys(resolvedParams).length > 0
62
+ ? '-' + JSON.stringify(resolvedParams)
63
+ : '';
64
+ return `${key}-${resolvedUrl}${paramsSuffix}`;
65
+ };
33
66
  // Fetch function for useAsyncData
34
67
  const fetchFn = async () => {
35
68
  // Get URL value for merging callbacks
36
69
  const finalUrl = typeof url === 'function' ? url() : url;
37
70
  // Merge local and global callbacks
38
- const mergedCallbacks = mergeCallbacks(finalUrl, { onRequest, onSuccess, onError, onFinish }, skipGlobalCallbacks);
71
+ const mergedCallbacks = mergeCallbacks(finalUrl, method, { onRequest, onSuccess, onError, onFinish }, skipGlobalCallbacks);
39
72
  try {
40
73
  // Get global headers
41
74
  const globalHeaders = getGlobalHeaders();
@@ -63,23 +96,81 @@ export function useApiAsyncData(key, url, options) {
63
96
  ...modifications.headers,
64
97
  };
65
98
  }
66
- if (modifications.params !== undefined) {
99
+ if (modifications.query !== undefined) {
67
100
  modifiedContext.params = {
68
101
  ...modifiedContext.params,
69
- ...modifications.params,
102
+ ...modifications.query,
70
103
  };
71
104
  }
72
105
  }
73
106
  }
74
107
  // Make the request with $fetch — toValue() unrefs any Ref/ComputedRef
75
- let data = await $fetch(modifiedContext.url, {
76
- method: modifiedContext.method,
77
- headers: modifiedContext.headers,
78
- body: toValue(modifiedContext.body),
79
- params: toValue(modifiedContext.params),
80
- ...(resolvedBaseURL ? { baseURL: resolvedBaseURL } : {}),
81
- ...restOptions,
82
- });
108
+ // For paginated requests with metaSource:'headers', use $fetch.raw for header access
109
+ let data;
110
+ if (paginated && activePaginationConfig) {
111
+ // Inject pagination params into the correct location
112
+ const paginationPayload = buildPaginationRequest(page.value, perPage.value, activePaginationConfig);
113
+ const paginatedQuery = { ...toValue(modifiedContext.params), ...paginationPayload.query };
114
+ const paginatedBody = paginationPayload.body
115
+ ? { ...(toValue(modifiedContext.body) ?? {}), ...paginationPayload.body }
116
+ : toValue(modifiedContext.body);
117
+ const paginatedHeaders = paginationPayload.headers
118
+ ? { ...modifiedContext.headers, ...paginationPayload.headers }
119
+ : modifiedContext.headers;
120
+ if (activePaginationConfig.meta.metaSource === 'headers') {
121
+ // Need raw fetch to access response headers
122
+ const response = await $fetch.raw(modifiedContext.url, {
123
+ method: modifiedContext.method,
124
+ headers: paginatedHeaders,
125
+ body: paginatedBody,
126
+ params: paginatedQuery,
127
+ ...(resolvedBaseURL ? { baseURL: resolvedBaseURL } : {}),
128
+ ...restOptions,
129
+ });
130
+ // Extract pagination meta from headers
131
+ const meta = extractPaginationMetaFromHeaders(response.headers, activePaginationConfig);
132
+ if (meta.total !== undefined)
133
+ paginationState.value.total = meta.total;
134
+ if (meta.totalPages !== undefined)
135
+ paginationState.value.totalPages = meta.totalPages;
136
+ if (meta.currentPage !== undefined)
137
+ paginationState.value.currentPage = meta.currentPage;
138
+ if (meta.perPage !== undefined)
139
+ paginationState.value.perPage = meta.perPage;
140
+ data = unwrapDataKey(response._data, activePaginationConfig);
141
+ }
142
+ else {
143
+ // metaSource: 'body' — extract after receiving the data
144
+ const rawData = await $fetch(modifiedContext.url, {
145
+ method: modifiedContext.method,
146
+ headers: paginatedHeaders,
147
+ body: paginatedBody,
148
+ params: paginatedQuery,
149
+ ...(resolvedBaseURL ? { baseURL: resolvedBaseURL } : {}),
150
+ ...restOptions,
151
+ });
152
+ const meta = extractPaginationMetaFromBody(rawData, activePaginationConfig);
153
+ if (meta.total !== undefined)
154
+ paginationState.value.total = meta.total;
155
+ if (meta.totalPages !== undefined)
156
+ paginationState.value.totalPages = meta.totalPages;
157
+ if (meta.currentPage !== undefined)
158
+ paginationState.value.currentPage = meta.currentPage;
159
+ if (meta.perPage !== undefined)
160
+ paginationState.value.perPage = meta.perPage;
161
+ data = unwrapDataKey(rawData, activePaginationConfig);
162
+ }
163
+ }
164
+ else {
165
+ data = await $fetch(modifiedContext.url, {
166
+ method: modifiedContext.method,
167
+ headers: modifiedContext.headers,
168
+ body: toValue(modifiedContext.body),
169
+ params: toValue(modifiedContext.params),
170
+ ...(resolvedBaseURL ? { baseURL: resolvedBaseURL } : {}),
171
+ ...restOptions,
172
+ });
173
+ }
83
174
  // Apply pick if provided
84
175
  if (pick) {
85
176
  data = applyPick(data, pick);
@@ -116,13 +207,35 @@ export function useApiAsyncData(key, url, options) {
116
207
  }
117
208
  }
118
209
  };
119
- // Use Nuxt's useAsyncData
120
- const result = useAsyncData(key, fetchFn, {
210
+ // Use Nuxt's useAsyncData with a computed key for proper cache isolation per params
211
+ const result = useAsyncData(computedKey, fetchFn, {
121
212
  immediate,
122
213
  lazy,
123
214
  server,
124
215
  dedupe,
125
- watch: watchSources.length > 0 ? watchSources : undefined,
216
+ watch: watchOption === false ? [] : watchSources,
126
217
  });
127
- return result;
218
+ if (!paginated)
219
+ return result;
220
+ // Pagination computed helpers
221
+ const hasNextPage = computed(() => paginationState.value.currentPage < paginationState.value.totalPages);
222
+ const hasPrevPage = computed(() => paginationState.value.currentPage > 1);
223
+ const goToPage = (n) => { page.value = n; };
224
+ const nextPage = () => { if (hasNextPage.value)
225
+ goToPage(page.value + 1); };
226
+ const prevPage = () => { if (hasPrevPage.value)
227
+ goToPage(page.value - 1); };
228
+ const setPerPage = (n) => { perPage.value = n; page.value = 1; };
229
+ return {
230
+ ...result,
231
+ pagination: computed(() => ({
232
+ ...paginationState.value,
233
+ hasNextPage: hasNextPage.value,
234
+ hasPrevPage: hasPrevPage.value,
235
+ })),
236
+ goToPage,
237
+ nextPage,
238
+ prevPage,
239
+ setPerPage,
240
+ };
128
241
  }
@@ -1,4 +1,13 @@
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 { ref, computed } from 'vue';
1
9
  import { getGlobalHeaders, getGlobalBaseUrl, applyPick, mergeCallbacks, } from '../../shared/runtime/apiHelpers.js';
10
+ import { getGlobalApiPagination, buildPaginationRequest, extractPaginationMetaFromBody, extractPaginationMetaFromHeaders, unwrapDataKey, } from '../../shared/runtime/pagination.js';
2
11
  /**
3
12
  * Generic wrapper for API calls using Nuxt's useAsyncData - RAW VERSION
4
13
  * Returns full response with headers and status information
@@ -12,9 +21,21 @@ import { getGlobalHeaders, getGlobalBaseUrl, applyPick, mergeCallbacks, } from '
12
21
  * - Watch pattern for reactive parameters
13
22
  */
14
23
  export function useApiAsyncDataRaw(key, url, options) {
15
- const { method = 'GET', body, headers = {}, params, transform, pick, onRequest, onSuccess, onError, onFinish, skipGlobalCallbacks, immediate = true, lazy = false, server = true, dedupe = 'cancel', ...restOptions } = options || {};
24
+ const { method = 'GET', body, headers = {}, params, baseURL, cacheKey, transform, pick, onRequest, onSuccess, onError, onFinish, skipGlobalCallbacks, immediate = true, lazy = false, server = true, dedupe = 'cancel', paginated, initialPage, initialPerPage, paginationConfig, ...restOptions } = options || {};
25
+ // ---------------------------------------------------------------------------
26
+ // Pagination setup
27
+ // ---------------------------------------------------------------------------
28
+ const activePaginationConfig = paginationConfig ?? (paginated ? getGlobalApiPagination() : null);
29
+ const page = ref(initialPage ?? activePaginationConfig?.request.defaults.page ?? 1);
30
+ const perPage = ref(initialPerPage ?? activePaginationConfig?.request.defaults.perPage ?? 20);
31
+ const paginationState = ref({
32
+ currentPage: page.value,
33
+ totalPages: 0,
34
+ total: 0,
35
+ perPage: perPage.value,
36
+ });
16
37
  // Resolve base URL once at setup time (not inside fetchFn to avoid warning on every request)
17
- const resolvedBaseURL = restOptions.baseURL || getGlobalBaseUrl();
38
+ const resolvedBaseURL = baseURL || getGlobalBaseUrl();
18
39
  if (!resolvedBaseURL) {
19
40
  console.warn('[nuxt-openapi-hyperfetch] No baseURL configured. Set runtimeConfig.public.apiBaseUrl in nuxt.config.ts or pass baseURL in options.');
20
41
  }
@@ -23,13 +44,27 @@ export function useApiAsyncDataRaw(key, url, options) {
23
44
  ...(typeof url === 'function' ? [url] : []),
24
45
  ...(body && typeof body === 'object' ? [() => body] : []),
25
46
  ...(params && typeof params === 'object' ? [() => params] : []),
47
+ // Add pagination refs so page/perPage changes trigger re-fetch
48
+ ...(paginated ? [page, perPage] : []),
26
49
  ];
50
+ // Build a reactive cache key: composableName + resolved URL + serialized query params
51
+ // This ensures distinct params produce distinct keys — preventing cache collisions
52
+ const computedKey = () => {
53
+ if (cacheKey)
54
+ return cacheKey;
55
+ const resolvedUrl = typeof url === 'function' ? url() : url;
56
+ const resolvedParams = toValue(params);
57
+ const paramsSuffix = resolvedParams && typeof resolvedParams === 'object' && Object.keys(resolvedParams).length > 0
58
+ ? '-' + JSON.stringify(resolvedParams)
59
+ : '';
60
+ return `${key}-${resolvedUrl}${paramsSuffix}`;
61
+ };
27
62
  // Fetch function for useAsyncData
28
63
  const fetchFn = async () => {
29
64
  // Get URL value for merging callbacks
30
65
  const finalUrl = typeof url === 'function' ? url() : url;
31
66
  // Merge local and global callbacks
32
- const mergedCallbacks = mergeCallbacks(finalUrl, { onRequest, onSuccess, onError, onFinish }, skipGlobalCallbacks);
67
+ const mergedCallbacks = mergeCallbacks(finalUrl, method, { onRequest, onSuccess, onError, onFinish }, skipGlobalCallbacks);
33
68
  try {
34
69
  // Get global headers
35
70
  const globalHeaders = getGlobalHeaders();
@@ -57,25 +92,58 @@ export function useApiAsyncDataRaw(key, url, options) {
57
92
  ...modifications.headers,
58
93
  };
59
94
  }
60
- if (modifications.params !== undefined) {
95
+ if (modifications.query !== undefined) {
61
96
  modifiedContext.params = {
62
97
  ...modifiedContext.params,
63
- ...modifications.params,
98
+ ...modifications.query,
64
99
  };
65
100
  }
66
101
  }
67
102
  }
103
+ // Build final request params, injecting pagination if enabled
104
+ let finalQuery = modifiedContext.params;
105
+ let finalBody = modifiedContext.body;
106
+ let finalHeaders = modifiedContext.headers;
107
+ if (paginated && activePaginationConfig) {
108
+ const paginationPayload = buildPaginationRequest(page.value, perPage.value, activePaginationConfig);
109
+ if (paginationPayload.query)
110
+ finalQuery = { ...finalQuery, ...paginationPayload.query };
111
+ if (paginationPayload.body)
112
+ finalBody = { ...(finalBody ?? {}), ...paginationPayload.body };
113
+ if (paginationPayload.headers)
114
+ finalHeaders = { ...finalHeaders, ...paginationPayload.headers };
115
+ }
68
116
  // Make the request with $fetch.raw to get full response
69
117
  const response = await $fetch.raw(modifiedContext.url, {
70
118
  method: modifiedContext.method,
71
- headers: modifiedContext.headers,
72
- body: modifiedContext.body,
73
- params: modifiedContext.params,
119
+ headers: finalHeaders,
120
+ body: finalBody,
121
+ params: finalQuery,
74
122
  ...(resolvedBaseURL ? { baseURL: resolvedBaseURL } : {}),
75
123
  ...restOptions,
76
124
  });
77
- // Extract data from response
78
- let data = response._data;
125
+ // Extract pagination meta from headers or body
126
+ if (paginated && activePaginationConfig) {
127
+ let meta;
128
+ if (activePaginationConfig.meta.metaSource === 'headers') {
129
+ meta = extractPaginationMetaFromHeaders(response.headers, activePaginationConfig);
130
+ }
131
+ else {
132
+ meta = extractPaginationMetaFromBody(response._data, activePaginationConfig);
133
+ }
134
+ if (meta.total !== undefined)
135
+ paginationState.value.total = meta.total;
136
+ if (meta.totalPages !== undefined)
137
+ paginationState.value.totalPages = meta.totalPages;
138
+ if (meta.currentPage !== undefined)
139
+ paginationState.value.currentPage = meta.currentPage;
140
+ if (meta.perPage !== undefined)
141
+ paginationState.value.perPage = meta.perPage;
142
+ }
143
+ // Extract data from response (unwrap dataKey for paginated responses)
144
+ let data = paginated && activePaginationConfig
145
+ ? unwrapDataKey(response._data, activePaginationConfig)
146
+ : response._data;
79
147
  // Apply pick if provided (only to data)
80
148
  if (pick) {
81
149
  data = applyPick(data, pick);
@@ -120,13 +188,35 @@ export function useApiAsyncDataRaw(key, url, options) {
120
188
  }
121
189
  }
122
190
  };
123
- // Use Nuxt's useAsyncData
124
- const result = useAsyncData(key, fetchFn, {
191
+ // Use Nuxt's useAsyncData with a computed key for proper cache isolation per params
192
+ const result = useAsyncData(computedKey, fetchFn, {
125
193
  immediate,
126
194
  lazy,
127
195
  server,
128
196
  dedupe,
129
197
  watch: watchSources.length > 0 ? watchSources : undefined,
130
198
  });
131
- return result;
199
+ if (!paginated)
200
+ return result;
201
+ // Pagination computed helpers
202
+ const hasNextPage = computed(() => paginationState.value.currentPage < paginationState.value.totalPages);
203
+ const hasPrevPage = computed(() => paginationState.value.currentPage > 1);
204
+ const goToPage = (n) => { page.value = n; };
205
+ const nextPage = () => { if (hasNextPage.value)
206
+ goToPage(page.value + 1); };
207
+ const prevPage = () => { if (hasPrevPage.value)
208
+ goToPage(page.value - 1); };
209
+ const setPerPage = (n) => { perPage.value = n; page.value = 1; };
210
+ return {
211
+ ...result,
212
+ pagination: computed(() => ({
213
+ ...paginationState.value,
214
+ hasNextPage: hasNextPage.value,
215
+ hasPrevPage: hasPrevPage.value,
216
+ })),
217
+ goToPage,
218
+ nextPage,
219
+ prevPage,
220
+ setPerPage,
221
+ };
132
222
  }
@@ -2,18 +2,18 @@
2
2
  * Generate file header with auto-generation warning
3
3
  */
4
4
  function generateFileHeader() {
5
- return `/**
6
- * ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
7
- *
8
- * This file was automatically generated by nuxt-openapi-generator.
9
- * Any manual changes will be overwritten on the next generation.
10
- *
11
- * @generated by nuxt-openapi-generator
12
- * @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
13
- */
14
-
15
- /* eslint-disable */
16
- // @ts-nocheck
5
+ return `/**
6
+ * ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
7
+ *
8
+ * This file was automatically generated by nuxt-openapi-generator.
9
+ * Any manual changes will be overwritten on the next generation.
10
+ *
11
+ * @generated by nuxt-openapi-generator
12
+ * @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
13
+ */
14
+
15
+ /* eslint-disable */
16
+ // @ts-nocheck
17
17
  `;
18
18
  }
19
19
  /**
@@ -117,8 +117,14 @@ function generateFunctionBody(method, isRaw, generateOptions) {
117
117
  // Choose the correct wrapper function
118
118
  const wrapperFunction = isRaw ? 'useApiAsyncDataRaw' : 'useApiAsyncData';
119
119
  const pInit = hasParams ? `\n const p = shallowRef(params)` : '';
120
- return `${description}export const ${composableName} = (${args}) => {${pInit}
121
- return ${wrapperFunction}${responseTypeGeneric}(${key}, ${url}, ${fetchOptions})
120
+ const argsExtraction = hasParams
121
+ ? ` const _hasKey = typeof args[0] === 'string'\n const params = _hasKey ? args[1] : args[0]\n const options = _hasKey ? { cacheKey: args[0], ...args[2] } : args[1]`
122
+ : ` const _hasKey = typeof args[0] === 'string'\n const options = _hasKey ? { cacheKey: args[0], ...args[1] } : args[0]`;
123
+ return `${description}export function ${composableName}(key: string, ${args})
124
+ export function ${composableName}(${args})
125
+ export function ${composableName}(...args: any[]) {
126
+ ${argsExtraction}${pInit}
127
+ return ${wrapperFunction}${responseTypeGeneric}(${key}, ${url}, ${fetchOptions})
122
128
  }`;
123
129
  }
124
130
  /**
@@ -1,5 +1,6 @@
1
1
  import { type GenerateOptions } from './templates.js';
2
+ import { type Logger } from '../../cli/logger.js';
2
3
  /**
3
4
  * Main function to generate useFetch composables
4
5
  */
5
- export declare function generateUseFetchComposables(inputDir: string, outputDir: string, options?: GenerateOptions): Promise<void>;
6
+ export declare function generateUseFetchComposables(inputDir: string, outputDir: string, options?: GenerateOptions, logger?: Logger): Promise<void>;
@@ -5,12 +5,12 @@ import { format } from 'prettier';
5
5
  import { getApiFiles as getApiFilesOfficial, parseApiFile as parseApiFileOfficial, } from './parser.js';
6
6
  import { getApiFiles as getApiFilesHeyApi, parseApiFile as parseApiFileHeyApi, } from '../shared/parsers/heyapi-parser.js';
7
7
  import { generateComposableFile, generateIndexFile } from './templates.js';
8
- import { p, logSuccess, logError } from '../../cli/logger.js';
8
+ import { createClackLogger } from '../../cli/logger.js';
9
9
  /**
10
10
  * Main function to generate useFetch composables
11
11
  */
12
- export async function generateUseFetchComposables(inputDir, outputDir, options) {
13
- const mainSpinner = p.spinner();
12
+ export async function generateUseFetchComposables(inputDir, outputDir, options, logger = createClackLogger()) {
13
+ const mainSpinner = logger.spinner();
14
14
  // Select parser based on chosen backend
15
15
  const getApiFiles = options?.backend === 'heyapi' ? getApiFilesHeyApi : getApiFilesOfficial;
16
16
  const parseApiFile = options?.backend === 'heyapi' ? parseApiFileHeyApi : parseApiFileOfficial;
@@ -31,12 +31,12 @@ export async function generateUseFetchComposables(inputDir, outputDir, options)
31
31
  allMethods.push(...apiInfo.methods);
32
32
  }
33
33
  catch (error) {
34
- logError(`Error parsing ${fileName}: ${String(error)}`);
34
+ logger.log.error(`Error parsing ${fileName}: ${String(error)}`);
35
35
  }
36
36
  }
37
37
  mainSpinner.stop(`Found ${allMethods.length} methods to generate`);
38
38
  if (allMethods.length === 0) {
39
- p.log.warn('No methods found to generate');
39
+ logger.log.warn('No methods found to generate');
40
40
  return;
41
41
  }
42
42
  // 3. Clean and create output directories
@@ -72,27 +72,27 @@ export async function generateUseFetchComposables(inputDir, outputDir, options)
72
72
  for (const method of allMethods) {
73
73
  try {
74
74
  const code = generateComposableFile(method, relativePath, options);
75
- const formattedCode = await formatCode(code);
75
+ const formattedCode = await formatCode(code, logger);
76
76
  const fileName = `${method.composableName}.ts`;
77
77
  const filePath = path.join(composablesDir, fileName);
78
78
  await fs.writeFile(filePath, formattedCode, 'utf-8');
79
79
  successCount++;
80
80
  }
81
81
  catch (error) {
82
- logError(`Error generating ${method.composableName}: ${String(error)}`);
82
+ logger.log.error(`Error generating ${method.composableName}: ${String(error)}`);
83
83
  errorCount++;
84
84
  }
85
85
  }
86
86
  // 7. Generate index.ts
87
87
  const indexCode = generateIndexFile(allMethods.map((m) => m.composableName));
88
- const formattedIndex = await formatCode(indexCode);
88
+ const formattedIndex = await formatCode(indexCode, logger);
89
89
  await fs.writeFile(path.join(outputDir, 'index.ts'), formattedIndex, 'utf-8');
90
90
  mainSpinner.stop(`Generated ${successCount} composables`);
91
91
  // 8. Summary
92
92
  if (errorCount > 0) {
93
- p.log.warn(`Completed with ${errorCount} error(s)`);
93
+ logger.log.warn(`Completed with ${errorCount} error(s)`);
94
94
  }
95
- logSuccess(`Generated ${successCount} useFetch composable(s) in ${outputDir}`);
95
+ logger.log.success(`Generated ${successCount} useFetch composable(s) in ${outputDir}`);
96
96
  }
97
97
  /**
98
98
  * Calculate relative import path from composables to APIs
@@ -113,7 +113,7 @@ function calculateRelativeImportPath(composablesDir, inputDir) {
113
113
  /**
114
114
  * Format code with Prettier
115
115
  */
116
- async function formatCode(code) {
116
+ async function formatCode(code, logger) {
117
117
  try {
118
118
  return await format(code, {
119
119
  parser: 'typescript',
@@ -125,7 +125,7 @@ async function formatCode(code) {
125
125
  });
126
126
  }
127
127
  catch {
128
- p.log.warn('Could not format code with Prettier');
128
+ logger.log.warn('Could not format code with Prettier');
129
129
  return code;
130
130
  }
131
131
  }