@codewithagents/openapi-server 1.2.0 → 1.3.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/README.md +55 -25
- package/dist/cli.cjs +298 -42
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -7
- package/dist/config.js.map +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +23 -6
- package/dist/generator.js.map +1 -1
- package/dist/plugins/router.d.ts +2 -0
- package/dist/plugins/router.d.ts.map +1 -1
- package/dist/plugins/router.js +296 -39
- package/dist/plugins/router.js.map +1 -1
- package/dist/plugins/service.d.ts.map +1 -1
- package/dist/plugins/service.js +19 -8
- package/dist/plugins/service.js.map +1 -1
- package/package.json +8 -3
package/dist/config.js
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
const FORBIDDEN_OUTPUT_PREFIXES = [
|
|
4
|
-
'/etc',
|
|
5
|
-
'/
|
|
6
|
-
'
|
|
4
|
+
'/etc',
|
|
5
|
+
'/usr',
|
|
6
|
+
'/bin',
|
|
7
|
+
'/sbin',
|
|
8
|
+
'/lib',
|
|
9
|
+
'/lib64',
|
|
10
|
+
'/sys',
|
|
11
|
+
'/proc',
|
|
12
|
+
'/dev',
|
|
13
|
+
'/boot',
|
|
14
|
+
'/run',
|
|
15
|
+
'C:\\Windows',
|
|
16
|
+
'C:\\Program Files',
|
|
7
17
|
];
|
|
8
18
|
const FORBIDDEN_INPUT_PREFIXES = [
|
|
9
|
-
'/etc',
|
|
10
|
-
'/
|
|
11
|
-
'
|
|
19
|
+
'/etc',
|
|
20
|
+
'/usr',
|
|
21
|
+
'/bin',
|
|
22
|
+
'/sbin',
|
|
23
|
+
'/lib',
|
|
24
|
+
'/lib64',
|
|
25
|
+
'/sys',
|
|
26
|
+
'/proc',
|
|
27
|
+
'/dev',
|
|
28
|
+
'C:\\Windows',
|
|
29
|
+
'C:\\Program Files',
|
|
12
30
|
];
|
|
13
31
|
export function validateConfigPath(configPath) {
|
|
14
32
|
if (!configPath.endsWith('.json')) {
|
|
@@ -35,6 +53,7 @@ export function validateInputPath(resolvedInput) {
|
|
|
35
53
|
}
|
|
36
54
|
}
|
|
37
55
|
}
|
|
56
|
+
// fallow-ignore-next-line complexity
|
|
38
57
|
export async function loadConfig(cwd, configPath) {
|
|
39
58
|
const resolvedConfigPath = configPath ?? join(cwd, 'openapi-server.config.json');
|
|
40
59
|
if (configPath !== undefined) {
|
|
@@ -66,8 +85,10 @@ export async function loadConfig(cwd, configPath) {
|
|
|
66
85
|
}
|
|
67
86
|
if (config['framework'] !== undefined &&
|
|
68
87
|
config['framework'] !== 'hono' &&
|
|
88
|
+
config['framework'] !== 'express' &&
|
|
89
|
+
config['framework'] !== 'fastify' &&
|
|
69
90
|
config['framework'] !== 'none') {
|
|
70
|
-
throw new Error('"framework" must be
|
|
91
|
+
throw new Error('"framework" must be one of: "hono", "express", "fastify", or "none"');
|
|
71
92
|
}
|
|
72
93
|
if (config['input_schema'] !== undefined &&
|
|
73
94
|
(typeof config['input_schema'] !== 'string' || !config['input_schema'])) {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAazC,MAAM,yBAAyB,GAAG;IAChC,MAAM,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/generator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAcA,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgF9E"}
|
package/dist/generator.js
CHANGED
|
@@ -8,7 +8,8 @@ async function formatTs(content, filePath) {
|
|
|
8
8
|
}
|
|
9
9
|
import { parseSpec } from '@codewithagents/openapi-gen';
|
|
10
10
|
import { generateService } from './plugins/service.js';
|
|
11
|
-
import { generateRouter } from './plugins/router.js';
|
|
11
|
+
import { generateRouter, generateExpressRouter, generateFastifyRouter } from './plugins/router.js';
|
|
12
|
+
// fallow-ignore-next-line complexity
|
|
12
13
|
export async function generate(cwd, configPath) {
|
|
13
14
|
console.log('Loading config...');
|
|
14
15
|
const config = await loadConfig(cwd, configPath);
|
|
@@ -23,6 +24,14 @@ export async function generate(cwd, configPath) {
|
|
|
23
24
|
// First pass: generate router without schema validation
|
|
24
25
|
generatedFiles.push(generateRouter(spec));
|
|
25
26
|
}
|
|
27
|
+
else if (framework === 'express') {
|
|
28
|
+
// First pass: generate router without schema validation
|
|
29
|
+
generatedFiles.push(generateExpressRouter(spec));
|
|
30
|
+
}
|
|
31
|
+
else if (framework === 'fastify') {
|
|
32
|
+
// First pass: generate router without schema validation
|
|
33
|
+
generatedFiles.push(generateFastifyRouter(spec));
|
|
34
|
+
}
|
|
26
35
|
console.log(`Writing output to: ${outputDir}`);
|
|
27
36
|
await mkdir(outputDir, { recursive: true });
|
|
28
37
|
for (const file of generatedFiles) {
|
|
@@ -31,7 +40,8 @@ export async function generate(cwd, configPath) {
|
|
|
31
40
|
console.log(` ✓ ${file.filename}`);
|
|
32
41
|
}
|
|
33
42
|
// Second pass: if input_schema is configured and file exists, re-generate router with Zod validation
|
|
34
|
-
if (framework === 'hono'
|
|
43
|
+
if ((framework === 'hono' || framework === 'express' || framework === 'fastify') &&
|
|
44
|
+
config.input_schema !== undefined) {
|
|
35
45
|
const schemaPath = resolve(cwd, config.input_schema);
|
|
36
46
|
let schemaContent;
|
|
37
47
|
try {
|
|
@@ -54,10 +64,17 @@ export async function generate(cwd, configPath) {
|
|
|
54
64
|
const schemaImportPath = relPath.startsWith('.') ? relPath : `./${relPath}`;
|
|
55
65
|
// Strip .ts extension for import (use .js for NodeNext compatibility)
|
|
56
66
|
const schemaImportPathJs = schemaImportPath.replace(/\.ts$/, '.js');
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
const routerOptions = { schemaNames: exportedSchemas, schemaImportPath: schemaImportPathJs };
|
|
68
|
+
let routerFile;
|
|
69
|
+
if (framework === 'hono') {
|
|
70
|
+
routerFile = generateRouter(spec, routerOptions);
|
|
71
|
+
}
|
|
72
|
+
else if (framework === 'express') {
|
|
73
|
+
routerFile = generateExpressRouter(spec, routerOptions);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
routerFile = generateFastifyRouter(spec, routerOptions);
|
|
77
|
+
}
|
|
61
78
|
const routerPath = join(outputDir, routerFile.filename);
|
|
62
79
|
await writeFile(routerPath, await formatTs(routerFile.content, routerPath), 'utf-8');
|
|
63
80
|
console.log(` ✓ router.ts (with Zod validation for ${exportedSchemas.size} schema(s))`);
|
package/dist/generator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,QAAgB;IACvD,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAA;IAC1D,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAA;IAC5C,OAAO,MAAM,CAAC,OAAO,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAA;AAC7D,CAAC;AACD,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
|
package/dist/plugins/router.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ interface RouterOptions {
|
|
|
4
4
|
schemaNames?: Set<string>;
|
|
5
5
|
schemaImportPath?: string;
|
|
6
6
|
}
|
|
7
|
+
export declare function generateExpressRouter(spec: OpenAPIV3_1.Document, options?: RouterOptions): GeneratedFile;
|
|
8
|
+
export declare function generateFastifyRouter(spec: OpenAPIV3_1.Document, options?: RouterOptions): GeneratedFile;
|
|
7
9
|
export declare function generateRouter(spec: OpenAPIV3_1.Document, options?: RouterOptions): GeneratedFile;
|
|
8
10
|
export {};
|
|
9
11
|
//# sourceMappingURL=router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/plugins/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/plugins/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAoWhE,UAAU,aAAa;IACrB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAsFD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,WAAW,CAAC,QAAQ,EAC1B,OAAO,CAAC,EAAE,aAAa,GACtB,aAAa,CA8Df;AAkGD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,WAAW,CAAC,QAAQ,EAC1B,OAAO,CAAC,EAAE,aAAa,GACtB,aAAa,CAuDf;AAKD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,aAAa,CA0DjG"}
|
package/dist/plugins/router.js
CHANGED
|
@@ -12,7 +12,7 @@ function extractPathParamsFromPath(path) {
|
|
|
12
12
|
const matches = path.match(/\{([^}]+)\}/g);
|
|
13
13
|
if (matches === null)
|
|
14
14
|
return [];
|
|
15
|
-
// Keep raw param names
|
|
15
|
+
// Keep raw param names: they are used in c.req.param() which must match
|
|
16
16
|
// the actual Hono route pattern (e.g. :job-id requires c.req.param('job-id'))
|
|
17
17
|
return matches.map((m) => m.slice(1, -1));
|
|
18
18
|
}
|
|
@@ -89,7 +89,7 @@ function deriveOperationName(method, path) {
|
|
|
89
89
|
const prefix = prefixMap[method] ?? method;
|
|
90
90
|
const segments = path.replace(/^\/api\/v\d+\//, '').replace(/^\//, '');
|
|
91
91
|
const parts = segments.split('/').map((seg) => {
|
|
92
|
-
// Handle mixed segments like "{maxLat}.{format}"
|
|
92
|
+
// Handle mixed segments like "{maxLat}.{format}": extract each {param} inside
|
|
93
93
|
const paramMatches = seg.match(/\{([^}]+)\}/g);
|
|
94
94
|
if (paramMatches !== null && !(seg.startsWith('{') && seg.endsWith('}'))) {
|
|
95
95
|
return paramMatches
|
|
@@ -112,12 +112,25 @@ function deriveOperationName(method, path) {
|
|
|
112
112
|
* Strips trailing [] (array marker), converts separators to camelCase.
|
|
113
113
|
*/
|
|
114
114
|
function normalizeParamName(name) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
// Split on non-alphanumeric sequences to avoid polynomial ReDoS from [^x]+y patterns.
|
|
116
|
+
const stripped = name.replace(/\[\]$/, '').replace(/'/g, '');
|
|
117
|
+
const parts = stripped.split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
118
|
+
if (parts.length === 0)
|
|
119
|
+
return '_';
|
|
120
|
+
const camel = parts
|
|
121
|
+
.map((part, i) => (i === 0 ? part : part[0].toUpperCase() + part.slice(1)))
|
|
122
|
+
.join('');
|
|
123
|
+
return /^[^a-zA-Z_$]/.test(camel) ? `_${camel}` : camel;
|
|
124
|
+
}
|
|
125
|
+
function schemaToTsType(schema) {
|
|
126
|
+
if (schema === undefined || isRef(schema))
|
|
127
|
+
return 'string';
|
|
128
|
+
const s = schema;
|
|
129
|
+
if (s.type === 'number' || s.type === 'integer')
|
|
130
|
+
return 'number';
|
|
131
|
+
if (s.type === 'boolean')
|
|
132
|
+
return 'boolean';
|
|
133
|
+
return 'string';
|
|
121
134
|
}
|
|
122
135
|
function getQueryParams(operation, spec) {
|
|
123
136
|
const parameters = operation.parameters;
|
|
@@ -129,17 +142,9 @@ function getQueryParams(operation, spec) {
|
|
|
129
142
|
if (resolved === undefined || resolved.in !== 'query')
|
|
130
143
|
continue;
|
|
131
144
|
const schema = resolved.schema;
|
|
132
|
-
let tsType = 'string';
|
|
133
|
-
if (schema !== undefined && !isRef(schema)) {
|
|
134
|
-
const s = schema;
|
|
135
|
-
if (s.type === 'number' || s.type === 'integer')
|
|
136
|
-
tsType = 'number';
|
|
137
|
-
else if (s.type === 'boolean')
|
|
138
|
-
tsType = 'boolean';
|
|
139
|
-
}
|
|
140
145
|
result.push({
|
|
141
146
|
name: normalizeParamName(resolved.name),
|
|
142
|
-
tsType,
|
|
147
|
+
tsType: schemaToTsType(schema),
|
|
143
148
|
required: resolved.required === true,
|
|
144
149
|
});
|
|
145
150
|
}
|
|
@@ -164,32 +169,29 @@ function getBodyInfo(operation) {
|
|
|
164
169
|
}
|
|
165
170
|
return { typeName: undefined };
|
|
166
171
|
}
|
|
172
|
+
function response200IsVoid(resp) {
|
|
173
|
+
if (isRef(resp))
|
|
174
|
+
return false;
|
|
175
|
+
const r = resp;
|
|
176
|
+
const content = r.content;
|
|
177
|
+
return content === undefined || Object.keys(content).length === 0;
|
|
178
|
+
}
|
|
167
179
|
function getResponseStatus(operation, httpMethod) {
|
|
168
180
|
const responses = operation.responses;
|
|
169
|
-
if (responses
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
181
|
+
if (responses === undefined) {
|
|
182
|
+
return httpMethod === 'delete' ? { status: 204, isVoid: true } : { status: 200, isVoid: false };
|
|
183
|
+
}
|
|
184
|
+
if (responses['201'] !== undefined)
|
|
185
|
+
return { status: 201, isVoid: false };
|
|
186
|
+
if (responses['204'] !== undefined)
|
|
187
|
+
return { status: 204, isVoid: true };
|
|
188
|
+
if (responses['200'] !== undefined) {
|
|
189
|
+
if (response200IsVoid(responses['200']))
|
|
175
190
|
return { status: 204, isVoid: true };
|
|
176
|
-
|
|
177
|
-
if (responses['200'] !== undefined) {
|
|
178
|
-
const resp = responses['200'];
|
|
179
|
-
if (!isRef(resp)) {
|
|
180
|
-
const r = resp;
|
|
181
|
-
const content = r.content;
|
|
182
|
-
if (content === undefined || Object.keys(content).length === 0) {
|
|
183
|
-
return { status: 204, isVoid: true };
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return { status: 200, isVoid: false };
|
|
187
|
-
}
|
|
191
|
+
return { status: 200, isVoid: false };
|
|
188
192
|
}
|
|
189
193
|
// Default: delete -> 204, otherwise 200
|
|
190
|
-
|
|
191
|
-
return { status: 204, isVoid: true };
|
|
192
|
-
return { status: 200, isVoid: false };
|
|
194
|
+
return httpMethod === 'delete' ? { status: 204, isVoid: true } : { status: 200, isVoid: false };
|
|
193
195
|
}
|
|
194
196
|
function collectOperations(spec) {
|
|
195
197
|
const paths = spec.paths;
|
|
@@ -220,6 +222,7 @@ function collectOperations(spec) {
|
|
|
220
222
|
}
|
|
221
223
|
return operations;
|
|
222
224
|
}
|
|
225
|
+
// fallow-ignore-next-line complexity
|
|
223
226
|
function buildRouteHandler(op, indent, schemaNames) {
|
|
224
227
|
const lines = [];
|
|
225
228
|
lines.push(`${indent}app.${op.httpMethod}('${op.honoPath}', async (c) => {`);
|
|
@@ -245,7 +248,7 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
245
248
|
// Zod validation when schema is available
|
|
246
249
|
const schemaName = op.bodyInfo.typeName !== undefined ? `${op.bodyInfo.typeName}Schema` : undefined;
|
|
247
250
|
if (schemaName !== undefined && schemaNames !== undefined && schemaNames.has(schemaName)) {
|
|
248
|
-
lines.push(`${indent} // Validate request body
|
|
251
|
+
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
249
252
|
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(body)`);
|
|
250
253
|
lines.push(`${indent} if (!parseResult.success) {`);
|
|
251
254
|
lines.push(`${indent} return c.json({ error: 'Invalid request body', issues: parseResult.error.issues }, 422)`);
|
|
@@ -280,6 +283,260 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
280
283
|
lines.push(`${indent}})`);
|
|
281
284
|
return lines.join('\n');
|
|
282
285
|
}
|
|
286
|
+
// ── Express router generator ───────────────────────────────────────────────────
|
|
287
|
+
// fallow-ignore-next-line complexity
|
|
288
|
+
function buildExpressRouteHandler(op, indent, schemaNames) {
|
|
289
|
+
const lines = [];
|
|
290
|
+
lines.push(`${indent}router.${op.httpMethod}('${op.honoPath}', async (req: Request, res: Response) => {`);
|
|
291
|
+
// Query params extraction
|
|
292
|
+
if (op.queryParams.length > 0) {
|
|
293
|
+
const fields = op.queryParams
|
|
294
|
+
.map((q) => {
|
|
295
|
+
if (q.tsType === 'number') {
|
|
296
|
+
return ` ${q.name}: Number(req.query['${q.name}'] as string)`;
|
|
297
|
+
}
|
|
298
|
+
if (q.tsType === 'boolean') {
|
|
299
|
+
return ` ${q.name}: req.query['${q.name}'] === 'true'`;
|
|
300
|
+
}
|
|
301
|
+
return ` ${q.name}: req.query['${q.name}'] as string | undefined`;
|
|
302
|
+
})
|
|
303
|
+
.join(',\n');
|
|
304
|
+
lines.push(`${indent} const params = {`);
|
|
305
|
+
lines.push(fields);
|
|
306
|
+
lines.push(`${indent} }`);
|
|
307
|
+
}
|
|
308
|
+
// Body extraction, with optional Zod validation
|
|
309
|
+
let bodyVarName = 'body';
|
|
310
|
+
if (op.bodyInfo !== undefined) {
|
|
311
|
+
const schemaName = op.bodyInfo.typeName !== undefined ? `${op.bodyInfo.typeName}Schema` : undefined;
|
|
312
|
+
const useZod = schemaName !== undefined && schemaNames !== undefined && schemaNames.has(schemaName);
|
|
313
|
+
if (useZod) {
|
|
314
|
+
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
315
|
+
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
316
|
+
lines.push(`${indent} if (!parseResult.success) {`);
|
|
317
|
+
lines.push(`${indent} return void res.status(422).json({ error: 'Invalid request body', issues: parseResult.error.issues })`);
|
|
318
|
+
lines.push(`${indent} }`);
|
|
319
|
+
lines.push(`${indent} const validatedBody = parseResult.data`);
|
|
320
|
+
bodyVarName = 'validatedBody';
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
const typeAnnotation = op.bodyInfo.typeName !== undefined ? ` as ${op.bodyInfo.typeName}` : '';
|
|
324
|
+
lines.push(`${indent} const body = req.body${typeAnnotation}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Build service call args
|
|
328
|
+
const serviceArgs = [];
|
|
329
|
+
for (const p of op.pathParams) {
|
|
330
|
+
serviceArgs.push(`req.params['${p}']!`);
|
|
331
|
+
}
|
|
332
|
+
if (op.bodyInfo !== undefined) {
|
|
333
|
+
serviceArgs.push(bodyVarName);
|
|
334
|
+
}
|
|
335
|
+
if (op.queryParams.length > 0) {
|
|
336
|
+
serviceArgs.push('params');
|
|
337
|
+
}
|
|
338
|
+
const serviceCall = `service.${op.methodName}(${serviceArgs.join(', ')})`;
|
|
339
|
+
// Response
|
|
340
|
+
if (op.responseStatus.isVoid) {
|
|
341
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
342
|
+
lines.push(`${indent} res.status(${op.responseStatus.status}).end()`);
|
|
343
|
+
}
|
|
344
|
+
else if (op.responseStatus.status === 201) {
|
|
345
|
+
lines.push(`${indent} res.status(201).json(await ${serviceCall})`);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
lines.push(`${indent} res.json(await ${serviceCall})`);
|
|
349
|
+
}
|
|
350
|
+
lines.push(`${indent}})`);
|
|
351
|
+
return lines.join('\n');
|
|
352
|
+
}
|
|
353
|
+
// fallow-ignore-next-line complexity
|
|
354
|
+
export function generateExpressRouter(spec, options) {
|
|
355
|
+
const serviceName = deriveServiceName(spec);
|
|
356
|
+
const operations = collectOperations(spec);
|
|
357
|
+
// Collect body type names for import from models.js
|
|
358
|
+
const bodyTypes = new Set();
|
|
359
|
+
for (const op of operations) {
|
|
360
|
+
if (op.bodyInfo?.typeName !== undefined) {
|
|
361
|
+
bodyTypes.add(op.bodyInfo.typeName);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const sortedBodyTypes = Array.from(bodyTypes).sort();
|
|
365
|
+
// Collect which schema names are actually needed (only for ops with a matching schema)
|
|
366
|
+
const usedSchemaNames = new Set();
|
|
367
|
+
if (options?.schemaNames !== undefined) {
|
|
368
|
+
for (const op of operations) {
|
|
369
|
+
const typeName = op.bodyInfo?.typeName;
|
|
370
|
+
if (typeName !== undefined) {
|
|
371
|
+
const schemaName = `${typeName}Schema`;
|
|
372
|
+
if (options.schemaNames.has(schemaName)) {
|
|
373
|
+
usedSchemaNames.add(schemaName);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const lines = [];
|
|
379
|
+
lines.push('// This file is auto-generated. Do not edit manually.');
|
|
380
|
+
lines.push('// Express: apply express.json() middleware before mounting this router so req.body is populated.');
|
|
381
|
+
lines.push('');
|
|
382
|
+
lines.push("import { Router } from 'express'");
|
|
383
|
+
lines.push("import type { Request, Response } from 'express'");
|
|
384
|
+
if (sortedBodyTypes.length > 0) {
|
|
385
|
+
lines.push(`import type { ${sortedBodyTypes.join(', ')} } from './models.js'`);
|
|
386
|
+
}
|
|
387
|
+
lines.push(`import type { ${serviceName} } from './service.js'`);
|
|
388
|
+
if (usedSchemaNames.size > 0 && options?.schemaImportPath !== undefined) {
|
|
389
|
+
lines.push(`import { z } from 'zod'`);
|
|
390
|
+
const sortedUsedSchemas = Array.from(usedSchemaNames).sort();
|
|
391
|
+
lines.push(`import { ${sortedUsedSchemas.join(', ')} } from '${options.schemaImportPath}'`);
|
|
392
|
+
}
|
|
393
|
+
lines.push('');
|
|
394
|
+
lines.push(`export function createRouter(service: ${serviceName}): Router {`);
|
|
395
|
+
lines.push(' const router = Router()');
|
|
396
|
+
lines.push('');
|
|
397
|
+
for (const op of operations) {
|
|
398
|
+
lines.push(buildExpressRouteHandler(op, ' ', options?.schemaNames));
|
|
399
|
+
lines.push('');
|
|
400
|
+
}
|
|
401
|
+
lines.push(' return router');
|
|
402
|
+
lines.push('}');
|
|
403
|
+
lines.push('');
|
|
404
|
+
return {
|
|
405
|
+
filename: 'router.ts',
|
|
406
|
+
content: lines.join('\n'),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
// ── Fastify router generator ───────────────────────────────────────────────────
|
|
410
|
+
// fallow-ignore-next-line complexity
|
|
411
|
+
function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
412
|
+
const lines = [];
|
|
413
|
+
// Build generic type argument
|
|
414
|
+
const genericParts = [];
|
|
415
|
+
if (op.queryParams.length > 0) {
|
|
416
|
+
const queryFields = op.queryParams
|
|
417
|
+
.map((q) => {
|
|
418
|
+
if (q.tsType === 'number')
|
|
419
|
+
return `${q.name}?: number`;
|
|
420
|
+
if (q.tsType === 'boolean')
|
|
421
|
+
return `${q.name}?: boolean`;
|
|
422
|
+
return `${q.name}?: string`;
|
|
423
|
+
})
|
|
424
|
+
.join('; ');
|
|
425
|
+
genericParts.push(`Querystring: { ${queryFields} }`);
|
|
426
|
+
}
|
|
427
|
+
if (op.bodyInfo !== undefined && op.bodyInfo.typeName !== undefined) {
|
|
428
|
+
genericParts.push(`Body: ${op.bodyInfo.typeName}`);
|
|
429
|
+
}
|
|
430
|
+
else if (op.bodyInfo !== undefined) {
|
|
431
|
+
genericParts.push('Body: unknown');
|
|
432
|
+
}
|
|
433
|
+
if (op.pathParams.length > 0) {
|
|
434
|
+
const paramFields = op.pathParams.map((p) => `${p}: string`).join('; ');
|
|
435
|
+
genericParts.push(`Params: { ${paramFields} }`);
|
|
436
|
+
}
|
|
437
|
+
const generic = genericParts.length > 0 ? `<{ ${genericParts.join('; ')} }>` : '';
|
|
438
|
+
lines.push(`${indent}app.${op.httpMethod}${generic}('${op.honoPath}', async (req, reply) => {`);
|
|
439
|
+
// Query params extraction
|
|
440
|
+
if (op.queryParams.length > 0) {
|
|
441
|
+
const fields = op.queryParams.map((q) => ` ${q.name}: req.query.${q.name}`).join(',\n');
|
|
442
|
+
lines.push(`${indent} const params = {`);
|
|
443
|
+
lines.push(fields);
|
|
444
|
+
lines.push(`${indent} }`);
|
|
445
|
+
}
|
|
446
|
+
// Body handling, with optional Zod validation
|
|
447
|
+
let bodyVarName = 'req.body';
|
|
448
|
+
if (op.bodyInfo !== undefined) {
|
|
449
|
+
const schemaName = op.bodyInfo.typeName !== undefined ? `${op.bodyInfo.typeName}Schema` : undefined;
|
|
450
|
+
const useZod = schemaName !== undefined && schemaNames !== undefined && schemaNames.has(schemaName);
|
|
451
|
+
if (useZod) {
|
|
452
|
+
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
453
|
+
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
454
|
+
lines.push(`${indent} if (!parseResult.success) {`);
|
|
455
|
+
lines.push(`${indent} return reply.status(422).send({ error: 'Invalid request body', issues: parseResult.error.issues })`);
|
|
456
|
+
lines.push(`${indent} }`);
|
|
457
|
+
bodyVarName = 'parseResult.data';
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Build service call args
|
|
461
|
+
const serviceArgs = [];
|
|
462
|
+
for (const p of op.pathParams) {
|
|
463
|
+
serviceArgs.push(`req.params.${p}`);
|
|
464
|
+
}
|
|
465
|
+
if (op.bodyInfo !== undefined) {
|
|
466
|
+
serviceArgs.push(bodyVarName);
|
|
467
|
+
}
|
|
468
|
+
if (op.queryParams.length > 0) {
|
|
469
|
+
serviceArgs.push('params');
|
|
470
|
+
}
|
|
471
|
+
const serviceCall = `service.${op.methodName}(${serviceArgs.join(', ')})`;
|
|
472
|
+
// Response
|
|
473
|
+
if (op.responseStatus.isVoid) {
|
|
474
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
475
|
+
lines.push(`${indent} reply.status(${op.responseStatus.status}).send()`);
|
|
476
|
+
}
|
|
477
|
+
else if (op.responseStatus.status === 201) {
|
|
478
|
+
lines.push(`${indent} reply.status(201)`);
|
|
479
|
+
lines.push(`${indent} return ${serviceCall}`);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
lines.push(`${indent} return ${serviceCall}`);
|
|
483
|
+
}
|
|
484
|
+
lines.push(`${indent}})`);
|
|
485
|
+
return lines.join('\n');
|
|
486
|
+
}
|
|
487
|
+
// fallow-ignore-next-line complexity
|
|
488
|
+
export function generateFastifyRouter(spec, options) {
|
|
489
|
+
const serviceName = deriveServiceName(spec);
|
|
490
|
+
const operations = collectOperations(spec);
|
|
491
|
+
// Collect body type names for import from models.js
|
|
492
|
+
const bodyTypes = new Set();
|
|
493
|
+
for (const op of operations) {
|
|
494
|
+
if (op.bodyInfo?.typeName !== undefined) {
|
|
495
|
+
bodyTypes.add(op.bodyInfo.typeName);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const sortedBodyTypes = Array.from(bodyTypes).sort();
|
|
499
|
+
// Collect which schema names are actually needed (only for ops with a matching schema)
|
|
500
|
+
const usedSchemaNames = new Set();
|
|
501
|
+
if (options?.schemaNames !== undefined) {
|
|
502
|
+
for (const op of operations) {
|
|
503
|
+
const typeName = op.bodyInfo?.typeName;
|
|
504
|
+
if (typeName !== undefined) {
|
|
505
|
+
const schemaName = `${typeName}Schema`;
|
|
506
|
+
if (options.schemaNames.has(schemaName)) {
|
|
507
|
+
usedSchemaNames.add(schemaName);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const lines = [];
|
|
513
|
+
lines.push('// This file is auto-generated. Do not edit manually.');
|
|
514
|
+
lines.push('');
|
|
515
|
+
lines.push("import type { FastifyInstance } from 'fastify'");
|
|
516
|
+
if (sortedBodyTypes.length > 0) {
|
|
517
|
+
lines.push(`import type { ${sortedBodyTypes.join(', ')} } from './models.js'`);
|
|
518
|
+
}
|
|
519
|
+
lines.push(`import type { ${serviceName} } from './service.js'`);
|
|
520
|
+
if (usedSchemaNames.size > 0 && options?.schemaImportPath !== undefined) {
|
|
521
|
+
lines.push(`import { z } from 'zod'`);
|
|
522
|
+
const sortedUsedSchemas = Array.from(usedSchemaNames).sort();
|
|
523
|
+
lines.push(`import { ${sortedUsedSchemas.join(', ')} } from '${options.schemaImportPath}'`);
|
|
524
|
+
}
|
|
525
|
+
lines.push('');
|
|
526
|
+
lines.push(`export function createRouter(app: FastifyInstance, service: ${serviceName}): void {`);
|
|
527
|
+
for (const op of operations) {
|
|
528
|
+
lines.push('');
|
|
529
|
+
lines.push(buildFastifyRouteHandler(op, ' ', options?.schemaNames));
|
|
530
|
+
}
|
|
531
|
+
lines.push('}');
|
|
532
|
+
lines.push('');
|
|
533
|
+
return {
|
|
534
|
+
filename: 'router.ts',
|
|
535
|
+
content: lines.join('\n'),
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
// ── Hono router generator ─────────────────────────────────────────────────────
|
|
539
|
+
// fallow-ignore-next-line complexity
|
|
283
540
|
export function generateRouter(spec, options) {
|
|
284
541
|
const serviceName = deriveServiceName(spec);
|
|
285
542
|
const operations = collectOperations(spec);
|