@techspokes/typescript-wsdl-client 0.9.0 → 0.9.2
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/LICENSE +21 -21
- package/README.md +222 -40
- package/dist/app/generateApp.d.ts +48 -0
- package/dist/app/generateApp.d.ts.map +1 -0
- package/dist/app/generateApp.js +553 -0
- package/dist/cli.js +145 -2
- package/dist/gateway/generators.d.ts.map +1 -1
- package/dist/gateway/generators.js +6 -3
- package/dist/gateway/helpers.d.ts +18 -0
- package/dist/gateway/helpers.d.ts.map +1 -1
- package/dist/gateway/helpers.js +117 -0
- package/dist/openapi/generateSchemas.js +1 -2
- package/dist/pipeline.d.ts +9 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +19 -1
- package/package.json +3 -2
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
// noinspection HttpUrlsUsage
|
|
2
|
+
/**
|
|
3
|
+
* Fastify App Generator
|
|
4
|
+
*
|
|
5
|
+
* This module generates a runnable Fastify application that imports and uses
|
|
6
|
+
* the generated gateway plugin and SOAP client. The app serves the OpenAPI spec,
|
|
7
|
+
* health checks, and all gateway routes.
|
|
8
|
+
*
|
|
9
|
+
* Core capabilities:
|
|
10
|
+
* - Generates server.ts with Fastify setup and plugin registration
|
|
11
|
+
* - Generates config.ts with environment-based configuration
|
|
12
|
+
* - Generates .env.example with required/optional environment variables
|
|
13
|
+
* - Generates README.md with instructions for running the app
|
|
14
|
+
* - Optionally copies OpenAPI spec into app directory
|
|
15
|
+
* - Validates required inputs (client-dir, gateway-dir, openapi-file, catalog-file)
|
|
16
|
+
*/
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { deriveClientName } from "../util/tools.js";
|
|
20
|
+
import { success } from "../util/cli.js";
|
|
21
|
+
/**
|
|
22
|
+
* Validates that all required files and directories exist
|
|
23
|
+
*
|
|
24
|
+
* @param {GenerateAppOptions} opts - App generation options
|
|
25
|
+
* @throws {Error} If any required file or directory doesn't exist
|
|
26
|
+
*/
|
|
27
|
+
function validateRequiredFiles(opts) {
|
|
28
|
+
const checks = [
|
|
29
|
+
{ path: opts.clientDir, type: "directory", label: "Client directory" },
|
|
30
|
+
{ path: opts.gatewayDir, type: "directory", label: "Gateway directory" },
|
|
31
|
+
{ path: opts.catalogFile, type: "file", label: "Catalog file" },
|
|
32
|
+
{ path: opts.openapiFile, type: "file", label: "OpenAPI file" },
|
|
33
|
+
];
|
|
34
|
+
for (const check of checks) {
|
|
35
|
+
const exists = fs.existsSync(check.path);
|
|
36
|
+
if (!exists) {
|
|
37
|
+
throw new Error(`${check.label} does not exist: ${check.path}`);
|
|
38
|
+
}
|
|
39
|
+
const stat = fs.statSync(check.path);
|
|
40
|
+
if (check.type === "directory" && !stat.isDirectory()) {
|
|
41
|
+
throw new Error(`${check.label} is not a directory: ${check.path}`);
|
|
42
|
+
}
|
|
43
|
+
if (check.type === "file" && !stat.isFile()) {
|
|
44
|
+
throw new Error(`${check.label} is not a file: ${check.path}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Check for client entrypoint (try multiple extensions)
|
|
48
|
+
const clientEntrypointFound = ["", ".ts", ".js"].some(ext => {
|
|
49
|
+
return fs.existsSync(path.join(opts.clientDir, `client${ext}`));
|
|
50
|
+
});
|
|
51
|
+
if (!clientEntrypointFound) {
|
|
52
|
+
throw new Error(`Client entrypoint does not exist in ${opts.clientDir} (tried client.ts, client.js, client)`);
|
|
53
|
+
}
|
|
54
|
+
// Check for gateway plugin entrypoint (try multiple extensions)
|
|
55
|
+
const gatewayEntrypointFound = ["", ".ts", ".js"].some(ext => {
|
|
56
|
+
return fs.existsSync(path.join(opts.gatewayDir, `plugin${ext}`));
|
|
57
|
+
});
|
|
58
|
+
if (!gatewayEntrypointFound) {
|
|
59
|
+
throw new Error(`Gateway plugin entrypoint does not exist in ${opts.gatewayDir} (tried plugin.ts, plugin.js, plugin)`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns the file extension for the import mode
|
|
64
|
+
*
|
|
65
|
+
* @param {string} imports - Import mode (js, ts, or bare)
|
|
66
|
+
* @returns {string} - File extension with leading dot or empty string for bare
|
|
67
|
+
*/
|
|
68
|
+
function getExtension(imports) {
|
|
69
|
+
if (imports === "js")
|
|
70
|
+
return ".js";
|
|
71
|
+
if (imports === "ts")
|
|
72
|
+
return ".ts";
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns the file extension for executable app files (server, config)
|
|
77
|
+
* These always need extensions even with bare imports since they are entry points
|
|
78
|
+
*
|
|
79
|
+
* @param {string} imports - Import mode (js, ts, or bare)
|
|
80
|
+
* @returns {string} - File extension with leading dot
|
|
81
|
+
*/
|
|
82
|
+
function getAppFileExtension(imports) {
|
|
83
|
+
if (imports === "ts")
|
|
84
|
+
return ".ts";
|
|
85
|
+
return ".js"; // Use .js for both "js" and "bare" modes
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Computes a relative import path from source to target
|
|
89
|
+
*
|
|
90
|
+
* @param {string} from - Source directory
|
|
91
|
+
* @param {string} to - Target file or directory
|
|
92
|
+
* @param {string} imports - Import mode (js, ts, or bare)
|
|
93
|
+
* @returns {string} - Relative import specifier with proper extension
|
|
94
|
+
*/
|
|
95
|
+
function computeRelativeImport(from, to, imports) {
|
|
96
|
+
const rel = path.relative(from, to);
|
|
97
|
+
// Normalize to POSIX separators
|
|
98
|
+
const posix = rel.split(path.sep).join("/");
|
|
99
|
+
// Ensure it starts with ./ or ../
|
|
100
|
+
const prefixed = posix.startsWith(".") ? posix : `./${posix}`;
|
|
101
|
+
// Apply import extension rules
|
|
102
|
+
const ext = getExtension(imports);
|
|
103
|
+
if (ext) {
|
|
104
|
+
return prefixed + ext;
|
|
105
|
+
}
|
|
106
|
+
return prefixed;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Reads and parses the catalog file
|
|
110
|
+
*
|
|
111
|
+
* @param {string} catalogPath - Path to catalog.json
|
|
112
|
+
* @returns {any} - Parsed catalog object
|
|
113
|
+
*/
|
|
114
|
+
function readCatalog(catalogPath) {
|
|
115
|
+
try {
|
|
116
|
+
const content = fs.readFileSync(catalogPath, "utf-8");
|
|
117
|
+
return JSON.parse(content);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
throw new Error(`Failed to read or parse catalog file: ${err instanceof Error ? err.message : String(err)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Derives default WSDL source from catalog if available
|
|
125
|
+
*
|
|
126
|
+
* @param {any} catalog - Parsed catalog object
|
|
127
|
+
* @returns {string|undefined} - WSDL source from catalog or undefined
|
|
128
|
+
*/
|
|
129
|
+
function getCatalogWsdlSource(catalog) {
|
|
130
|
+
return catalog?.options?.wsdl || catalog?.wsdlUri;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generates server.ts file
|
|
134
|
+
*
|
|
135
|
+
* @param {string} appDir - App output directory
|
|
136
|
+
* @param {GenerateAppOptions} opts - App generation options
|
|
137
|
+
* @param {string} clientClassName - Derived client class name
|
|
138
|
+
*/
|
|
139
|
+
function generateServerFile(appDir, opts, clientClassName) {
|
|
140
|
+
const imports = opts.imports || "js";
|
|
141
|
+
const ext = getAppFileExtension(imports); // Use getAppFileExtension for executable entry point
|
|
142
|
+
const configImport = computeRelativeImport(appDir, path.join(appDir, "config"), imports);
|
|
143
|
+
const gatewayPluginImport = computeRelativeImport(appDir, path.join(opts.gatewayDir, "plugin"), imports);
|
|
144
|
+
const clientImport = computeRelativeImport(appDir, path.join(opts.clientDir, "client"), imports);
|
|
145
|
+
// For OpenAPI serving, we need to read and parse the file at startup in ESM mode
|
|
146
|
+
const openapiServeLogic = opts.openapiMode === "copy"
|
|
147
|
+
? `
|
|
148
|
+
// Read and parse OpenAPI spec at startup
|
|
149
|
+
const openapiSpecPath = path.join(__dirname, "openapi.json");
|
|
150
|
+
const openapiSpec = JSON.parse(fs.readFileSync(openapiSpecPath, "utf-8"));
|
|
151
|
+
|
|
152
|
+
// Serve OpenAPI specification
|
|
153
|
+
fastify.get("/openapi.json", async () => {
|
|
154
|
+
return openapiSpec;
|
|
155
|
+
});`
|
|
156
|
+
: `
|
|
157
|
+
// Serve OpenAPI specification from original file
|
|
158
|
+
const openapiSpecPath = path.resolve(__dirname, "${computeRelativeImport(appDir, opts.openapiFile, "bare")}");
|
|
159
|
+
const openapiSpec = JSON.parse(fs.readFileSync(openapiSpecPath, "utf-8"));
|
|
160
|
+
|
|
161
|
+
fastify.get("/openapi.json", async () => {
|
|
162
|
+
return openapiSpec;
|
|
163
|
+
});`;
|
|
164
|
+
const content = `/**
|
|
165
|
+
* Generated Fastify Application
|
|
166
|
+
*
|
|
167
|
+
* This file bootstraps a Fastify server that:
|
|
168
|
+
* - Loads configuration from environment variables
|
|
169
|
+
* - Instantiates the SOAP client
|
|
170
|
+
* - Registers the gateway plugin
|
|
171
|
+
* - Serves the OpenAPI specification
|
|
172
|
+
* - Provides health check endpoint
|
|
173
|
+
*
|
|
174
|
+
* Auto-generated - do not edit manually.
|
|
175
|
+
*/
|
|
176
|
+
import fs from "node:fs";
|
|
177
|
+
import path from "node:path";
|
|
178
|
+
import { fileURLToPath } from "node:url";
|
|
179
|
+
import { dirname } from "node:path";
|
|
180
|
+
import Fastify from "fastify";
|
|
181
|
+
import { loadConfig } from "${configImport}";
|
|
182
|
+
import gatewayPlugin from "${gatewayPluginImport}";
|
|
183
|
+
import { ${clientClassName} } from "${clientImport}";
|
|
184
|
+
|
|
185
|
+
// ES module dirname/filename helpers
|
|
186
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
187
|
+
const __dirname = dirname(__filename);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Main application entry point
|
|
191
|
+
*/
|
|
192
|
+
async function main() {
|
|
193
|
+
// Load configuration from environment
|
|
194
|
+
const config = loadConfig();
|
|
195
|
+
|
|
196
|
+
// Create Fastify instance
|
|
197
|
+
const fastify = Fastify({
|
|
198
|
+
logger: config.logger,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Instantiate SOAP client
|
|
202
|
+
const client = new ${clientClassName}({
|
|
203
|
+
source: config.wsdlSource,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Register gateway plugin
|
|
207
|
+
await fastify.register(gatewayPlugin, {
|
|
208
|
+
client,
|
|
209
|
+
prefix: config.prefix,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Health check endpoint
|
|
213
|
+
fastify.get("/health", async () => {
|
|
214
|
+
return { ok: true };
|
|
215
|
+
});
|
|
216
|
+
${openapiServeLogic}
|
|
217
|
+
|
|
218
|
+
// Start server
|
|
219
|
+
try {
|
|
220
|
+
await fastify.listen({
|
|
221
|
+
host: config.host,
|
|
222
|
+
port: config.port,
|
|
223
|
+
});
|
|
224
|
+
fastify.log.info(\`Server listening on http://\${config.host}:\${config.port}\`);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
fastify.log.error(err);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Handle shutdown gracefully
|
|
232
|
+
process.on("SIGINT", () => {
|
|
233
|
+
console.log("\\nReceived SIGINT, shutting down gracefully...");
|
|
234
|
+
process.exit(0);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
process.on("SIGTERM", () => {
|
|
238
|
+
console.log("\\nReceived SIGTERM, shutting down gracefully...");
|
|
239
|
+
process.exit(0);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Run the application
|
|
243
|
+
main().catch((err) => {
|
|
244
|
+
console.error("Failed to start application:", err);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
});
|
|
247
|
+
`;
|
|
248
|
+
fs.writeFileSync(path.join(appDir, `server${ext}`), content, "utf-8");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Generates config.ts file
|
|
252
|
+
*
|
|
253
|
+
* @param {string} appDir - App output directory
|
|
254
|
+
* @param {GenerateAppOptions} opts - App generation options
|
|
255
|
+
* @param {string|undefined} defaultWsdlSource - Default WSDL source from catalog
|
|
256
|
+
*/
|
|
257
|
+
function generateConfigFile(appDir, opts, defaultWsdlSource) {
|
|
258
|
+
const imports = opts.imports || "js";
|
|
259
|
+
const ext = getAppFileExtension(imports); // Use getAppFileExtension for executable entry point
|
|
260
|
+
const defaultHost = opts.host || "127.0.0.1";
|
|
261
|
+
const defaultPort = opts.port || 3000;
|
|
262
|
+
const defaultPrefix = opts.prefix || "";
|
|
263
|
+
const defaultLogger = opts.logger !== false;
|
|
264
|
+
// For .js files, we need to generate plain JavaScript (no TypeScript types)
|
|
265
|
+
// For .ts files, we can use TypeScript syntax
|
|
266
|
+
const isTypeScript = ext === ".ts";
|
|
267
|
+
const typeAnnotations = isTypeScript ? `
|
|
268
|
+
/**
|
|
269
|
+
* Application configuration interface
|
|
270
|
+
*/
|
|
271
|
+
export interface AppConfig {
|
|
272
|
+
wsdlSource: string;
|
|
273
|
+
host: string;
|
|
274
|
+
port: number;
|
|
275
|
+
prefix: string;
|
|
276
|
+
logger: boolean;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Loads configuration from environment variables
|
|
281
|
+
*
|
|
282
|
+
* @returns {AppConfig} - Application configuration
|
|
283
|
+
* @throws {Error} If required configuration is missing
|
|
284
|
+
*/` : `
|
|
285
|
+
/**
|
|
286
|
+
* Loads configuration from environment variables
|
|
287
|
+
*
|
|
288
|
+
* @returns {object} - Application configuration with wsdlSource, host, port, prefix, logger
|
|
289
|
+
* @throws {Error} If required configuration is missing
|
|
290
|
+
*/`;
|
|
291
|
+
const content = `/**
|
|
292
|
+
* Application Configuration
|
|
293
|
+
*
|
|
294
|
+
* Loads configuration from environment variables with sensible defaults.
|
|
295
|
+
* Configuration precedence:
|
|
296
|
+
* 1. Environment variables (runtime overrides)
|
|
297
|
+
* 2. Catalog defaults (generation-time recorded values)
|
|
298
|
+
* 3. Hard defaults (defined in this file)
|
|
299
|
+
*
|
|
300
|
+
* Auto-generated - do not edit manually.
|
|
301
|
+
*/
|
|
302
|
+
${typeAnnotations}
|
|
303
|
+
export function loadConfig() {
|
|
304
|
+
// WSDL source: required from env or catalog default
|
|
305
|
+
const wsdlSource = process.env.WSDL_SOURCE${defaultWsdlSource ? ` || "${defaultWsdlSource}"` : ""};
|
|
306
|
+
if (!wsdlSource) {
|
|
307
|
+
throw new Error("WSDL_SOURCE environment variable is required");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Host: default to ${defaultHost}
|
|
311
|
+
const host = process.env.HOST || "${defaultHost}";
|
|
312
|
+
|
|
313
|
+
// Port: default to ${defaultPort}
|
|
314
|
+
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : ${defaultPort};
|
|
315
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
316
|
+
throw new Error(\`Invalid PORT value: \${process.env.PORT}\`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Prefix: default to empty string
|
|
320
|
+
const prefix = process.env.PREFIX || "${defaultPrefix}";
|
|
321
|
+
|
|
322
|
+
// Logger: default to ${defaultLogger}
|
|
323
|
+
const logger = process.env.LOGGER ? process.env.LOGGER === "true" : ${defaultLogger};
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
wsdlSource,
|
|
327
|
+
host,
|
|
328
|
+
port,
|
|
329
|
+
prefix,
|
|
330
|
+
logger,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
`;
|
|
334
|
+
fs.writeFileSync(path.join(appDir, `config${ext}`), content, "utf-8");
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Generates .env.example file
|
|
338
|
+
*
|
|
339
|
+
* @param {string} appDir - App output directory
|
|
340
|
+
* @param {GenerateAppOptions} opts - App generation options
|
|
341
|
+
* @param {string|undefined} defaultWsdlSource - Default WSDL source from catalog
|
|
342
|
+
*/
|
|
343
|
+
function generateEnvExample(appDir, opts, defaultWsdlSource) {
|
|
344
|
+
const defaultHost = opts.host || "127.0.0.1";
|
|
345
|
+
const defaultPort = opts.port || 3000;
|
|
346
|
+
const defaultPrefix = opts.prefix || "";
|
|
347
|
+
const defaultLogger = opts.logger !== false;
|
|
348
|
+
const content = `# Generated Fastify Application Environment Variables
|
|
349
|
+
#
|
|
350
|
+
# Copy this file to .env and customize as needed.
|
|
351
|
+
# Configuration precedence:
|
|
352
|
+
# 1. Environment variables (runtime overrides)
|
|
353
|
+
# 2. Catalog defaults (generation-time recorded values)
|
|
354
|
+
# 3. Hard defaults (see config file)
|
|
355
|
+
|
|
356
|
+
# WSDL source (required unless provided in catalog)
|
|
357
|
+
${defaultWsdlSource ? `# Default from catalog: ${defaultWsdlSource}` : "# Required: specify the WSDL URL or local file path"}
|
|
358
|
+
${defaultWsdlSource ? `#WSDL_SOURCE=${defaultWsdlSource}` : `WSDL_SOURCE=`}
|
|
359
|
+
|
|
360
|
+
# Server host (default: ${defaultHost})
|
|
361
|
+
HOST=${defaultHost}
|
|
362
|
+
|
|
363
|
+
# Server port (default: ${defaultPort})
|
|
364
|
+
PORT=${defaultPort}
|
|
365
|
+
|
|
366
|
+
# Route prefix (default: empty)
|
|
367
|
+
PREFIX=${defaultPrefix}
|
|
368
|
+
|
|
369
|
+
# Enable Fastify logger (default: ${defaultLogger})
|
|
370
|
+
LOGGER=${defaultLogger}
|
|
371
|
+
|
|
372
|
+
# Optional: SOAP security settings (configure based on your client requirements)
|
|
373
|
+
# SOAP_USERNAME=
|
|
374
|
+
# SOAP_PASSWORD=
|
|
375
|
+
# SOAP_ENDPOINT=
|
|
376
|
+
`;
|
|
377
|
+
fs.writeFileSync(path.join(appDir, ".env.example"), content, "utf-8");
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Generates README.md file
|
|
381
|
+
*
|
|
382
|
+
* @param {string} appDir - App output directory
|
|
383
|
+
* @param {GenerateAppOptions} opts - App generation options
|
|
384
|
+
*/
|
|
385
|
+
function generateReadme(appDir, opts) {
|
|
386
|
+
const imports = opts.imports || "js";
|
|
387
|
+
const ext = getExtension(imports);
|
|
388
|
+
const runCommand = ext === ".ts"
|
|
389
|
+
? "npx tsx server.ts"
|
|
390
|
+
: "node server.js";
|
|
391
|
+
const content = `# Generated Fastify Application
|
|
392
|
+
|
|
393
|
+
This application was auto-generated by \`wsdl-tsc app\`.
|
|
394
|
+
|
|
395
|
+
## Overview
|
|
396
|
+
|
|
397
|
+
This Fastify application provides a REST gateway to a SOAP service, automatically bridging between REST endpoints and SOAP operations.
|
|
398
|
+
|
|
399
|
+
## Structure
|
|
400
|
+
|
|
401
|
+
- \`server${ext}\` - Main application entry point
|
|
402
|
+
- \`config${ext}\` - Configuration loader (environment-based)
|
|
403
|
+
- \`.env.example\` - Example environment configuration
|
|
404
|
+
- \`openapi.json\` - OpenAPI specification${opts.openapiMode === "copy" ? " (copied)" : " (referenced)"}
|
|
405
|
+
|
|
406
|
+
## Prerequisites
|
|
407
|
+
|
|
408
|
+
- Node.js >= 20.0.0
|
|
409
|
+
- Dependencies installed (\`npm install\`)
|
|
410
|
+
|
|
411
|
+
## Quick Start
|
|
412
|
+
|
|
413
|
+
1. **Copy environment template**:
|
|
414
|
+
\`\`\`bash
|
|
415
|
+
cp .env.example .env
|
|
416
|
+
\`\`\`
|
|
417
|
+
|
|
418
|
+
2. **Configure environment**:
|
|
419
|
+
Edit \`.env\` and set required variables (especially \`WSDL_SOURCE\` if not provided via catalog).
|
|
420
|
+
|
|
421
|
+
3. **Run the server**:
|
|
422
|
+
\`\`\`bash
|
|
423
|
+
${runCommand}
|
|
424
|
+
\`\`\`
|
|
425
|
+
|
|
426
|
+
## Endpoints
|
|
427
|
+
|
|
428
|
+
### Health Check
|
|
429
|
+
\`\`\`
|
|
430
|
+
GET /health
|
|
431
|
+
\`\`\`
|
|
432
|
+
Returns: \`{ ok: true }\`
|
|
433
|
+
|
|
434
|
+
### OpenAPI Specification
|
|
435
|
+
\`\`\`
|
|
436
|
+
GET /openapi.json
|
|
437
|
+
\`\`\`
|
|
438
|
+
Returns: OpenAPI 3.1 specification document
|
|
439
|
+
|
|
440
|
+
### Gateway Routes
|
|
441
|
+
All SOAP operations are exposed as REST endpoints. See \`openapi.json\` for complete API documentation.
|
|
442
|
+
|
|
443
|
+
## Configuration
|
|
444
|
+
|
|
445
|
+
Configuration is loaded from environment variables with the following precedence:
|
|
446
|
+
|
|
447
|
+
1. Environment variables (runtime overrides)
|
|
448
|
+
2. Catalog defaults (from generation-time)
|
|
449
|
+
3. Hard defaults (in config file)
|
|
450
|
+
|
|
451
|
+
### Environment Variables
|
|
452
|
+
|
|
453
|
+
| Variable | Default | Description |
|
|
454
|
+
|----------|---------|-------------|
|
|
455
|
+
| \`WSDL_SOURCE\` | (from catalog) | WSDL URL or local file path (required) |
|
|
456
|
+
| \`HOST\` | ${opts.host || "127.0.0.1"} | Server bind address |
|
|
457
|
+
| \`PORT\` | ${opts.port || 3000} | Server listen port |
|
|
458
|
+
| \`PREFIX\` | ${opts.prefix || "(empty)"} | Route prefix |
|
|
459
|
+
| \`LOGGER\` | ${opts.logger !== false} | Enable Fastify logger |
|
|
460
|
+
|
|
461
|
+
## Development
|
|
462
|
+
|
|
463
|
+
### Running with watch mode
|
|
464
|
+
\`\`\`bash
|
|
465
|
+
npx tsx watch server${ext}
|
|
466
|
+
\`\`\`
|
|
467
|
+
|
|
468
|
+
### Testing endpoints
|
|
469
|
+
\`\`\`bash
|
|
470
|
+
# Health check
|
|
471
|
+
curl http://localhost:3000/health
|
|
472
|
+
|
|
473
|
+
# OpenAPI spec
|
|
474
|
+
curl http://localhost:3000/openapi.json
|
|
475
|
+
\`\`\`
|
|
476
|
+
|
|
477
|
+
## Troubleshooting
|
|
478
|
+
|
|
479
|
+
### WSDL_SOURCE missing
|
|
480
|
+
If you see "WSDL_SOURCE environment variable is required", set it in your \`.env\` file or export it:
|
|
481
|
+
\`\`\`bash
|
|
482
|
+
export WSDL_SOURCE=path/to/service.wsdl
|
|
483
|
+
${runCommand}
|
|
484
|
+
\`\`\`
|
|
485
|
+
|
|
486
|
+
### Port already in use
|
|
487
|
+
Change the \`PORT\` in your \`.env\` file or:
|
|
488
|
+
\`\`\`bash
|
|
489
|
+
PORT=8080 ${runCommand}
|
|
490
|
+
\`\`\`
|
|
491
|
+
|
|
492
|
+
## Notes
|
|
493
|
+
|
|
494
|
+
- This app uses the generated client from: \`${opts.clientDir}\`
|
|
495
|
+
- Gateway plugin from: \`${opts.gatewayDir}\`
|
|
496
|
+
- OpenAPI spec from: \`${opts.openapiFile}\`
|
|
497
|
+
|
|
498
|
+
## Generated By
|
|
499
|
+
|
|
500
|
+
- Tool: [@techspokes/typescript-wsdl-client](https://github.com/TechSpokes/typescript-wsdl-client)
|
|
501
|
+
- Command: \`wsdl-tsc app\`
|
|
502
|
+
`;
|
|
503
|
+
fs.writeFileSync(path.join(appDir, "README.md"), content, "utf-8");
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Generates a runnable Fastify application
|
|
507
|
+
*
|
|
508
|
+
* This function orchestrates the complete app generation process:
|
|
509
|
+
* 1. Validates all required inputs exist
|
|
510
|
+
* 2. Reads catalog.json for metadata
|
|
511
|
+
* 3. Creates app directory
|
|
512
|
+
* 4. Generates server.ts with Fastify setup
|
|
513
|
+
* 5. Generates config.ts with environment loading
|
|
514
|
+
* 6. Generates .env.example with configuration template
|
|
515
|
+
* 7. Generates README.md with usage instructions
|
|
516
|
+
* 8. Optionally copies OpenAPI spec into app directory
|
|
517
|
+
*
|
|
518
|
+
* @param {GenerateAppOptions} opts - App generation options
|
|
519
|
+
* @returns {Promise<void>}
|
|
520
|
+
* @throws {Error} If validation fails or required files are missing
|
|
521
|
+
*/
|
|
522
|
+
export async function generateApp(opts) {
|
|
523
|
+
// Resolve all paths to absolute
|
|
524
|
+
const resolvedOpts = {
|
|
525
|
+
...opts,
|
|
526
|
+
clientDir: path.resolve(opts.clientDir),
|
|
527
|
+
gatewayDir: path.resolve(opts.gatewayDir),
|
|
528
|
+
openapiFile: path.resolve(opts.openapiFile),
|
|
529
|
+
catalogFile: path.resolve(opts.catalogFile),
|
|
530
|
+
appDir: path.resolve(opts.appDir),
|
|
531
|
+
};
|
|
532
|
+
// Validate required files and directories
|
|
533
|
+
validateRequiredFiles(resolvedOpts);
|
|
534
|
+
// Read catalog for metadata
|
|
535
|
+
const catalog = readCatalog(resolvedOpts.catalogFile);
|
|
536
|
+
const clientClassName = deriveClientName(catalog);
|
|
537
|
+
const defaultWsdlSource = getCatalogWsdlSource(catalog);
|
|
538
|
+
// Create app directory
|
|
539
|
+
fs.mkdirSync(resolvedOpts.appDir, { recursive: true });
|
|
540
|
+
// Generate app files
|
|
541
|
+
generateServerFile(resolvedOpts.appDir, resolvedOpts, clientClassName);
|
|
542
|
+
generateConfigFile(resolvedOpts.appDir, resolvedOpts, defaultWsdlSource);
|
|
543
|
+
generateEnvExample(resolvedOpts.appDir, resolvedOpts, defaultWsdlSource);
|
|
544
|
+
generateReadme(resolvedOpts.appDir, resolvedOpts);
|
|
545
|
+
// Handle OpenAPI file
|
|
546
|
+
const openapiMode = resolvedOpts.openapiMode || "copy";
|
|
547
|
+
if (openapiMode === "copy") {
|
|
548
|
+
const destPath = path.join(resolvedOpts.appDir, "openapi.json");
|
|
549
|
+
fs.copyFileSync(resolvedOpts.openapiFile, destPath);
|
|
550
|
+
success(`Copied OpenAPI spec to ${destPath}`);
|
|
551
|
+
}
|
|
552
|
+
success(`Generated runnable Fastify app in ${resolvedOpts.appDir}`);
|
|
553
|
+
}
|