@techspokes/typescript-wsdl-client 0.7.15 → 0.8.14
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 +1504 -263
- package/dist/cli.js +423 -270
- package/dist/{emit/clientEmitter.d.ts → client/generateClient.d.ts} +2 -2
- package/dist/client/generateClient.d.ts.map +1 -0
- package/dist/{emit/clientEmitter.js → client/generateClient.js} +5 -5
- package/dist/{emit/typesEmitter.d.ts → client/generateTypes.d.ts} +4 -4
- package/dist/client/generateTypes.d.ts.map +1 -0
- package/dist/{emit/typesEmitter.js → client/generateTypes.js} +6 -6
- package/dist/{emit/utilsEmitter.d.ts → client/generateUtils.d.ts} +4 -4
- package/dist/client/generateUtils.d.ts.map +1 -0
- package/dist/{emit/utilsEmitter.js → client/generateUtils.js} +7 -7
- package/dist/{emit/catalogEmitter.d.ts → compiler/generateCatalog.d.ts} +4 -4
- package/dist/compiler/generateCatalog.d.ts.map +1 -0
- package/dist/{emit/catalogEmitter.js → compiler/generateCatalog.js} +5 -5
- package/dist/compiler/schemaCompiler.d.ts +1 -1
- package/dist/compiler/schemaCompiler.js +1 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -0
- package/dist/gateway/generateGateway.d.ts +73 -0
- package/dist/gateway/generateGateway.d.ts.map +1 -0
- package/dist/gateway/generateGateway.js +135 -0
- package/dist/gateway/generators.d.ts +90 -0
- package/dist/gateway/generators.d.ts.map +1 -0
- package/dist/gateway/generators.js +270 -0
- package/dist/gateway/helpers.d.ts +115 -0
- package/dist/gateway/helpers.d.ts.map +1 -0
- package/dist/gateway/helpers.js +224 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -18
- package/dist/loader/wsdlLoader.d.ts.map +1 -1
- package/dist/loader/wsdlLoader.js +1 -3
- package/dist/openapi/generateOpenAPI.d.ts +25 -1
- package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
- package/dist/openapi/generateOpenAPI.js +28 -27
- package/dist/openapi/{buildPaths.d.ts → generatePaths.d.ts} +6 -6
- package/dist/openapi/generatePaths.d.ts.map +1 -0
- package/dist/openapi/{buildPaths.js → generatePaths.js} +1 -1
- package/dist/openapi/{buildSchemas.d.ts → generateSchemas.d.ts} +10 -10
- package/dist/openapi/generateSchemas.d.ts.map +1 -0
- package/dist/openapi/{buildSchemas.js → generateSchemas.js} +5 -5
- package/dist/openapi/security.d.ts.map +1 -1
- package/dist/openapi/security.js +2 -1
- package/dist/pipeline.d.ts +21 -7
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +66 -32
- package/dist/util/builder.d.ts +25 -0
- package/dist/util/builder.d.ts.map +1 -0
- package/dist/util/builder.js +52 -0
- package/dist/util/cli.d.ts +106 -0
- package/dist/util/cli.d.ts.map +1 -0
- package/dist/util/cli.js +164 -0
- package/package.json +11 -8
- package/dist/emit/catalogEmitter.d.ts.map +0 -1
- package/dist/emit/clientEmitter.d.ts.map +0 -1
- package/dist/emit/typesEmitter.d.ts.map +0 -1
- package/dist/emit/utilsEmitter.d.ts.map +0 -1
- package/dist/openapi/buildPaths.d.ts.map +0 -1
- package/dist/openapi/buildSchemas.d.ts.map +0 -1
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Code Generators
|
|
3
|
+
*
|
|
4
|
+
* This module contains functions for generating various gateway code artifacts:
|
|
5
|
+
* - Model JSON Schema files with URN IDs
|
|
6
|
+
* - Operation schema files with Fastify-compatible structure
|
|
7
|
+
* - schemas.ts module for registering all model schemas
|
|
8
|
+
* - Individual route files with handler stubs
|
|
9
|
+
* - routes.ts module for registering all routes
|
|
10
|
+
*
|
|
11
|
+
* All emitters follow deterministic generation rules for diff-friendly output.
|
|
12
|
+
*/
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { rewriteSchemaRefs, slugName, } from "./helpers.js";
|
|
16
|
+
/**
|
|
17
|
+
* Emits individual JSON Schema files for each OpenAPI component schema
|
|
18
|
+
*
|
|
19
|
+
* Process:
|
|
20
|
+
* 1. Creates schemas/models/ directory
|
|
21
|
+
* 2. For each component schema:
|
|
22
|
+
* - Generates URN $id: urn:services:{service}:{version}:schemas:models:{slug}
|
|
23
|
+
* - Rewrites all internal $refs to URN format
|
|
24
|
+
* - Writes to {slug}.json
|
|
25
|
+
*
|
|
26
|
+
* @param {Record<string, any>} schemas - OpenAPI components.schemas object
|
|
27
|
+
* @param {string} modelsDir - Output directory for model schema files
|
|
28
|
+
* @param {string} versionSlug - Version slug for URN generation
|
|
29
|
+
* @param {string} serviceSlug - Service slug for URN generation
|
|
30
|
+
* @returns {Record<string, string>} - Map from original schema name to URN ID
|
|
31
|
+
*/
|
|
32
|
+
export function emitModelSchemas(schemas, modelsDir, versionSlug, serviceSlug) {
|
|
33
|
+
fs.mkdirSync(modelsDir, { recursive: true });
|
|
34
|
+
const schemaIdByName = {};
|
|
35
|
+
const modelSlugByName = {};
|
|
36
|
+
// First pass: generate all URN IDs
|
|
37
|
+
for (const name of Object.keys(schemas)) {
|
|
38
|
+
const slug = slugName(name);
|
|
39
|
+
if (modelSlugByName[name]) {
|
|
40
|
+
throw new Error(`Duplicate schema name '${name}'`);
|
|
41
|
+
}
|
|
42
|
+
modelSlugByName[name] = slug;
|
|
43
|
+
schemaIdByName[name] = `urn:services:${serviceSlug}:${versionSlug}:schemas:models:${slug}`;
|
|
44
|
+
}
|
|
45
|
+
// Second pass: rewrite refs and emit files
|
|
46
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
47
|
+
const slug = modelSlugByName[name];
|
|
48
|
+
const cloned = rewriteSchemaRefs(schema, schemaIdByName);
|
|
49
|
+
cloned.$id = schemaIdByName[name];
|
|
50
|
+
const outPath = path.join(modelsDir, `${slug}.json`);
|
|
51
|
+
fs.writeFileSync(outPath, JSON.stringify(cloned, null, 2), "utf8");
|
|
52
|
+
}
|
|
53
|
+
return schemaIdByName;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Emits Fastify-compatible operation schema files
|
|
57
|
+
*
|
|
58
|
+
* Schema structure:
|
|
59
|
+
* {
|
|
60
|
+
* "$id": "urn:services:{service}:{version}:schemas:operations:{slug}",
|
|
61
|
+
* "body": { $ref: "..." }, // optional
|
|
62
|
+
* "params": { type: object, ... }, // optional
|
|
63
|
+
* "querystring": { type: object, ... }, // optional
|
|
64
|
+
* "headers": { type: object, ... }, // optional
|
|
65
|
+
* "response": {
|
|
66
|
+
* "200": { $ref: "..." },
|
|
67
|
+
* "400": { $ref: "..." },
|
|
68
|
+
* ...
|
|
69
|
+
* }
|
|
70
|
+
* }
|
|
71
|
+
*
|
|
72
|
+
* @param {OpenAPIDocument} doc - OpenAPI document
|
|
73
|
+
* @param {string} opsDir - Output directory for operation schema files
|
|
74
|
+
* @param {string} versionSlug - Version slug for URN generation
|
|
75
|
+
* @param {string} serviceSlug - Service slug for URN generation
|
|
76
|
+
* @param {Record<string, string>} schemaIdByName - Schema name to URN ID mapping
|
|
77
|
+
* @param {number[]} defaultResponseStatusCodes - Status codes to backfill with default response
|
|
78
|
+
* @param {Function} buildParamSchemas - Function to build param schemas
|
|
79
|
+
* @param {Function} getRefName - Function to extract $ref name
|
|
80
|
+
* @param {Function} isNumeric - Function to check if status code is numeric
|
|
81
|
+
* @returns {OperationMetadata[]} - Array of operation metadata for route generation
|
|
82
|
+
*/
|
|
83
|
+
export function emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, schemaIdByName, defaultResponseStatusCodes, buildParamSchemas, getRefName, isNumeric) {
|
|
84
|
+
fs.mkdirSync(opsDir, { recursive: true });
|
|
85
|
+
const operations = [];
|
|
86
|
+
const seenOperationSlugs = new Set();
|
|
87
|
+
for (const [p, pathItem] of Object.entries(doc.paths)) {
|
|
88
|
+
if (!pathItem || typeof pathItem !== "object")
|
|
89
|
+
continue;
|
|
90
|
+
const pathLevel = pathItem;
|
|
91
|
+
for (const [method, opVal] of Object.entries(pathItem)) {
|
|
92
|
+
const lowerMethod = method.toLowerCase();
|
|
93
|
+
if (!["get", "post", "put", "patch", "delete", "options", "head"].includes(lowerMethod)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const operation = opVal;
|
|
97
|
+
if (!operation || typeof operation !== "object")
|
|
98
|
+
continue;
|
|
99
|
+
let operationId = operation.operationId;
|
|
100
|
+
if (!operationId) {
|
|
101
|
+
operationId = `${lowerMethod}_${p}`;
|
|
102
|
+
}
|
|
103
|
+
const operationSlug = slugName(operationId);
|
|
104
|
+
if (seenOperationSlugs.has(operationSlug)) {
|
|
105
|
+
throw new Error(`Duplicate operation slug '${operationSlug}' derived from operationId '${operationId}'`);
|
|
106
|
+
}
|
|
107
|
+
seenOperationSlugs.add(operationSlug);
|
|
108
|
+
// Build request body schema
|
|
109
|
+
let bodySchema;
|
|
110
|
+
const rbSchema = operation.requestBody?.content?.["application/json"]?.schema;
|
|
111
|
+
if (rbSchema) {
|
|
112
|
+
const name = getRefName(rbSchema);
|
|
113
|
+
const id = schemaIdByName[name];
|
|
114
|
+
if (!id) {
|
|
115
|
+
throw new Error(`Unknown request body schema component '${name}'`);
|
|
116
|
+
}
|
|
117
|
+
bodySchema = { $ref: id + "#" };
|
|
118
|
+
}
|
|
119
|
+
// Build parameter schemas
|
|
120
|
+
const { paramsSchema, querySchema, headersSchema } = buildParamSchemas(pathLevel, operation, doc, schemaIdByName);
|
|
121
|
+
// Build response schemas
|
|
122
|
+
const responses = operation.responses || {};
|
|
123
|
+
const responseObj = {};
|
|
124
|
+
const explicitCodes = Object.keys(responses).filter(isNumeric);
|
|
125
|
+
for (const code of explicitCodes) {
|
|
126
|
+
const r = responses[code];
|
|
127
|
+
if (!r || typeof r !== "object")
|
|
128
|
+
continue;
|
|
129
|
+
const rSchema = r.content?.["application/json"]?.schema;
|
|
130
|
+
if (!rSchema) {
|
|
131
|
+
throw new Error(`Response ${code} for operation '${operationId}' is missing application/json schema`);
|
|
132
|
+
}
|
|
133
|
+
const name = getRefName(rSchema);
|
|
134
|
+
const id = schemaIdByName[name];
|
|
135
|
+
if (!id) {
|
|
136
|
+
throw new Error(`Unknown response schema component '${name}' for code ${code}`);
|
|
137
|
+
}
|
|
138
|
+
responseObj[code] = { $ref: id + "#" };
|
|
139
|
+
}
|
|
140
|
+
// Validate and get default response
|
|
141
|
+
const defaultResp = responses.default;
|
|
142
|
+
if (!defaultResp || typeof defaultResp !== "object") {
|
|
143
|
+
throw new Error(`Operation '${operationId}' is missing default response; required by gateway codegen`);
|
|
144
|
+
}
|
|
145
|
+
const defaultSchema = defaultResp.content?.["application/json"]?.schema;
|
|
146
|
+
if (!defaultSchema) {
|
|
147
|
+
throw new Error(`Operation '${operationId}' default response is missing application/json schema`);
|
|
148
|
+
}
|
|
149
|
+
const defaultName = getRefName(defaultSchema);
|
|
150
|
+
const defaultId = schemaIdByName[defaultName];
|
|
151
|
+
if (!defaultId) {
|
|
152
|
+
throw new Error(`Unknown default response schema component '${defaultName}' for operation '${operationId}'`);
|
|
153
|
+
}
|
|
154
|
+
// Backfill default response codes
|
|
155
|
+
for (const code of defaultResponseStatusCodes) {
|
|
156
|
+
const codeStr = String(code);
|
|
157
|
+
if (!responseObj[codeStr]) {
|
|
158
|
+
responseObj[codeStr] = { $ref: defaultId + "#" };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Build operation schema object
|
|
162
|
+
const opSchema = {
|
|
163
|
+
$id: `urn:services:${serviceSlug}:${versionSlug}:schemas:operations:${operationSlug}`,
|
|
164
|
+
response: responseObj,
|
|
165
|
+
};
|
|
166
|
+
if (bodySchema)
|
|
167
|
+
opSchema.body = bodySchema;
|
|
168
|
+
if (paramsSchema)
|
|
169
|
+
opSchema.params = paramsSchema;
|
|
170
|
+
if (querySchema)
|
|
171
|
+
opSchema.querystring = querySchema;
|
|
172
|
+
if (headersSchema)
|
|
173
|
+
opSchema.headers = headersSchema;
|
|
174
|
+
const opOutPath = path.join(opsDir, `${operationSlug}.json`);
|
|
175
|
+
fs.writeFileSync(opOutPath, JSON.stringify(opSchema, null, 2), "utf8");
|
|
176
|
+
operations.push({
|
|
177
|
+
operationSlug,
|
|
178
|
+
method: lowerMethod,
|
|
179
|
+
path: p,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return operations;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Emits schemas.ts module that registers all model schemas with Fastify
|
|
187
|
+
*
|
|
188
|
+
* Generated code:
|
|
189
|
+
* - Imports all JSON files from schemas/models/
|
|
190
|
+
* - Exports registerSchemas_{version}_{service}(fastify) function
|
|
191
|
+
* - Calls fastify.addSchema for each model schema
|
|
192
|
+
*
|
|
193
|
+
* @param {string} outDir - Root output directory
|
|
194
|
+
* @param {string} modelsDir - Directory containing model schema files
|
|
195
|
+
* @param {string} versionSlug - Version slug for function naming
|
|
196
|
+
* @param {string} serviceSlug - Service slug for function naming
|
|
197
|
+
*/
|
|
198
|
+
export function emitSchemasModule(outDir, modelsDir, versionSlug, serviceSlug) {
|
|
199
|
+
const modelFiles = fs.readdirSync(modelsDir).filter((f) => f.endsWith(".json"));
|
|
200
|
+
modelFiles.sort();
|
|
201
|
+
let schemasTs = "";
|
|
202
|
+
schemasTs += `import type { FastifyInstance } from "fastify";\n`;
|
|
203
|
+
modelFiles.forEach((file, idx) => {
|
|
204
|
+
const varName = `m${idx}`;
|
|
205
|
+
schemasTs += `import ${varName} from "./schemas/models/${file}" with { type: "json" };\n`;
|
|
206
|
+
});
|
|
207
|
+
schemasTs += `\nexport async function registerSchemas_${slugName(versionSlug)}_${slugName(serviceSlug)}(fastify: FastifyInstance) {\n`;
|
|
208
|
+
schemasTs += ` const schemas = [${modelFiles
|
|
209
|
+
.map((_, idx) => `m${idx}`)
|
|
210
|
+
.join(", ")}];\n`;
|
|
211
|
+
schemasTs += ` for (const s of schemas) {\n`;
|
|
212
|
+
schemasTs += ` fastify.addSchema(s as any);\n`;
|
|
213
|
+
schemasTs += ` }\n`;
|
|
214
|
+
schemasTs += `}\n`;
|
|
215
|
+
fs.writeFileSync(path.join(outDir, "schemas.ts"), schemasTs, "utf8");
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Emits individual route files and routes.ts aggregator module
|
|
219
|
+
*
|
|
220
|
+
* For each operation:
|
|
221
|
+
* - Creates routes/{slug}.ts with registerRoute_{slug} function
|
|
222
|
+
* - Imports operation schema from schemas/operations/{slug}.json
|
|
223
|
+
* - Defines fastify.route() with method, url, schema, and stub handler
|
|
224
|
+
*
|
|
225
|
+
* Then creates routes.ts:
|
|
226
|
+
* - Imports all route registration functions
|
|
227
|
+
* - Exports registerRoutes_{version}_{service}(fastify) function
|
|
228
|
+
*
|
|
229
|
+
* @param {string} outDir - Root output directory
|
|
230
|
+
* @param {string} routesDir - Directory for individual route files
|
|
231
|
+
* @param {string} versionSlug - Version slug for function naming
|
|
232
|
+
* @param {string} serviceSlug - Service slug for function naming
|
|
233
|
+
* @param {OperationMetadata[]} operations - Array of operation metadata
|
|
234
|
+
* @param {"js"|"ts"|"bare"} importsMode - Import-extension mode for generated TypeScript modules
|
|
235
|
+
*/
|
|
236
|
+
export function emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode) {
|
|
237
|
+
fs.mkdirSync(routesDir, { recursive: true });
|
|
238
|
+
// Sort operations for deterministic output
|
|
239
|
+
operations.sort((a, b) => a.operationSlug.localeCompare(b.operationSlug));
|
|
240
|
+
const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
|
|
241
|
+
let routesTs = `import type { FastifyInstance } from "fastify";\n`;
|
|
242
|
+
operations.forEach((op) => {
|
|
243
|
+
const fnName = `registerRoute_${slugName(versionSlug)}_${slugName(serviceSlug)}_${op.operationSlug.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
244
|
+
const routeFileBase = op.operationSlug;
|
|
245
|
+
routesTs += `import { ${fnName} } from "./routes/${routeFileBase}${suffix}";\n`;
|
|
246
|
+
// Generate individual route file
|
|
247
|
+
let routeTs = "";
|
|
248
|
+
routeTs += `import type { FastifyInstance } from "fastify";\n`;
|
|
249
|
+
routeTs += `import schema from "../schemas/operations/${op.operationSlug}.json" with { type: "json" };\n\n`;
|
|
250
|
+
routeTs += `export async function ${fnName}(fastify: FastifyInstance) {\n`;
|
|
251
|
+
routeTs += ` fastify.route({\n`;
|
|
252
|
+
routeTs += ` method: "${op.method.toUpperCase()}",\n`;
|
|
253
|
+
routeTs += ` url: "${op.path}",\n`;
|
|
254
|
+
routeTs += ` schema: schema as any,\n`;
|
|
255
|
+
routeTs += ` handler: async (request, reply) => {\n`;
|
|
256
|
+
routeTs += ` throw new Error("Not implemented");\n`;
|
|
257
|
+
routeTs += ` }\n`;
|
|
258
|
+
routeTs += ` });\n`;
|
|
259
|
+
routeTs += `}\n`;
|
|
260
|
+
fs.writeFileSync(path.join(routesDir, `${routeFileBase}.ts`), routeTs, "utf8");
|
|
261
|
+
});
|
|
262
|
+
const routeFnName = `registerRoutes_${slugName(versionSlug)}_${slugName(serviceSlug)}`;
|
|
263
|
+
routesTs += `\nexport async function ${routeFnName}(fastify: FastifyInstance) {\n`;
|
|
264
|
+
operations.forEach((op) => {
|
|
265
|
+
const fnName = `registerRoute_${slugName(versionSlug)}_${slugName(serviceSlug)}_${op.operationSlug.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
266
|
+
routesTs += ` await ${fnName}(fastify);\n`;
|
|
267
|
+
});
|
|
268
|
+
routesTs += `}\n`;
|
|
269
|
+
fs.writeFileSync(path.join(outDir, "routes.ts"), routesTs, "utf8");
|
|
270
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Generation Helpers
|
|
3
|
+
*
|
|
4
|
+
* This module provides utility functions for generating Fastify gateway code from OpenAPI 3.1 specifications.
|
|
5
|
+
* It handles slug generation, schema reference rewriting, parameter resolution, and version/service detection.
|
|
6
|
+
*
|
|
7
|
+
* Key capabilities:
|
|
8
|
+
* - Deterministic slug generation from OpenAPI component names
|
|
9
|
+
* - Schema $ref rewriting from OpenAPI references to URN-style IDs
|
|
10
|
+
* - Parameter resolution with path-level and operation-level override support
|
|
11
|
+
* - Automatic version/service detection from OpenAPI paths
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* OpenAPI document structure (minimal type for internal use)
|
|
15
|
+
*/
|
|
16
|
+
export interface OpenAPIDocument {
|
|
17
|
+
openapi: string;
|
|
18
|
+
info?: any;
|
|
19
|
+
paths: Record<string, any>;
|
|
20
|
+
components?: {
|
|
21
|
+
schemas?: Record<string, any>;
|
|
22
|
+
parameters?: Record<string, any>;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Converts a name to a deterministic slug for use in file names and URN IDs
|
|
27
|
+
*
|
|
28
|
+
* Rules:
|
|
29
|
+
* - Lowercase all characters
|
|
30
|
+
* - Replace non-alphanumeric sequences with single underscores
|
|
31
|
+
* - Strip leading/trailing underscores
|
|
32
|
+
* - Collapse multiple underscores to single underscore
|
|
33
|
+
*
|
|
34
|
+
* @param {string} name - Original name to convert
|
|
35
|
+
* @returns {string} - Slugified name
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* slugName("GetUserDetails") // "getuserdetails"
|
|
39
|
+
* slugName("User-Profile_Data") // "user_profile_data"
|
|
40
|
+
*/
|
|
41
|
+
export declare function slugName(name: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Checks if a status code string is numeric
|
|
44
|
+
*
|
|
45
|
+
* @param {string} code - Status code to check
|
|
46
|
+
* @returns {boolean} - True if the code is numeric
|
|
47
|
+
*/
|
|
48
|
+
export declare function isNumericStatus(code: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Recursively rewrites all $ref properties in a schema object from OpenAPI component references
|
|
51
|
+
* to URN-style schema IDs
|
|
52
|
+
*
|
|
53
|
+
* Contract enforcement:
|
|
54
|
+
* - All $ref values must start with "#/components/schemas/"
|
|
55
|
+
* - The referenced schema name must exist in schemaIdByName map
|
|
56
|
+
* - Throws if unknown schema is referenced
|
|
57
|
+
*
|
|
58
|
+
* @param {any} node - Schema object or primitive value to process
|
|
59
|
+
* @param {Record<string, string>} schemaIdByName - Map from OpenAPI component name to URN ID
|
|
60
|
+
* @returns {any} - Deep clone with rewritten $ref properties
|
|
61
|
+
* @throws {Error} If unknown schema reference is encountered
|
|
62
|
+
*/
|
|
63
|
+
export declare function rewriteSchemaRefs(node: any, schemaIdByName: Record<string, string>): any;
|
|
64
|
+
/**
|
|
65
|
+
* Extracts the component schema name from a $ref object
|
|
66
|
+
*
|
|
67
|
+
* Contract enforcement (strict validation):
|
|
68
|
+
* - Schema must be an object with a $ref property
|
|
69
|
+
* - $ref must start with "#/components/schemas/"
|
|
70
|
+
* - Throws if inline schema or invalid $ref format
|
|
71
|
+
*
|
|
72
|
+
* @param {any} schema - Schema object expected to contain a $ref
|
|
73
|
+
* @returns {string} - Extracted schema component name
|
|
74
|
+
* @throws {Error} If schema is not a proper $ref or uses unsupported format
|
|
75
|
+
*/
|
|
76
|
+
export declare function getJsonSchemaRefName(schema: any): string;
|
|
77
|
+
/**
|
|
78
|
+
* Resolves a parameter object or $ref to a component parameter
|
|
79
|
+
*
|
|
80
|
+
* Supports:
|
|
81
|
+
* - Inline parameter objects (returned as-is)
|
|
82
|
+
* - $ref to "#/components/parameters/Name" (resolved from document)
|
|
83
|
+
*
|
|
84
|
+
* @param {any} paramOrRef - Parameter object or reference
|
|
85
|
+
* @param {OpenAPIDocument} doc - OpenAPI document containing components
|
|
86
|
+
* @returns {any} - Resolved parameter object
|
|
87
|
+
* @throws {Error} If $ref format is invalid or referenced parameter not found
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveParameter(paramOrRef: any, doc: OpenAPIDocument): any;
|
|
90
|
+
/**
|
|
91
|
+
* Result of building Fastify-compatible param schemas from OpenAPI parameters
|
|
92
|
+
*/
|
|
93
|
+
export interface ParamSchemaBuildResult {
|
|
94
|
+
paramsSchema?: any;
|
|
95
|
+
querySchema?: any;
|
|
96
|
+
headersSchema?: any;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Builds Fastify-compatible params/querystring/headers schemas from OpenAPI parameters
|
|
100
|
+
*
|
|
101
|
+
* Logic:
|
|
102
|
+
* 1. Merges path-level and operation-level parameters (operation overrides path)
|
|
103
|
+
* 2. Partitions by "in" field: path -> params, query -> querystring, header -> headers
|
|
104
|
+
* 3. Builds inline object schemas with properties/required arrays
|
|
105
|
+
* 4. Lowercases header names (Fastify convention)
|
|
106
|
+
* 5. Rewrites any schema $refs to URN format
|
|
107
|
+
*
|
|
108
|
+
* @param {any} pathItem - Path-level object (may have parameters array)
|
|
109
|
+
* @param {any} operation - Operation object (may have parameters array)
|
|
110
|
+
* @param {OpenAPIDocument} doc - OpenAPI document for resolving parameter refs
|
|
111
|
+
* @param {Record<string, string>} schemaIdByName - Schema name to URN ID mapping
|
|
112
|
+
* @returns {ParamSchemaBuildResult} - Object with optional params/querystring/headers schemas
|
|
113
|
+
*/
|
|
114
|
+
export declare function buildParamSchemasForOperation(pathItem: any, operation: any, doc: OpenAPIDocument, schemaIdByName: Record<string, string>): ParamSchemaBuildResult;
|
|
115
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAClC,CAAC;CACH;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,GAAG,CAqBxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAcxD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,GAAG,GAAG,CAiB3E;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,aAAa,CAAC,EAAE,GAAG,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,GAAG,EACb,SAAS,EAAE,GAAG,EACd,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,sBAAsB,CAmFxB"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Generation Helpers
|
|
3
|
+
*
|
|
4
|
+
* This module provides utility functions for generating Fastify gateway code from OpenAPI 3.1 specifications.
|
|
5
|
+
* It handles slug generation, schema reference rewriting, parameter resolution, and version/service detection.
|
|
6
|
+
*
|
|
7
|
+
* Key capabilities:
|
|
8
|
+
* - Deterministic slug generation from OpenAPI component names
|
|
9
|
+
* - Schema $ref rewriting from OpenAPI references to URN-style IDs
|
|
10
|
+
* - Parameter resolution with path-level and operation-level override support
|
|
11
|
+
* - Automatic version/service detection from OpenAPI paths
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Converts a name to a deterministic slug for use in file names and URN IDs
|
|
15
|
+
*
|
|
16
|
+
* Rules:
|
|
17
|
+
* - Lowercase all characters
|
|
18
|
+
* - Replace non-alphanumeric sequences with single underscores
|
|
19
|
+
* - Strip leading/trailing underscores
|
|
20
|
+
* - Collapse multiple underscores to single underscore
|
|
21
|
+
*
|
|
22
|
+
* @param {string} name - Original name to convert
|
|
23
|
+
* @returns {string} - Slugified name
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* slugName("GetUserDetails") // "getuserdetails"
|
|
27
|
+
* slugName("User-Profile_Data") // "user_profile_data"
|
|
28
|
+
*/
|
|
29
|
+
export function slugName(name) {
|
|
30
|
+
const lower = String(name ?? "").toLowerCase();
|
|
31
|
+
const collapsed = lower.replace(/[^a-z0-9]+/g, "_");
|
|
32
|
+
return collapsed.replace(/^_+|_+$/g, "");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Checks if a status code string is numeric
|
|
36
|
+
*
|
|
37
|
+
* @param {string} code - Status code to check
|
|
38
|
+
* @returns {boolean} - True if the code is numeric
|
|
39
|
+
*/
|
|
40
|
+
export function isNumericStatus(code) {
|
|
41
|
+
return /^\d+$/.test(code);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Recursively rewrites all $ref properties in a schema object from OpenAPI component references
|
|
45
|
+
* to URN-style schema IDs
|
|
46
|
+
*
|
|
47
|
+
* Contract enforcement:
|
|
48
|
+
* - All $ref values must start with "#/components/schemas/"
|
|
49
|
+
* - The referenced schema name must exist in schemaIdByName map
|
|
50
|
+
* - Throws if unknown schema is referenced
|
|
51
|
+
*
|
|
52
|
+
* @param {any} node - Schema object or primitive value to process
|
|
53
|
+
* @param {Record<string, string>} schemaIdByName - Map from OpenAPI component name to URN ID
|
|
54
|
+
* @returns {any} - Deep clone with rewritten $ref properties
|
|
55
|
+
* @throws {Error} If unknown schema reference is encountered
|
|
56
|
+
*/
|
|
57
|
+
export function rewriteSchemaRefs(node, schemaIdByName) {
|
|
58
|
+
if (Array.isArray(node)) {
|
|
59
|
+
return node.map((v) => rewriteSchemaRefs(v, schemaIdByName));
|
|
60
|
+
}
|
|
61
|
+
if (node && typeof node === "object") {
|
|
62
|
+
const out = {};
|
|
63
|
+
for (const [k, v] of Object.entries(node)) {
|
|
64
|
+
if (k === "$ref" && typeof v === "string" && v.startsWith("#/components/schemas/")) {
|
|
65
|
+
const name = v.slice("#/components/schemas/".length);
|
|
66
|
+
const id = schemaIdByName[name];
|
|
67
|
+
if (!id) {
|
|
68
|
+
throw new Error(`Unknown schema reference '${name}' in $ref '${v}'`);
|
|
69
|
+
}
|
|
70
|
+
out["$ref"] = id + "#";
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
out[k] = rewriteSchemaRefs(v, schemaIdByName);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
return node;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Extracts the component schema name from a $ref object
|
|
82
|
+
*
|
|
83
|
+
* Contract enforcement (strict validation):
|
|
84
|
+
* - Schema must be an object with a $ref property
|
|
85
|
+
* - $ref must start with "#/components/schemas/"
|
|
86
|
+
* - Throws if inline schema or invalid $ref format
|
|
87
|
+
*
|
|
88
|
+
* @param {any} schema - Schema object expected to contain a $ref
|
|
89
|
+
* @returns {string} - Extracted schema component name
|
|
90
|
+
* @throws {Error} If schema is not a proper $ref or uses unsupported format
|
|
91
|
+
*/
|
|
92
|
+
export function getJsonSchemaRefName(schema) {
|
|
93
|
+
if (!schema || typeof schema !== "object" || typeof schema.$ref !== "string") {
|
|
94
|
+
throw new Error("Expected schema with $ref to '#/components/schemas/Name', but got inline or missing $ref");
|
|
95
|
+
}
|
|
96
|
+
const ref = schema.$ref;
|
|
97
|
+
const prefix = "#/components/schemas/";
|
|
98
|
+
if (!ref.startsWith(prefix)) {
|
|
99
|
+
throw new Error(`Unsupported $ref '${ref}'. Expected to start with '${prefix}'.`);
|
|
100
|
+
}
|
|
101
|
+
return ref.slice(prefix.length);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Resolves a parameter object or $ref to a component parameter
|
|
105
|
+
*
|
|
106
|
+
* Supports:
|
|
107
|
+
* - Inline parameter objects (returned as-is)
|
|
108
|
+
* - $ref to "#/components/parameters/Name" (resolved from document)
|
|
109
|
+
*
|
|
110
|
+
* @param {any} paramOrRef - Parameter object or reference
|
|
111
|
+
* @param {OpenAPIDocument} doc - OpenAPI document containing components
|
|
112
|
+
* @returns {any} - Resolved parameter object
|
|
113
|
+
* @throws {Error} If $ref format is invalid or referenced parameter not found
|
|
114
|
+
*/
|
|
115
|
+
export function resolveParameter(paramOrRef, doc) {
|
|
116
|
+
if (paramOrRef && typeof paramOrRef === "object" && typeof paramOrRef.$ref === "string") {
|
|
117
|
+
const ref = paramOrRef.$ref;
|
|
118
|
+
const prefix = "#/components/parameters/";
|
|
119
|
+
if (!ref.startsWith(prefix)) {
|
|
120
|
+
throw new Error(`Unsupported parameter $ref '${ref}'. Expected to start with '${prefix}'.`);
|
|
121
|
+
}
|
|
122
|
+
const name = ref.slice(prefix.length);
|
|
123
|
+
const p = doc.components?.parameters?.[name];
|
|
124
|
+
if (!p) {
|
|
125
|
+
throw new Error(`Parameter '${name}' not found in components.parameters`);
|
|
126
|
+
}
|
|
127
|
+
return p;
|
|
128
|
+
}
|
|
129
|
+
return paramOrRef;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Builds Fastify-compatible params/querystring/headers schemas from OpenAPI parameters
|
|
133
|
+
*
|
|
134
|
+
* Logic:
|
|
135
|
+
* 1. Merges path-level and operation-level parameters (operation overrides path)
|
|
136
|
+
* 2. Partitions by "in" field: path -> params, query -> querystring, header -> headers
|
|
137
|
+
* 3. Builds inline object schemas with properties/required arrays
|
|
138
|
+
* 4. Lowercases header names (Fastify convention)
|
|
139
|
+
* 5. Rewrites any schema $refs to URN format
|
|
140
|
+
*
|
|
141
|
+
* @param {any} pathItem - Path-level object (may have parameters array)
|
|
142
|
+
* @param {any} operation - Operation object (may have parameters array)
|
|
143
|
+
* @param {OpenAPIDocument} doc - OpenAPI document for resolving parameter refs
|
|
144
|
+
* @param {Record<string, string>} schemaIdByName - Schema name to URN ID mapping
|
|
145
|
+
* @returns {ParamSchemaBuildResult} - Object with optional params/querystring/headers schemas
|
|
146
|
+
*/
|
|
147
|
+
export function buildParamSchemasForOperation(pathItem, operation, doc, schemaIdByName) {
|
|
148
|
+
const all = [];
|
|
149
|
+
if (Array.isArray(pathItem?.parameters)) {
|
|
150
|
+
all.push(...pathItem.parameters);
|
|
151
|
+
}
|
|
152
|
+
if (Array.isArray(operation?.parameters)) {
|
|
153
|
+
all.push(...operation.parameters);
|
|
154
|
+
}
|
|
155
|
+
// Map by (in + ":" + name), last one wins (operation-level override)
|
|
156
|
+
const byKey = {};
|
|
157
|
+
for (const p of all) {
|
|
158
|
+
const resolved = resolveParameter(p, doc);
|
|
159
|
+
if (!resolved || typeof resolved !== "object")
|
|
160
|
+
continue;
|
|
161
|
+
const loc = resolved.in;
|
|
162
|
+
const name = resolved.name;
|
|
163
|
+
if (!loc || !name)
|
|
164
|
+
continue;
|
|
165
|
+
const key = `${loc}:${name}`;
|
|
166
|
+
byKey[key] = resolved;
|
|
167
|
+
}
|
|
168
|
+
const pathParams = [];
|
|
169
|
+
const queryParams = [];
|
|
170
|
+
const headerParams = [];
|
|
171
|
+
for (const param of Object.values(byKey)) {
|
|
172
|
+
switch (param.in) {
|
|
173
|
+
case "path":
|
|
174
|
+
pathParams.push(param);
|
|
175
|
+
break;
|
|
176
|
+
case "query":
|
|
177
|
+
queryParams.push(param);
|
|
178
|
+
break;
|
|
179
|
+
case "header":
|
|
180
|
+
headerParams.push(param);
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
// ignore cookie or other locations for now
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function buildObjectFromParams(params, header) {
|
|
188
|
+
if (params.length === 0)
|
|
189
|
+
return undefined;
|
|
190
|
+
const properties = {};
|
|
191
|
+
const required = [];
|
|
192
|
+
for (const p of params) {
|
|
193
|
+
let name = p.name;
|
|
194
|
+
if (header) {
|
|
195
|
+
name = name.toLowerCase();
|
|
196
|
+
}
|
|
197
|
+
let propSchema = {};
|
|
198
|
+
if (p.schema) {
|
|
199
|
+
propSchema = rewriteSchemaRefs(p.schema, schemaIdByName);
|
|
200
|
+
}
|
|
201
|
+
properties[name] = propSchema;
|
|
202
|
+
if (p.required) {
|
|
203
|
+
required.push(name);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const obj = {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties,
|
|
209
|
+
additionalProperties: false,
|
|
210
|
+
};
|
|
211
|
+
if (required.length > 0) {
|
|
212
|
+
obj.required = required;
|
|
213
|
+
}
|
|
214
|
+
return obj;
|
|
215
|
+
}
|
|
216
|
+
const paramsSchema = buildObjectFromParams(pathParams, false);
|
|
217
|
+
const querySchema = buildObjectFromParams(queryParams, false);
|
|
218
|
+
const headersSchema = buildObjectFromParams(headerParams, true);
|
|
219
|
+
return {
|
|
220
|
+
paramsSchema,
|
|
221
|
+
querySchema,
|
|
222
|
+
headersSchema,
|
|
223
|
+
};
|
|
224
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CompilerOptions } from "./config.js";
|
|
2
2
|
export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
3
|
+
export { generateGateway } from "./gateway/generateGateway.js";
|
|
3
4
|
export { runGenerationPipeline } from "./pipeline.js";
|
|
4
5
|
/**
|
|
5
6
|
* Compiles a WSDL file to TypeScript client code
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAUjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GACjE,OAAO,CAAC,IAAI,CAAC,CAsDf"}
|