nuxt-openapi-hyperfetch 0.1.7-alpha.1 → 0.2.7-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +291 -292
- package/INSTRUCTIONS.md +327 -327
- package/LICENSE +202 -202
- package/README.md +231 -227
- package/dist/cli/logger.d.ts +26 -0
- package/dist/cli/logger.js +36 -0
- package/dist/cli/logo.js +5 -5
- package/dist/generators/components/connector-generator/generator.d.ts +12 -0
- package/dist/generators/components/connector-generator/generator.js +116 -0
- package/dist/generators/components/connector-generator/templates.d.ts +18 -0
- package/dist/generators/components/connector-generator/templates.js +222 -0
- package/dist/generators/components/connector-generator/types.d.ts +32 -0
- package/dist/generators/components/connector-generator/types.js +7 -0
- package/dist/generators/components/schema-analyzer/index.d.ts +17 -0
- package/dist/generators/components/schema-analyzer/index.js +20 -0
- package/dist/generators/components/schema-analyzer/intent-detector.d.ts +17 -0
- package/dist/generators/components/schema-analyzer/intent-detector.js +143 -0
- package/dist/generators/components/schema-analyzer/openapi-reader.d.ts +11 -0
- package/dist/generators/components/schema-analyzer/openapi-reader.js +76 -0
- package/dist/generators/components/schema-analyzer/resource-grouper.d.ts +6 -0
- package/dist/generators/components/schema-analyzer/resource-grouper.js +132 -0
- package/dist/generators/components/schema-analyzer/schema-field-mapper.d.ts +35 -0
- package/dist/generators/components/schema-analyzer/schema-field-mapper.js +220 -0
- package/dist/generators/components/schema-analyzer/types.d.ts +156 -0
- package/dist/generators/components/schema-analyzer/types.js +7 -0
- package/dist/generators/nuxt-server/generator.d.ts +2 -1
- package/dist/generators/nuxt-server/generator.js +21 -21
- package/dist/generators/shared/runtime/apiHelpers.d.ts +81 -41
- package/dist/generators/shared/runtime/apiHelpers.js +97 -104
- package/dist/generators/shared/runtime/pagination.d.ts +168 -0
- package/dist/generators/shared/runtime/pagination.js +179 -0
- package/dist/generators/shared/runtime/useDeleteConnector.d.ts +16 -0
- package/dist/generators/shared/runtime/useDeleteConnector.js +93 -0
- package/dist/generators/shared/runtime/useDetailConnector.d.ts +14 -0
- package/dist/generators/shared/runtime/useDetailConnector.js +50 -0
- package/dist/generators/shared/runtime/useFormConnector.d.ts +19 -0
- package/dist/generators/shared/runtime/useFormConnector.js +113 -0
- package/dist/generators/shared/runtime/useListConnector.d.ts +25 -0
- package/dist/generators/shared/runtime/useListConnector.js +125 -0
- package/dist/generators/shared/runtime/zod-error-merger.d.ts +23 -0
- package/dist/generators/shared/runtime/zod-error-merger.js +106 -0
- package/dist/generators/shared/templates/api-callbacks-plugin.js +54 -11
- package/dist/generators/shared/templates/api-pagination-plugin.d.ts +51 -0
- package/dist/generators/shared/templates/api-pagination-plugin.js +152 -0
- package/dist/generators/use-async-data/generator.d.ts +2 -1
- package/dist/generators/use-async-data/generator.js +14 -14
- package/dist/generators/use-async-data/runtime/useApiAsyncData.js +114 -13
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +88 -10
- package/dist/generators/use-async-data/templates.js +17 -17
- package/dist/generators/use-fetch/generator.d.ts +2 -1
- package/dist/generators/use-fetch/generator.js +12 -12
- package/dist/generators/use-fetch/runtime/useApiRequest.js +149 -40
- package/dist/generators/use-fetch/templates.js +14 -14
- package/dist/index.js +25 -0
- package/dist/module/index.d.ts +4 -0
- package/dist/module/index.js +93 -0
- package/dist/module/types.d.ts +27 -0
- package/dist/module/types.js +1 -0
- package/docs/API-REFERENCE.md +886 -887
- package/docs/generated-components.md +615 -0
- package/docs/headless-composables-ui.md +569 -0
- package/eslint.config.js +13 -0
- package/package.json +29 -2
- package/src/cli/config.ts +140 -140
- package/src/cli/logger.ts +124 -66
- package/src/cli/logo.ts +25 -25
- package/src/cli/types.ts +50 -50
- package/src/generators/components/connector-generator/generator.ts +138 -0
- package/src/generators/components/connector-generator/templates.ts +254 -0
- package/src/generators/components/connector-generator/types.ts +34 -0
- package/src/generators/components/schema-analyzer/index.ts +44 -0
- package/src/generators/components/schema-analyzer/intent-detector.ts +187 -0
- package/src/generators/components/schema-analyzer/openapi-reader.ts +96 -0
- package/src/generators/components/schema-analyzer/resource-grouper.ts +166 -0
- package/src/generators/components/schema-analyzer/schema-field-mapper.ts +268 -0
- package/src/generators/components/schema-analyzer/types.ts +177 -0
- package/src/generators/nuxt-server/generator.ts +272 -270
- package/src/generators/shared/runtime/apiHelpers.ts +535 -507
- package/src/generators/shared/runtime/pagination.ts +323 -0
- package/src/generators/shared/runtime/useDeleteConnector.ts +109 -0
- package/src/generators/shared/runtime/useDetailConnector.ts +64 -0
- package/src/generators/shared/runtime/useFormConnector.ts +139 -0
- package/src/generators/shared/runtime/useListConnector.ts +148 -0
- package/src/generators/shared/runtime/zod-error-merger.ts +119 -0
- package/src/generators/shared/templates/api-callbacks-plugin.ts +399 -352
- package/src/generators/shared/templates/api-pagination-plugin.ts +158 -0
- package/src/generators/use-async-data/generator.ts +205 -204
- package/src/generators/use-async-data/runtime/useApiAsyncData.ts +329 -229
- package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +324 -245
- package/src/generators/use-async-data/templates.ts +257 -257
- package/src/generators/use-fetch/generator.ts +170 -169
- package/src/generators/use-fetch/runtime/useApiRequest.ts +354 -234
- package/src/generators/use-fetch/templates.ts +214 -214
- package/src/index.ts +303 -265
- package/src/module/index.ts +133 -0
- package/src/module/types.ts +31 -0
- package/src/generators/tanstack-query/generator.ts +0 -11
|
@@ -5,18 +5,18 @@ import { getApiFiles as getApiFilesOfficial, parseApiFile as parseApiFileOfficia
|
|
|
5
5
|
import { getApiFiles as getApiFilesHeyApi, parseApiFile as parseApiFileHeyApi, } from '../shared/parsers/heyapi-parser.js';
|
|
6
6
|
import { generateServerRouteFile, generateRouteFilePath, generateRoutesIndexFile, } from './templates.js';
|
|
7
7
|
import { generateAuthContextStub, generateAuthTypesStub, generateTransformerStub, generateTransformerExamples, generateBffReadme, } from './bff-templates.js';
|
|
8
|
-
import {
|
|
8
|
+
import { createClackLogger } from '../../cli/logger.js';
|
|
9
9
|
/**
|
|
10
10
|
* Main function to generate Nuxt Server Routes
|
|
11
11
|
*/
|
|
12
|
-
export async function generateNuxtServerRoutes(inputDir, serverRoutePath, options) {
|
|
13
|
-
const mainSpinner =
|
|
12
|
+
export async function generateNuxtServerRoutes(inputDir, serverRoutePath, 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;
|
|
17
17
|
const enableBff = options?.enableBff ?? false;
|
|
18
18
|
if (enableBff) {
|
|
19
|
-
|
|
19
|
+
logger.log.info('BFF Mode: Enabled (transformers + auth)');
|
|
20
20
|
}
|
|
21
21
|
// 1. Get all API files
|
|
22
22
|
mainSpinner.start('Scanning API files');
|
|
@@ -35,12 +35,12 @@ export async function generateNuxtServerRoutes(inputDir, serverRoutePath, option
|
|
|
35
35
|
allMethods.push(...apiInfo.methods);
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
38
|
-
|
|
38
|
+
logger.log.error(`Error parsing ${fileName}: ${String(error)}`);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
mainSpinner.stop(`Found ${allMethods.length} routes to generate`);
|
|
42
42
|
if (allMethods.length === 0) {
|
|
43
|
-
|
|
43
|
+
logger.log.warn('No methods found to generate');
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
// 3. Clean and create output directory
|
|
@@ -49,7 +49,7 @@ export async function generateNuxtServerRoutes(inputDir, serverRoutePath, option
|
|
|
49
49
|
mainSpinner.stop('Output directory ready');
|
|
50
50
|
// 4. Generate BFF structure if enabled
|
|
51
51
|
if (enableBff) {
|
|
52
|
-
await generateBffStructure(allMethods, serverRoutePath, inputDir);
|
|
52
|
+
await generateBffStructure(allMethods, serverRoutePath, inputDir, logger);
|
|
53
53
|
}
|
|
54
54
|
// 5. Calculate relative import path from server routes to APIs
|
|
55
55
|
const relativePath = calculateRelativeImportPath(serverRoutePath, inputDir);
|
|
@@ -65,7 +65,7 @@ export async function generateNuxtServerRoutes(inputDir, serverRoutePath, option
|
|
|
65
65
|
enableBff: enableBff,
|
|
66
66
|
resource: resource,
|
|
67
67
|
});
|
|
68
|
-
const formattedCode = await formatCode(code);
|
|
68
|
+
const formattedCode = await formatCode(code, logger);
|
|
69
69
|
const routeFilePath = generateRouteFilePath(method);
|
|
70
70
|
const fullPath = path.join(serverRoutePath, routeFilePath);
|
|
71
71
|
// Ensure directory exists
|
|
@@ -74,7 +74,7 @@ export async function generateNuxtServerRoutes(inputDir, serverRoutePath, option
|
|
|
74
74
|
successCount++;
|
|
75
75
|
}
|
|
76
76
|
catch (error) {
|
|
77
|
-
|
|
77
|
+
logger.log.error(`Error generating ${method.path} [${method.httpMethod}]: ${String(error)}`);
|
|
78
78
|
errorCount++;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -83,14 +83,14 @@ export async function generateNuxtServerRoutes(inputDir, serverRoutePath, option
|
|
|
83
83
|
mainSpinner.start('Generating configuration files');
|
|
84
84
|
// Generate routes index (documentation)
|
|
85
85
|
const routesIndexCode = generateRoutesIndexFile(allMethods);
|
|
86
|
-
const formattedRoutesIndex = await formatCode(routesIndexCode);
|
|
86
|
+
const formattedRoutesIndex = await formatCode(routesIndexCode, logger);
|
|
87
87
|
await fs.writeFile(path.join(serverRoutePath, '_routes.ts'), formattedRoutesIndex, 'utf-8');
|
|
88
88
|
mainSpinner.stop('Configuration files generated');
|
|
89
89
|
// 8. Summary and Next Steps
|
|
90
90
|
if (errorCount > 0) {
|
|
91
|
-
|
|
91
|
+
logger.log.warn(`Completed with ${errorCount} error(s)`);
|
|
92
92
|
}
|
|
93
|
-
|
|
93
|
+
logger.log.success(`Generated ${successCount} server route(s) in ${serverRoutePath}`);
|
|
94
94
|
// Build next steps message
|
|
95
95
|
let nextSteps = '1. Configure API_BASE_URL and API_SECRET in your .env\n';
|
|
96
96
|
nextSteps += '2. Update nuxt.config.ts with runtimeConfig (add apiBaseUrl and apiSecret)';
|
|
@@ -103,7 +103,7 @@ export async function generateNuxtServerRoutes(inputDir, serverRoutePath, option
|
|
|
103
103
|
else {
|
|
104
104
|
nextSteps += '\n3. Start your Nuxt dev server and test the routes';
|
|
105
105
|
}
|
|
106
|
-
|
|
106
|
+
logger.note(nextSteps, 'Next steps');
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
109
|
* Calculate relative import path from server routes to APIs
|
|
@@ -118,7 +118,7 @@ function calculateRelativeImportPath(serverRoutePath, inputDir) {
|
|
|
118
118
|
/**
|
|
119
119
|
* Format code with Prettier
|
|
120
120
|
*/
|
|
121
|
-
async function formatCode(code) {
|
|
121
|
+
async function formatCode(code, logger) {
|
|
122
122
|
try {
|
|
123
123
|
return await format(code, {
|
|
124
124
|
parser: 'typescript',
|
|
@@ -129,15 +129,15 @@ async function formatCode(code) {
|
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
catch {
|
|
132
|
-
|
|
132
|
+
logger.log.warn('Prettier formatting failed, using unformatted code');
|
|
133
133
|
return code;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* Generate BFF structure (auth + transformers)
|
|
138
138
|
*/
|
|
139
|
-
async function generateBffStructure(allMethods, serverRoutePath, inputDir) {
|
|
140
|
-
const bffSpinner =
|
|
139
|
+
async function generateBffStructure(allMethods, serverRoutePath, inputDir, logger) {
|
|
140
|
+
const bffSpinner = logger.spinner();
|
|
141
141
|
bffSpinner.start('Generating BFF structure (auth + transformers)');
|
|
142
142
|
const serverRoot = path.dirname(serverRoutePath);
|
|
143
143
|
// 1. Generate auth files (only if they don't exist)
|
|
@@ -146,13 +146,13 @@ async function generateBffStructure(allMethods, serverRoutePath, inputDir) {
|
|
|
146
146
|
const authContextPath = path.join(authDir, 'context.ts');
|
|
147
147
|
if (!fs.existsSync(authContextPath)) {
|
|
148
148
|
const authContextCode = generateAuthContextStub();
|
|
149
|
-
const formattedAuthContext = await formatCode(authContextCode);
|
|
149
|
+
const formattedAuthContext = await formatCode(authContextCode, logger);
|
|
150
150
|
await fs.writeFile(authContextPath, formattedAuthContext, 'utf-8');
|
|
151
151
|
}
|
|
152
152
|
const authTypesPath = path.join(authDir, 'types.ts');
|
|
153
153
|
if (!fs.existsSync(authTypesPath)) {
|
|
154
154
|
const authTypesCode = generateAuthTypesStub();
|
|
155
|
-
const formattedAuthTypes = await formatCode(authTypesCode);
|
|
155
|
+
const formattedAuthTypes = await formatCode(authTypesCode, logger);
|
|
156
156
|
await fs.writeFile(authTypesPath, formattedAuthTypes, 'utf-8');
|
|
157
157
|
}
|
|
158
158
|
// 2. Generate transformer stubs (only if they don't exist)
|
|
@@ -173,14 +173,14 @@ async function generateBffStructure(allMethods, serverRoutePath, inputDir) {
|
|
|
173
173
|
const transformerPath = path.join(transformersDir, `${resource}.ts`);
|
|
174
174
|
if (!fs.existsSync(transformerPath)) {
|
|
175
175
|
const transformerCode = generateTransformerStub(resource, methods, inputDir);
|
|
176
|
-
const formattedTransformer = await formatCode(transformerCode);
|
|
176
|
+
const formattedTransformer = await formatCode(transformerCode, logger);
|
|
177
177
|
await fs.writeFile(transformerPath, formattedTransformer, 'utf-8');
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
// 3. Generate examples file (always regenerated)
|
|
181
181
|
const examplesPath = path.join(bffDir, '_transformers.example.ts');
|
|
182
182
|
const examplesCode = generateTransformerExamples();
|
|
183
|
-
const formattedExamples = await formatCode(examplesCode);
|
|
183
|
+
const formattedExamples = await formatCode(examplesCode, logger);
|
|
184
184
|
await fs.writeFile(examplesPath, formattedExamples, 'utf-8');
|
|
185
185
|
// 4. Generate BFF README (always regenerated)
|
|
186
186
|
const bffReadmePath = path.join(bffDir, 'README.md');
|
|
@@ -40,39 +40,62 @@ export interface FinishContext<T> {
|
|
|
40
40
|
success: boolean;
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* A single rule in the global callbacks configuration.
|
|
44
|
+
* Each rule independently targets specific URL patterns and/or HTTP methods.
|
|
45
|
+
* Rules are executed in order; any rule may return false to suppress the local callback.
|
|
45
46
|
*/
|
|
46
|
-
export interface
|
|
47
|
+
export interface GlobalCallbacksRule {
|
|
47
48
|
/**
|
|
48
|
-
*
|
|
49
|
-
* Only apply global callbacks to URLs matching these patterns
|
|
49
|
+
* URL glob patterns — only apply this rule to matching URLs.
|
|
50
50
|
* Supports wildcards: '/api/**', '/api/public/*', etc.
|
|
51
|
-
* If omitted,
|
|
51
|
+
* If omitted, the rule applies to all URLs.
|
|
52
52
|
*/
|
|
53
53
|
patterns?: string[];
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* HTTP methods — only apply this rule to matching methods (case-insensitive).
|
|
56
|
+
* Example: ['DELETE', 'POST']
|
|
57
|
+
* If omitted, the rule applies to all methods.
|
|
58
|
+
*/
|
|
59
|
+
methods?: string[];
|
|
60
|
+
/**
|
|
61
|
+
* Called before the request is sent.
|
|
62
|
+
* Return modified context (headers/body/query) to alter the request.
|
|
63
|
+
* Return false to prevent local onRequest execution (Opción 2).
|
|
58
64
|
*/
|
|
59
65
|
onRequest?: (context: RequestContext) => void | Promise<void> | ModifiedRequestContext | Promise<ModifiedRequestContext> | boolean | Promise<boolean>;
|
|
60
66
|
/**
|
|
61
|
-
* Called when request succeeds
|
|
62
|
-
* Return false to prevent local
|
|
67
|
+
* Called when the request succeeds.
|
|
68
|
+
* Return false to prevent local onSuccess execution (Opción 2).
|
|
63
69
|
*/
|
|
64
70
|
onSuccess?: (data: any, context?: any) => void | Promise<void> | boolean | Promise<boolean>;
|
|
65
71
|
/**
|
|
66
|
-
* Called when request fails
|
|
67
|
-
* Return false to prevent local
|
|
72
|
+
* Called when the request fails.
|
|
73
|
+
* Return false to prevent local onError execution (Opción 2).
|
|
68
74
|
*/
|
|
69
75
|
onError?: (error: any, context?: any) => void | Promise<void> | boolean | Promise<boolean>;
|
|
70
76
|
/**
|
|
71
|
-
* Called when request finishes (success or error)
|
|
72
|
-
* Return false to prevent local
|
|
77
|
+
* Called when the request finishes (success or error).
|
|
78
|
+
* Return false to prevent local onFinish execution (Opción 2).
|
|
73
79
|
*/
|
|
74
80
|
onFinish?: (context: FinishContext<any>) => void | Promise<void> | boolean | Promise<boolean>;
|
|
75
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Global callbacks configuration.
|
|
84
|
+
* Accepts a single rule (backward-compatible) or an array of rules.
|
|
85
|
+
* Each rule can independently target URLs and HTTP methods.
|
|
86
|
+
* Provided via Nuxt plugin: $getGlobalApiCallbacks
|
|
87
|
+
*
|
|
88
|
+
* @example Single rule (backward-compatible)
|
|
89
|
+
* getGlobalApiCallbacks: () => ({ onError: (e) => console.error(e) })
|
|
90
|
+
*
|
|
91
|
+
* @example Multiple rules with method/pattern targeting
|
|
92
|
+
* getGlobalApiCallbacks: () => [
|
|
93
|
+
* { onRequest: (ctx) => console.log(ctx.url) },
|
|
94
|
+
* { methods: ['DELETE'], onSuccess: () => toast.success('Deleted!') },
|
|
95
|
+
* { patterns: ['/api/private/**'], onRequest: () => ({ headers: { Authorization: '...' } }) },
|
|
96
|
+
* ]
|
|
97
|
+
*/
|
|
98
|
+
export type GlobalCallbacksConfig = GlobalCallbacksRule | GlobalCallbacksRule[];
|
|
76
99
|
/**
|
|
77
100
|
* Type for skipGlobalCallbacks option (Opción 1)
|
|
78
101
|
* - true: skip all global callbacks
|
|
@@ -124,6 +147,28 @@ export interface ApiRequestOptions<T = any> {
|
|
|
124
147
|
* Useful for manual cache control or sharing cache between components.
|
|
125
148
|
*/
|
|
126
149
|
cacheKey?: string;
|
|
150
|
+
/**
|
|
151
|
+
* Enable pagination for this request.
|
|
152
|
+
* When true, the composable injects page/perPage params and exposes `pagination` state + helpers.
|
|
153
|
+
* Uses global pagination config by default (set via plugins/api-pagination.ts).
|
|
154
|
+
* @example
|
|
155
|
+
* const { data, pagination, goToPage, nextPage, prevPage, setPerPage } = useGetPets(params, { paginated: true })
|
|
156
|
+
*/
|
|
157
|
+
paginated?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Initial page number. Defaults to global config default (usually 1).
|
|
160
|
+
*/
|
|
161
|
+
initialPage?: number;
|
|
162
|
+
/**
|
|
163
|
+
* Initial page size. Defaults to global config default (usually 20).
|
|
164
|
+
*/
|
|
165
|
+
initialPerPage?: number;
|
|
166
|
+
/**
|
|
167
|
+
* Per-request pagination config override.
|
|
168
|
+
* Takes priority over the global pagination config set in plugins/api-pagination.ts.
|
|
169
|
+
* Useful when one specific endpoint has a different pagination convention.
|
|
170
|
+
*/
|
|
171
|
+
paginationConfig?: import('./pagination.js').PaginationConfig;
|
|
127
172
|
/** Base URL prepended to every request URL. Overrides runtimeConfig.public.apiBaseUrl. */
|
|
128
173
|
baseURL?: string;
|
|
129
174
|
/** HTTP method (GET, POST, PUT, PATCH, DELETE, etc.) */
|
|
@@ -154,52 +199,47 @@ export declare function applyPick<T>(data: T, paths: ReadonlyArray<string>): any
|
|
|
154
199
|
*/
|
|
155
200
|
export declare function getGlobalHeaders(): Record<string, string>;
|
|
156
201
|
/**
|
|
157
|
-
* Helper function to get global
|
|
202
|
+
* Helper function to get global callback rules from user configuration.
|
|
203
|
+
* Always returns a normalized array — wraps legacy single-object config automatically for
|
|
204
|
+
* full backward compatibility.
|
|
158
205
|
* Uses Nuxt plugin provide: plugins/api-callbacks.ts with $getGlobalApiCallbacks
|
|
159
206
|
*/
|
|
160
|
-
export declare function getGlobalCallbacks():
|
|
207
|
+
export declare function getGlobalCallbacks(): GlobalCallbacksRule[];
|
|
161
208
|
/**
|
|
162
209
|
* Helper function to get the global base URL from runtimeConfig.public.apiBaseUrl
|
|
163
210
|
* Returns the configured URL or undefined if not set or not in a Nuxt context.
|
|
164
211
|
*/
|
|
165
212
|
export declare function getGlobalBaseUrl(): string | undefined;
|
|
166
213
|
/**
|
|
167
|
-
* Check if a global
|
|
168
|
-
* Implements Opción 1 (skipGlobalCallbacks) and
|
|
214
|
+
* Check if a global rule should be applied to a specific request.
|
|
215
|
+
* Implements Opción 1 (skipGlobalCallbacks), URL pattern matching, and HTTP method matching.
|
|
169
216
|
*/
|
|
170
|
-
export declare function shouldApplyGlobalCallback(url: string, callbackName: 'onRequest' | 'onSuccess' | 'onError' | 'onFinish',
|
|
217
|
+
export declare function shouldApplyGlobalCallback(url: string, method: string, callbackName: 'onRequest' | 'onSuccess' | 'onError' | 'onFinish', rule: GlobalCallbacksRule, skipConfig?: SkipGlobalCallbacks): boolean;
|
|
171
218
|
/**
|
|
172
|
-
* Merge local and global
|
|
219
|
+
* Merge local and global callback rules with proper execution order.
|
|
220
|
+
* Global rules are iterated in definition order. Any rule returning false suppresses the local callback.
|
|
173
221
|
* Implements all 3 options:
|
|
174
|
-
* - Opción 1: skipGlobalCallbacks to disable global
|
|
175
|
-
* - Opción 2:
|
|
176
|
-
* - Opción 3: pattern matching
|
|
222
|
+
* - Opción 1: skipGlobalCallbacks to disable all global rules per request
|
|
223
|
+
* - Opción 2: a rule callback can return false to prevent local callback execution
|
|
224
|
+
* - Opción 3: per-rule URL pattern matching and HTTP method filtering
|
|
177
225
|
*/
|
|
178
|
-
export declare function mergeCallbacks(url: string, localCallbacks: {
|
|
226
|
+
export declare function mergeCallbacks(url: string, method: string, localCallbacks: {
|
|
179
227
|
onRequest?: Function;
|
|
180
228
|
onSuccess?: Function;
|
|
181
229
|
onError?: Function;
|
|
182
230
|
onFinish?: Function;
|
|
183
231
|
}, skipConfig?: SkipGlobalCallbacks): {
|
|
184
232
|
/**
|
|
185
|
-
* Merged onRequest
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
onRequest: (ctx: RequestContext) => Promise<any>;
|
|
190
|
-
/**
|
|
191
|
-
* Merged onSuccess callback
|
|
192
|
-
* Executes global first, then local (if global doesn't return false)
|
|
233
|
+
* Merged onRequest: runs all applicable global rules collecting and deep-merging
|
|
234
|
+
* modifications (headers and query are merged; body is last-write-wins).
|
|
235
|
+
* Local onRequest runs after all rules unless any returns false, and its
|
|
236
|
+
* modifications take highest priority.
|
|
193
237
|
*/
|
|
238
|
+
onRequest: (ctx: RequestContext) => Promise<ModifiedRequestContext | undefined>;
|
|
239
|
+
/** Merged onSuccess: global rules first in order, then local (unless suppressed). */
|
|
194
240
|
onSuccess: (data: any, context?: any) => Promise<void>;
|
|
195
|
-
/**
|
|
196
|
-
* Merged onError callback
|
|
197
|
-
* Executes global first, then local (if global doesn't return false)
|
|
198
|
-
*/
|
|
241
|
+
/** Merged onError: global rules first in order, then local (unless suppressed). */
|
|
199
242
|
onError: (error: any, context?: any) => Promise<void>;
|
|
200
|
-
/**
|
|
201
|
-
* Merged onFinish callback
|
|
202
|
-
* Executes global first, then local (if global doesn't return false)
|
|
203
|
-
*/
|
|
243
|
+
/** Merged onFinish: global rules first in order, then local (unless suppressed). */
|
|
204
244
|
onFinish: (context: any) => Promise<void>;
|
|
205
245
|
};
|
|
@@ -101,7 +101,9 @@ export function getGlobalHeaders() {
|
|
|
101
101
|
return headers;
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
|
-
* Helper function to get global
|
|
104
|
+
* Helper function to get global callback rules from user configuration.
|
|
105
|
+
* Always returns a normalized array — wraps legacy single-object config automatically for
|
|
106
|
+
* full backward compatibility.
|
|
105
107
|
* Uses Nuxt plugin provide: plugins/api-callbacks.ts with $getGlobalApiCallbacks
|
|
106
108
|
*/
|
|
107
109
|
export function getGlobalCallbacks() {
|
|
@@ -110,16 +112,17 @@ export function getGlobalCallbacks() {
|
|
|
110
112
|
// @ts-ignore - $getGlobalApiCallbacks may or may not exist (user-defined)
|
|
111
113
|
if (nuxtApp.$getGlobalApiCallbacks) {
|
|
112
114
|
// @ts-ignore
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
|
|
115
|
+
const config = nuxtApp.$getGlobalApiCallbacks();
|
|
116
|
+
if (config && typeof config === 'object') {
|
|
117
|
+
// Normalize: wrap single-rule object in array for backward compatibility
|
|
118
|
+
return Array.isArray(config) ? config : [config];
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
catch (e) {
|
|
120
123
|
// useNuxtApp not available or plugin not configured, that's OK
|
|
121
124
|
}
|
|
122
|
-
return
|
|
125
|
+
return [];
|
|
123
126
|
}
|
|
124
127
|
/**
|
|
125
128
|
* Helper function to get the global base URL from runtimeConfig.public.apiBaseUrl
|
|
@@ -137,144 +140,134 @@ export function getGlobalBaseUrl() {
|
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
/**
|
|
140
|
-
* Check if a global
|
|
141
|
-
* Implements Opción 1 (skipGlobalCallbacks) and
|
|
143
|
+
* Check if a global rule should be applied to a specific request.
|
|
144
|
+
* Implements Opción 1 (skipGlobalCallbacks), URL pattern matching, and HTTP method matching.
|
|
142
145
|
*/
|
|
143
|
-
export function shouldApplyGlobalCallback(url, callbackName,
|
|
146
|
+
export function shouldApplyGlobalCallback(url, method, callbackName, rule, skipConfig) {
|
|
144
147
|
// Opción 1: Check if callback is skipped via skipGlobalCallbacks
|
|
145
|
-
if (skipConfig === true)
|
|
146
|
-
return false;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return patterns.some((pattern) => {
|
|
154
|
-
// Convert glob pattern to regex
|
|
155
|
-
// ** matches any characters including /
|
|
156
|
-
// * matches any characters except /
|
|
148
|
+
if (skipConfig === true)
|
|
149
|
+
return false;
|
|
150
|
+
if (Array.isArray(skipConfig) && skipConfig.includes(callbackName))
|
|
151
|
+
return false;
|
|
152
|
+
// URL pattern matching — if patterns defined, URL must match at least one
|
|
153
|
+
if (rule.patterns && rule.patterns.length > 0) {
|
|
154
|
+
const matchesUrl = rule.patterns.some((pattern) => {
|
|
155
|
+
// Convert glob pattern to regex: ** = any path, * = single segment
|
|
157
156
|
const regexPattern = pattern
|
|
158
157
|
.replace(/\*\*/g, '@@DOUBLE_STAR@@')
|
|
159
158
|
.replace(/\*/g, '[^/]*')
|
|
160
159
|
.replace(/@@DOUBLE_STAR@@/g, '.*');
|
|
161
|
-
|
|
162
|
-
return regex.test(url);
|
|
160
|
+
return new RegExp('^' + regexPattern + '$').test(url);
|
|
163
161
|
});
|
|
162
|
+
if (!matchesUrl)
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
// Method matching — if methods defined, request method must match at least one
|
|
166
|
+
if (rule.methods && rule.methods.length > 0) {
|
|
167
|
+
if (!rule.methods.map((m) => m.toUpperCase()).includes(method.toUpperCase()))
|
|
168
|
+
return false;
|
|
164
169
|
}
|
|
165
|
-
// By default, apply global callback
|
|
166
170
|
return true;
|
|
167
171
|
}
|
|
168
172
|
/**
|
|
169
|
-
* Merge local and global
|
|
173
|
+
* Merge local and global callback rules with proper execution order.
|
|
174
|
+
* Global rules are iterated in definition order. Any rule returning false suppresses the local callback.
|
|
170
175
|
* Implements all 3 options:
|
|
171
|
-
* - Opción 1: skipGlobalCallbacks to disable global
|
|
172
|
-
* - Opción 2:
|
|
173
|
-
* - Opción 3: pattern matching
|
|
176
|
+
* - Opción 1: skipGlobalCallbacks to disable all global rules per request
|
|
177
|
+
* - Opción 2: a rule callback can return false to prevent local callback execution
|
|
178
|
+
* - Opción 3: per-rule URL pattern matching and HTTP method filtering
|
|
174
179
|
*/
|
|
175
|
-
export function mergeCallbacks(url, localCallbacks, skipConfig) {
|
|
176
|
-
const
|
|
180
|
+
export function mergeCallbacks(url, method, localCallbacks, skipConfig) {
|
|
181
|
+
const rules = getGlobalCallbacks();
|
|
182
|
+
/**
|
|
183
|
+
* Iterate all applicable global rules for onSuccess, onError, or onFinish.
|
|
184
|
+
* Returns true if the local callback should still execute.
|
|
185
|
+
*/
|
|
186
|
+
async function runGlobalRules(callbackName, ...args) {
|
|
187
|
+
let continueLocal = true;
|
|
188
|
+
for (const rule of rules) {
|
|
189
|
+
const cb = rule[callbackName];
|
|
190
|
+
if (!cb || !shouldApplyGlobalCallback(url, method, callbackName, rule, skipConfig))
|
|
191
|
+
continue;
|
|
192
|
+
try {
|
|
193
|
+
const result = await cb(...args);
|
|
194
|
+
// Opción 2: returning false from any rule suppresses the local callback
|
|
195
|
+
if (result === false)
|
|
196
|
+
continueLocal = false;
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
console.error(`Error in global ${callbackName} callback:`, error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return continueLocal;
|
|
203
|
+
}
|
|
177
204
|
return {
|
|
178
205
|
/**
|
|
179
|
-
* Merged onRequest
|
|
180
|
-
*
|
|
181
|
-
*
|
|
206
|
+
* Merged onRequest: runs all applicable global rules collecting and deep-merging
|
|
207
|
+
* modifications (headers and query are merged; body is last-write-wins).
|
|
208
|
+
* Local onRequest runs after all rules unless any returns false, and its
|
|
209
|
+
* modifications take highest priority.
|
|
182
210
|
*/
|
|
183
211
|
onRequest: async (ctx) => {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
212
|
+
let mergedMods;
|
|
213
|
+
let continueLocal = true;
|
|
214
|
+
for (const rule of rules) {
|
|
215
|
+
if (!rule.onRequest || !shouldApplyGlobalCallback(url, method, 'onRequest', rule, skipConfig))
|
|
216
|
+
continue;
|
|
187
217
|
try {
|
|
188
|
-
const result = await
|
|
189
|
-
// Opción 2: If global returns false, don't execute local
|
|
218
|
+
const result = await rule.onRequest(ctx);
|
|
190
219
|
if (result === false) {
|
|
191
|
-
|
|
220
|
+
continueLocal = false;
|
|
192
221
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
222
|
+
else if (result && typeof result === 'object') {
|
|
223
|
+
const mod = result;
|
|
224
|
+
// Deep-merge headers and query; body is last-write-wins
|
|
225
|
+
mergedMods = {
|
|
226
|
+
...(mergedMods ?? {}),
|
|
227
|
+
...mod,
|
|
228
|
+
headers: { ...(mergedMods?.headers ?? {}), ...(mod.headers ?? {}) },
|
|
229
|
+
query: { ...(mergedMods?.query ?? {}), ...(mod.query ?? {}) },
|
|
230
|
+
};
|
|
196
231
|
}
|
|
197
232
|
}
|
|
198
233
|
catch (error) {
|
|
199
234
|
console.error('Error in global onRequest callback:', error);
|
|
200
235
|
}
|
|
201
236
|
}
|
|
202
|
-
// Execute local onRequest
|
|
203
|
-
if (localCallbacks.onRequest) {
|
|
204
|
-
|
|
237
|
+
// Execute local onRequest — its modifications take highest priority
|
|
238
|
+
if (continueLocal && localCallbacks.onRequest) {
|
|
239
|
+
const localResult = await localCallbacks.onRequest(ctx);
|
|
240
|
+
if (localResult && typeof localResult === 'object') {
|
|
241
|
+
const localMod = localResult;
|
|
242
|
+
return mergedMods
|
|
243
|
+
? {
|
|
244
|
+
...mergedMods,
|
|
245
|
+
...localMod,
|
|
246
|
+
headers: { ...(mergedMods.headers ?? {}), ...(localMod.headers ?? {}) },
|
|
247
|
+
query: { ...(mergedMods.query ?? {}), ...(localMod.query ?? {}) },
|
|
248
|
+
}
|
|
249
|
+
: localMod;
|
|
250
|
+
}
|
|
205
251
|
}
|
|
252
|
+
return mergedMods;
|
|
206
253
|
},
|
|
207
|
-
/**
|
|
208
|
-
* Merged onSuccess callback
|
|
209
|
-
* Executes global first, then local (if global doesn't return false)
|
|
210
|
-
*/
|
|
254
|
+
/** Merged onSuccess: global rules first in order, then local (unless suppressed). */
|
|
211
255
|
onSuccess: async (data, context) => {
|
|
212
|
-
|
|
213
|
-
// Execute global onSuccess
|
|
214
|
-
if (shouldApplyGlobalCallback(url, 'onSuccess', global.patterns, skipConfig) &&
|
|
215
|
-
global.onSuccess) {
|
|
216
|
-
try {
|
|
217
|
-
const result = await global.onSuccess(data, context);
|
|
218
|
-
// Opción 2: If global returns false, don't execute local
|
|
219
|
-
if (result === false) {
|
|
220
|
-
continueLocal = false;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
console.error('Error in global onSuccess callback:', error);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Execute local onSuccess (if not cancelled)
|
|
256
|
+
const continueLocal = await runGlobalRules('onSuccess', data, context);
|
|
228
257
|
if (continueLocal && localCallbacks.onSuccess) {
|
|
229
258
|
await localCallbacks.onSuccess(data, context);
|
|
230
259
|
}
|
|
231
260
|
},
|
|
232
|
-
/**
|
|
233
|
-
* Merged onError callback
|
|
234
|
-
* Executes global first, then local (if global doesn't return false)
|
|
235
|
-
*/
|
|
261
|
+
/** Merged onError: global rules first in order, then local (unless suppressed). */
|
|
236
262
|
onError: async (error, context) => {
|
|
237
|
-
|
|
238
|
-
// Execute global onError
|
|
239
|
-
if (shouldApplyGlobalCallback(url, 'onError', global.patterns, skipConfig) &&
|
|
240
|
-
global.onError) {
|
|
241
|
-
try {
|
|
242
|
-
const result = await global.onError(error, context);
|
|
243
|
-
// Opción 2: If global returns false, don't execute local
|
|
244
|
-
if (result === false) {
|
|
245
|
-
continueLocal = false;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
console.error('Error in global onError callback:', error);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
// Execute local onError (if not cancelled)
|
|
263
|
+
const continueLocal = await runGlobalRules('onError', error, context);
|
|
253
264
|
if (continueLocal && localCallbacks.onError) {
|
|
254
265
|
await localCallbacks.onError(error, context);
|
|
255
266
|
}
|
|
256
267
|
},
|
|
257
|
-
/**
|
|
258
|
-
* Merged onFinish callback
|
|
259
|
-
* Executes global first, then local (if global doesn't return false)
|
|
260
|
-
*/
|
|
268
|
+
/** Merged onFinish: global rules first in order, then local (unless suppressed). */
|
|
261
269
|
onFinish: async (context) => {
|
|
262
|
-
|
|
263
|
-
// Execute global onFinish
|
|
264
|
-
if (shouldApplyGlobalCallback(url, 'onFinish', global.patterns, skipConfig) &&
|
|
265
|
-
global.onFinish) {
|
|
266
|
-
try {
|
|
267
|
-
const result = await global.onFinish(context);
|
|
268
|
-
// Opción 2: If global returns false, don't execute local
|
|
269
|
-
if (result === false) {
|
|
270
|
-
continueLocal = false;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
catch (error) {
|
|
274
|
-
console.error('Error in global onFinish callback:', error);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
// Execute local onFinish (if not cancelled)
|
|
270
|
+
const continueLocal = await runGlobalRules('onFinish', context);
|
|
278
271
|
if (continueLocal && localCallbacks.onFinish) {
|
|
279
272
|
await localCallbacks.onFinish(context);
|
|
280
273
|
}
|