@techspokes/typescript-wsdl-client 0.10.2 → 0.11.0
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 +28 -2
- package/dist/app/generateApp.d.ts +11 -5
- package/dist/app/generateApp.d.ts.map +1 -1
- package/dist/app/generateApp.js +262 -157
- package/dist/cli.js +67 -9
- package/dist/client/generateOperations.d.ts +13 -0
- package/dist/client/generateOperations.d.ts.map +1 -0
- package/dist/client/generateOperations.js +71 -0
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +15 -1
- package/dist/gateway/generateGateway.d.ts +1 -0
- package/dist/gateway/generateGateway.d.ts.map +1 -1
- package/dist/gateway/generateGateway.js +4 -2
- package/dist/gateway/generators.d.ts +2 -15
- package/dist/gateway/generators.d.ts.map +1 -1
- package/dist/gateway/generators.js +111 -27
- package/dist/gateway/helpers.d.ts +4 -2
- package/dist/gateway/helpers.d.ts.map +1 -1
- package/dist/gateway/helpers.js +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/loader/wsdlLoader.d.ts.map +1 -1
- package/dist/loader/wsdlLoader.js +30 -4
- package/dist/openapi/generateOpenAPI.d.ts +1 -0
- package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
- package/dist/openapi/generateOpenAPI.js +1 -0
- package/dist/openapi/generateSchemas.d.ts +1 -0
- package/dist/openapi/generateSchemas.d.ts.map +1 -1
- package/dist/openapi/generateSchemas.js +4 -3
- package/dist/pipeline.d.ts +4 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +10 -1
- package/dist/util/builder.d.ts.map +1 -1
- package/dist/util/builder.js +1 -0
- package/dist/util/cli.d.ts +3 -2
- package/dist/util/cli.d.ts.map +1 -1
- package/dist/util/cli.js +14 -4
- package/dist/util/errors.d.ts +37 -0
- package/dist/util/errors.d.ts.map +1 -0
- package/dist/util/errors.js +37 -0
- package/docs/README.md +1 -0
- package/docs/architecture.md +1 -1
- package/docs/cli-reference.md +46 -14
- package/docs/concepts.md +29 -2
- package/docs/gateway-guide.md +36 -2
- package/docs/generated-code.md +56 -0
- package/docs/testing.md +193 -0
- package/package.json +19 -13
package/dist/app/generateApp.js
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
// noinspection HttpUrlsUsage
|
|
2
2
|
/**
|
|
3
|
-
* Fastify App Generator
|
|
3
|
+
* Fastify App Scaffold Generator
|
|
4
4
|
*
|
|
5
5
|
* This module generates a runnable Fastify application that imports and uses
|
|
6
6
|
* the generated gateway plugin and SOAP client. The app serves the OpenAPI spec,
|
|
7
7
|
* health checks, and all gateway routes.
|
|
8
8
|
*
|
|
9
|
+
* The generated scaffold is intended as a one-time starting point. Developers
|
|
10
|
+
* should customize it freely after generation. Use --force-init to overwrite
|
|
11
|
+
* existing scaffold files.
|
|
12
|
+
*
|
|
9
13
|
* Core capabilities:
|
|
10
14
|
* - Generates server.ts with Fastify setup and plugin registration
|
|
11
15
|
* - Generates config.ts with environment-based configuration
|
|
16
|
+
* - Generates package.json with required dependencies
|
|
17
|
+
* - Generates tsconfig.json with NodeNext/ES2022 settings
|
|
12
18
|
* - Generates .env.example with required/optional environment variables
|
|
13
19
|
* - Generates README.md with instructions for running the app
|
|
14
20
|
* - Optionally copies OpenAPI spec into app directory
|
|
15
21
|
* - Validates required inputs (client-dir, gateway-dir, openapi-file, catalog-file)
|
|
22
|
+
* - Skip-if-exists protection for scaffold files (override with force option)
|
|
16
23
|
*/
|
|
17
24
|
import fs from "node:fs";
|
|
18
25
|
import path from "node:path";
|
|
19
26
|
import { deriveClientName } from "../util/tools.js";
|
|
20
|
-
import { success } from "../util/cli.js";
|
|
27
|
+
import { info, success } from "../util/cli.js";
|
|
21
28
|
/**
|
|
22
29
|
* Validates that all required files and directories exist
|
|
23
30
|
*
|
|
@@ -73,16 +80,13 @@ function getExtension(imports) {
|
|
|
73
80
|
return "";
|
|
74
81
|
}
|
|
75
82
|
/**
|
|
76
|
-
* Returns the file extension for
|
|
77
|
-
*
|
|
83
|
+
* Returns the file extension for scaffold app files (server, config).
|
|
84
|
+
* Always .ts — the scaffold is TypeScript for full type safety.
|
|
78
85
|
*
|
|
79
|
-
* @param {string} imports - Import mode (js, ts, or bare)
|
|
80
86
|
* @returns {string} - File extension with leading dot
|
|
81
87
|
*/
|
|
82
|
-
function getAppFileExtension(
|
|
83
|
-
|
|
84
|
-
return ".ts";
|
|
85
|
-
return ".js"; // Use .js for both "js" and "bare" modes
|
|
88
|
+
function getAppFileExtension() {
|
|
89
|
+
return ".ts";
|
|
86
90
|
}
|
|
87
91
|
/**
|
|
88
92
|
* Computes a relative import path from source to target
|
|
@@ -105,6 +109,48 @@ function computeRelativeImport(from, to, imports) {
|
|
|
105
109
|
}
|
|
106
110
|
return prefixed;
|
|
107
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Checks if a string is a URL (http:// or https://)
|
|
114
|
+
*/
|
|
115
|
+
function isUrl(value) {
|
|
116
|
+
return /^https?:\/\//i.test(value);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Normalizes a file path to POSIX separators
|
|
120
|
+
*/
|
|
121
|
+
function toPosix(filePath) {
|
|
122
|
+
return filePath.split(path.sep).join("/");
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Resolves a WSDL source path relative to the app directory.
|
|
126
|
+
* URLs are returned as-is. File paths are computed relative to appDir.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} wsdlSource - Original WSDL source from catalog
|
|
129
|
+
* @param {string} appDir - Resolved app output directory
|
|
130
|
+
* @returns {string} - WSDL source suitable for use from the app directory
|
|
131
|
+
*/
|
|
132
|
+
function resolveWsdlSourceForApp(wsdlSource, appDir) {
|
|
133
|
+
if (isUrl(wsdlSource))
|
|
134
|
+
return wsdlSource;
|
|
135
|
+
return toPosix(path.relative(appDir, path.resolve(wsdlSource)));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Checks whether a scaffold file should be written.
|
|
139
|
+
* Returns true if the file does not exist or force is enabled.
|
|
140
|
+
* Logs an info message and returns false if the file exists and force is disabled.
|
|
141
|
+
*
|
|
142
|
+
* @param {string} filePath - Absolute path to the file
|
|
143
|
+
* @param {boolean} force - Whether to overwrite existing files
|
|
144
|
+
* @returns {boolean} - Whether the file should be written
|
|
145
|
+
*/
|
|
146
|
+
function shouldWriteScaffoldFile(filePath, force) {
|
|
147
|
+
if (!fs.existsSync(filePath))
|
|
148
|
+
return true;
|
|
149
|
+
if (force)
|
|
150
|
+
return true;
|
|
151
|
+
info(`Skipping ${path.basename(filePath)} (already exists, use --force-init to overwrite)`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
108
154
|
/**
|
|
109
155
|
* Reads and parses the catalog file
|
|
110
156
|
*
|
|
@@ -135,10 +181,14 @@ function getCatalogWsdlSource(catalog) {
|
|
|
135
181
|
* @param {string} appDir - App output directory
|
|
136
182
|
* @param {GenerateAppOptions} opts - App generation options
|
|
137
183
|
* @param {string} clientClassName - Derived client class name
|
|
184
|
+
* @param {boolean} force - Whether to overwrite existing files
|
|
138
185
|
*/
|
|
139
|
-
function generateServerFile(appDir, opts, clientClassName) {
|
|
186
|
+
function generateServerFile(appDir, opts, clientClassName, force) {
|
|
140
187
|
const imports = opts.imports || "js";
|
|
141
|
-
const ext = getAppFileExtension(
|
|
188
|
+
const ext = getAppFileExtension();
|
|
189
|
+
const filePath = path.join(appDir, `server${ext}`);
|
|
190
|
+
if (!shouldWriteScaffoldFile(filePath, force))
|
|
191
|
+
return;
|
|
142
192
|
const configImport = computeRelativeImport(appDir, path.join(appDir, "config"), imports);
|
|
143
193
|
const gatewayPluginImport = computeRelativeImport(appDir, path.join(opts.gatewayDir, "plugin"), imports);
|
|
144
194
|
const clientImport = computeRelativeImport(appDir, path.join(opts.clientDir, "client"), imports);
|
|
@@ -149,6 +199,11 @@ function generateServerFile(appDir, opts, clientClassName) {
|
|
|
149
199
|
const openapiSpecPath = path.join(__dirname, "openapi.json");
|
|
150
200
|
const openapiSpec = JSON.parse(fs.readFileSync(openapiSpecPath, "utf-8"));
|
|
151
201
|
|
|
202
|
+
// Override OpenAPI server URL at runtime if configured
|
|
203
|
+
if (config.openapiServerUrl) {
|
|
204
|
+
openapiSpec.servers = [{ url: config.openapiServerUrl }];
|
|
205
|
+
}
|
|
206
|
+
|
|
152
207
|
// Serve OpenAPI specification
|
|
153
208
|
fastify.get("/openapi.json", async () => {
|
|
154
209
|
return openapiSpec;
|
|
@@ -158,11 +213,16 @@ function generateServerFile(appDir, opts, clientClassName) {
|
|
|
158
213
|
const openapiSpecPath = path.resolve(__dirname, "${computeRelativeImport(appDir, opts.openapiFile, "bare")}");
|
|
159
214
|
const openapiSpec = JSON.parse(fs.readFileSync(openapiSpecPath, "utf-8"));
|
|
160
215
|
|
|
216
|
+
// Override OpenAPI server URL at runtime if configured
|
|
217
|
+
if (config.openapiServerUrl) {
|
|
218
|
+
openapiSpec.servers = [{ url: config.openapiServerUrl }];
|
|
219
|
+
}
|
|
220
|
+
|
|
161
221
|
fastify.get("/openapi.json", async () => {
|
|
162
222
|
return openapiSpec;
|
|
163
223
|
});`;
|
|
164
224
|
const content = `/**
|
|
165
|
-
*
|
|
225
|
+
* Fastify Application
|
|
166
226
|
*
|
|
167
227
|
* This file bootstraps a Fastify server that:
|
|
168
228
|
* - Loads configuration from environment variables
|
|
@@ -171,7 +231,7 @@ function generateServerFile(appDir, opts, clientClassName) {
|
|
|
171
231
|
* - Serves the OpenAPI specification
|
|
172
232
|
* - Provides health check endpoint
|
|
173
233
|
*
|
|
174
|
-
*
|
|
234
|
+
* Scaffolded by wsdl-tsc. Customize freely.
|
|
175
235
|
*/
|
|
176
236
|
import fs from "node:fs";
|
|
177
237
|
import path from "node:path";
|
|
@@ -245,7 +305,7 @@ main().catch((err) => {
|
|
|
245
305
|
process.exit(1);
|
|
246
306
|
});
|
|
247
307
|
`;
|
|
248
|
-
fs.writeFileSync(
|
|
308
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
249
309
|
}
|
|
250
310
|
/**
|
|
251
311
|
* Generates config.ts file
|
|
@@ -253,18 +313,35 @@ main().catch((err) => {
|
|
|
253
313
|
* @param {string} appDir - App output directory
|
|
254
314
|
* @param {GenerateAppOptions} opts - App generation options
|
|
255
315
|
* @param {string|undefined} defaultWsdlSource - Default WSDL source from catalog
|
|
316
|
+
* @param {boolean} force - Whether to overwrite existing files
|
|
256
317
|
*/
|
|
257
|
-
function generateConfigFile(appDir, opts, defaultWsdlSource) {
|
|
258
|
-
const
|
|
259
|
-
const
|
|
318
|
+
function generateConfigFile(appDir, opts, defaultWsdlSource, force) {
|
|
319
|
+
const ext = getAppFileExtension();
|
|
320
|
+
const filePath = path.join(appDir, `config${ext}`);
|
|
321
|
+
if (!shouldWriteScaffoldFile(filePath, force))
|
|
322
|
+
return;
|
|
260
323
|
const defaultHost = opts.host || "127.0.0.1";
|
|
261
324
|
const defaultPort = opts.port || 3000;
|
|
262
325
|
const defaultPrefix = opts.prefix || "";
|
|
263
326
|
const defaultLogger = opts.logger !== false;
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
327
|
+
// Resolve WSDL source relative to app directory
|
|
328
|
+
const resolvedWsdlSource = defaultWsdlSource
|
|
329
|
+
? resolveWsdlSourceForApp(defaultWsdlSource, appDir)
|
|
330
|
+
: undefined;
|
|
331
|
+
// For URL sources, use as fallback default. For file sources, require explicit WSDL_SOURCE.
|
|
332
|
+
const wsdlIsUrl = resolvedWsdlSource && isUrl(resolvedWsdlSource);
|
|
333
|
+
const wsdlFallback = wsdlIsUrl ? ` || "${resolvedWsdlSource}"` : "";
|
|
334
|
+
const content = `/**
|
|
335
|
+
* Application Configuration
|
|
336
|
+
*
|
|
337
|
+
* Loads configuration from environment variables with sensible defaults.
|
|
338
|
+
* Configuration precedence:
|
|
339
|
+
* 1. Environment variables (runtime overrides)
|
|
340
|
+
* 2. Hard defaults (defined in this file)
|
|
341
|
+
*
|
|
342
|
+
* Scaffolded by wsdl-tsc. Customize freely.
|
|
343
|
+
*/
|
|
344
|
+
|
|
268
345
|
/**
|
|
269
346
|
* Application configuration interface
|
|
270
347
|
*/
|
|
@@ -274,6 +351,7 @@ export interface AppConfig {
|
|
|
274
351
|
port: number;
|
|
275
352
|
prefix: string;
|
|
276
353
|
logger: boolean;
|
|
354
|
+
openapiServerUrl: string;
|
|
277
355
|
}
|
|
278
356
|
|
|
279
357
|
/**
|
|
@@ -281,30 +359,12 @@ export interface AppConfig {
|
|
|
281
359
|
*
|
|
282
360
|
* @returns {AppConfig} - Application configuration
|
|
283
361
|
* @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
362
|
*/
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const wsdlSource = process.env.WSDL_SOURCE${defaultWsdlSource ? ` || "${defaultWsdlSource}"` : ""};
|
|
363
|
+
export function loadConfig(): AppConfig {
|
|
364
|
+
// WSDL source: required from env${wsdlIsUrl ? " or URL default" : ""}
|
|
365
|
+
const wsdlSource = process.env.WSDL_SOURCE${wsdlFallback};
|
|
306
366
|
if (!wsdlSource) {
|
|
307
|
-
throw new Error("WSDL_SOURCE environment variable is required");
|
|
367
|
+
throw new Error("WSDL_SOURCE environment variable is required${resolvedWsdlSource && !wsdlIsUrl ? ` (hint: ${resolvedWsdlSource})` : ""}");
|
|
308
368
|
}
|
|
309
369
|
|
|
310
370
|
// Host: default to ${defaultHost}
|
|
@@ -322,16 +382,20 @@ export function loadConfig() {
|
|
|
322
382
|
// Logger: default to ${defaultLogger}
|
|
323
383
|
const logger = process.env.LOGGER ? process.env.LOGGER === "true" : ${defaultLogger};
|
|
324
384
|
|
|
385
|
+
// OpenAPI server URL override (replaces servers in OpenAPI spec at runtime)
|
|
386
|
+
const openapiServerUrl = process.env.OPENAPI_SERVER_URL || "";
|
|
387
|
+
|
|
325
388
|
return {
|
|
326
389
|
wsdlSource,
|
|
327
390
|
host,
|
|
328
391
|
port,
|
|
329
392
|
prefix,
|
|
330
393
|
logger,
|
|
394
|
+
openapiServerUrl,
|
|
331
395
|
};
|
|
332
396
|
}
|
|
333
397
|
`;
|
|
334
|
-
fs.writeFileSync(
|
|
398
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
335
399
|
}
|
|
336
400
|
/**
|
|
337
401
|
* Generates .env.example file
|
|
@@ -339,23 +403,42 @@ export function loadConfig() {
|
|
|
339
403
|
* @param {string} appDir - App output directory
|
|
340
404
|
* @param {GenerateAppOptions} opts - App generation options
|
|
341
405
|
* @param {string|undefined} defaultWsdlSource - Default WSDL source from catalog
|
|
406
|
+
* @param {boolean} force - Whether to overwrite existing files
|
|
342
407
|
*/
|
|
343
|
-
function generateEnvExample(appDir, opts, defaultWsdlSource) {
|
|
408
|
+
function generateEnvExample(appDir, opts, defaultWsdlSource, force) {
|
|
409
|
+
const filePath = path.join(appDir, ".env.example");
|
|
410
|
+
if (!shouldWriteScaffoldFile(filePath, force))
|
|
411
|
+
return;
|
|
344
412
|
const defaultHost = opts.host || "127.0.0.1";
|
|
345
413
|
const defaultPort = opts.port || 3000;
|
|
346
414
|
const defaultPrefix = opts.prefix || "";
|
|
347
415
|
const defaultLogger = opts.logger !== false;
|
|
348
|
-
|
|
416
|
+
// Resolve WSDL source relative to app directory
|
|
417
|
+
const resolvedWsdlSource = defaultWsdlSource
|
|
418
|
+
? resolveWsdlSourceForApp(defaultWsdlSource, appDir)
|
|
419
|
+
: undefined;
|
|
420
|
+
const wsdlIsUrl = resolvedWsdlSource && isUrl(resolvedWsdlSource);
|
|
421
|
+
let wsdlSection;
|
|
422
|
+
if (wsdlIsUrl) {
|
|
423
|
+
// URL source: show as commented default (it's the fallback in config.ts)
|
|
424
|
+
wsdlSection = `# WSDL source URL (default: ${resolvedWsdlSource})
|
|
425
|
+
#WSDL_SOURCE=${resolvedWsdlSource}`;
|
|
426
|
+
}
|
|
427
|
+
else if (resolvedWsdlSource) {
|
|
428
|
+
// File source: require explicit setting, show hint
|
|
429
|
+
wsdlSection = `# WSDL source (required — set to URL or file path)
|
|
430
|
+
# Generation-time path: ${resolvedWsdlSource}
|
|
431
|
+
WSDL_SOURCE=`;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
wsdlSection = `# WSDL source (required — set to URL or file path)
|
|
435
|
+
WSDL_SOURCE=`;
|
|
436
|
+
}
|
|
437
|
+
const content = `# Fastify Application Environment Variables
|
|
349
438
|
#
|
|
350
439
|
# 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
440
|
|
|
356
|
-
|
|
357
|
-
${defaultWsdlSource ? `# Default from catalog: ${defaultWsdlSource}` : "# Required: specify the WSDL URL or local file path"}
|
|
358
|
-
${defaultWsdlSource ? `#WSDL_SOURCE=${defaultWsdlSource}` : `WSDL_SOURCE=`}
|
|
441
|
+
${wsdlSection}
|
|
359
442
|
|
|
360
443
|
# Server host (default: ${defaultHost})
|
|
361
444
|
HOST=${defaultHost}
|
|
@@ -369,151 +452,162 @@ PREFIX=${defaultPrefix}
|
|
|
369
452
|
# Enable Fastify logger (default: ${defaultLogger})
|
|
370
453
|
LOGGER=${defaultLogger}
|
|
371
454
|
|
|
455
|
+
# Override OpenAPI spec server URL at runtime (default: use generation-time value)
|
|
456
|
+
#OPENAPI_SERVER_URL=http://localhost:${defaultPort}
|
|
457
|
+
|
|
372
458
|
# Optional: SOAP security settings (configure based on your client requirements)
|
|
373
459
|
# SOAP_USERNAME=
|
|
374
460
|
# SOAP_PASSWORD=
|
|
375
461
|
# SOAP_ENDPOINT=
|
|
376
462
|
`;
|
|
377
|
-
fs.writeFileSync(
|
|
463
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Generates package.json file (skipped if already exists)
|
|
467
|
+
*
|
|
468
|
+
* @param {string} appDir - App output directory
|
|
469
|
+
* @param {boolean} force - Whether to overwrite existing files
|
|
470
|
+
*/
|
|
471
|
+
function generatePackageJson(appDir, force) {
|
|
472
|
+
const filePath = path.join(appDir, "package.json");
|
|
473
|
+
if (!shouldWriteScaffoldFile(filePath, force))
|
|
474
|
+
return;
|
|
475
|
+
const pkg = {
|
|
476
|
+
name: "wsdl-gateway-app",
|
|
477
|
+
version: "0.0.1",
|
|
478
|
+
private: true,
|
|
479
|
+
type: "module",
|
|
480
|
+
scripts: {
|
|
481
|
+
start: "tsx server.ts",
|
|
482
|
+
dev: "tsx watch server.ts",
|
|
483
|
+
},
|
|
484
|
+
dependencies: {
|
|
485
|
+
fastify: "^5.7.4",
|
|
486
|
+
"fastify-plugin": "^5.1.0",
|
|
487
|
+
soap: "^1.6.5",
|
|
488
|
+
},
|
|
489
|
+
devDependencies: {
|
|
490
|
+
tsx: "^4.21.0",
|
|
491
|
+
typescript: "^5.9.3",
|
|
492
|
+
},
|
|
493
|
+
};
|
|
494
|
+
fs.writeFileSync(filePath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Generates tsconfig.json file (skipped if already exists)
|
|
498
|
+
*
|
|
499
|
+
* @param {string} appDir - App output directory
|
|
500
|
+
* @param {GenerateAppOptions} opts - App generation options
|
|
501
|
+
* @param {boolean} force - Whether to overwrite existing files
|
|
502
|
+
*/
|
|
503
|
+
function generateTsConfig(appDir, opts, force) {
|
|
504
|
+
const filePath = path.join(appDir, "tsconfig.json");
|
|
505
|
+
if (!shouldWriteScaffoldFile(filePath, force))
|
|
506
|
+
return;
|
|
507
|
+
// Compute include paths relative to app directory
|
|
508
|
+
const clientInclude = toPosix(path.relative(appDir, opts.clientDir)) + "/**/*.ts";
|
|
509
|
+
const gatewayInclude = toPosix(path.relative(appDir, opts.gatewayDir)) + "/**/*.ts";
|
|
510
|
+
const tsconfig = {
|
|
511
|
+
compilerOptions: {
|
|
512
|
+
module: "NodeNext",
|
|
513
|
+
moduleResolution: "NodeNext",
|
|
514
|
+
target: "ES2022",
|
|
515
|
+
strict: true,
|
|
516
|
+
esModuleInterop: true,
|
|
517
|
+
skipLibCheck: true,
|
|
518
|
+
outDir: "dist",
|
|
519
|
+
rootDir: ".",
|
|
520
|
+
},
|
|
521
|
+
include: [
|
|
522
|
+
"*.ts",
|
|
523
|
+
clientInclude,
|
|
524
|
+
gatewayInclude,
|
|
525
|
+
],
|
|
526
|
+
};
|
|
527
|
+
fs.writeFileSync(filePath, JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
|
|
378
528
|
}
|
|
379
529
|
/**
|
|
380
530
|
* Generates README.md file
|
|
381
531
|
*
|
|
382
532
|
* @param {string} appDir - App output directory
|
|
383
533
|
* @param {GenerateAppOptions} opts - App generation options
|
|
534
|
+
* @param {boolean} force - Whether to overwrite existing files
|
|
384
535
|
*/
|
|
385
|
-
function generateReadme(appDir, opts) {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
? "npx tsx server.ts"
|
|
390
|
-
: "node server.js";
|
|
536
|
+
function generateReadme(appDir, opts, force) {
|
|
537
|
+
const filePath = path.join(appDir, "README.md");
|
|
538
|
+
if (!shouldWriteScaffoldFile(filePath, force))
|
|
539
|
+
return;
|
|
391
540
|
const content = `# Generated Fastify Application
|
|
392
541
|
|
|
393
|
-
This application was
|
|
542
|
+
This application was scaffolded by \`wsdl-tsc\`. Customize freely.
|
|
394
543
|
|
|
395
|
-
##
|
|
544
|
+
## Quick Start
|
|
396
545
|
|
|
397
|
-
|
|
546
|
+
\`\`\`bash
|
|
547
|
+
npm install
|
|
548
|
+
cp .env.example .env
|
|
549
|
+
# Edit .env — set WSDL_SOURCE to your WSDL URL or file path
|
|
550
|
+
npm start
|
|
551
|
+
\`\`\`
|
|
398
552
|
|
|
399
553
|
## Structure
|
|
400
554
|
|
|
401
|
-
- \`server
|
|
402
|
-
- \`config
|
|
555
|
+
- \`server.ts\` - Main application entry point
|
|
556
|
+
- \`config.ts\` - Configuration loader (environment-based)
|
|
557
|
+
- \`package.json\` - Dependencies and scripts
|
|
558
|
+
- \`tsconfig.json\` - TypeScript configuration
|
|
403
559
|
- \`.env.example\` - Example environment configuration
|
|
404
560
|
- \`openapi.json\` - OpenAPI specification${opts.openapiMode === "copy" ? " (copied)" : " (referenced)"}
|
|
405
561
|
|
|
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
562
|
## Endpoints
|
|
427
563
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
GET
|
|
431
|
-
|
|
432
|
-
|
|
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.
|
|
564
|
+
| Endpoint | Method | Description |
|
|
565
|
+
|----------|--------|-------------|
|
|
566
|
+
| \`/health\` | GET | Health check — returns \`{ "ok": true }\` |
|
|
567
|
+
| \`/openapi.json\` | GET | OpenAPI 3.1 specification |
|
|
568
|
+
| All SOAP operations | POST | REST-to-SOAP gateway routes (see openapi.json) |
|
|
442
569
|
|
|
443
570
|
## Configuration
|
|
444
571
|
|
|
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
572
|
| Variable | Default | Description |
|
|
454
573
|
|----------|---------|-------------|
|
|
455
|
-
| \`WSDL_SOURCE\` | (
|
|
574
|
+
| \`WSDL_SOURCE\` | (see .env.example) | WSDL URL or local file path (required) |
|
|
456
575
|
| \`HOST\` | ${opts.host || "127.0.0.1"} | Server bind address |
|
|
457
576
|
| \`PORT\` | ${opts.port || 3000} | Server listen port |
|
|
458
|
-
| \`PREFIX\` |
|
|
577
|
+
| \`PREFIX\` | (empty) | Route prefix |
|
|
459
578
|
| \`LOGGER\` | ${opts.logger !== false} | Enable Fastify logger |
|
|
579
|
+
| \`OPENAPI_SERVER_URL\` | (empty) | Override OpenAPI spec server URL at runtime |
|
|
460
580
|
|
|
461
581
|
## Development
|
|
462
582
|
|
|
463
|
-
### Running with watch mode
|
|
464
|
-
\`\`\`bash
|
|
465
|
-
npx tsx watch server${ext}
|
|
466
|
-
\`\`\`
|
|
467
|
-
|
|
468
|
-
### Testing endpoints
|
|
469
583
|
\`\`\`bash
|
|
470
|
-
#
|
|
471
|
-
curl
|
|
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}
|
|
584
|
+
npm run dev # Start with file watching
|
|
585
|
+
curl localhost:3000/health
|
|
586
|
+
curl localhost:3000/openapi.json
|
|
490
587
|
\`\`\`
|
|
491
588
|
|
|
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
589
|
## Generated By
|
|
499
590
|
|
|
500
|
-
|
|
501
|
-
- Command: \`wsdl-tsc app\`
|
|
591
|
+
[@techspokes/typescript-wsdl-client](https://github.com/TechSpokes/typescript-wsdl-client)
|
|
502
592
|
`;
|
|
503
|
-
fs.writeFileSync(
|
|
593
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
504
594
|
}
|
|
505
595
|
/**
|
|
506
|
-
* Generates a runnable Fastify application
|
|
596
|
+
* Generates a runnable Fastify application scaffold
|
|
507
597
|
*
|
|
508
|
-
* This function orchestrates the complete app
|
|
598
|
+
* This function orchestrates the complete app scaffold process:
|
|
509
599
|
* 1. Validates all required inputs exist
|
|
510
600
|
* 2. Reads catalog.json for metadata
|
|
511
601
|
* 3. Creates app directory
|
|
512
602
|
* 4. Generates server.ts with Fastify setup
|
|
513
603
|
* 5. Generates config.ts with environment loading
|
|
514
|
-
* 6. Generates .
|
|
515
|
-
* 7. Generates
|
|
516
|
-
* 8.
|
|
604
|
+
* 6. Generates package.json with dependencies
|
|
605
|
+
* 7. Generates tsconfig.json with TypeScript settings
|
|
606
|
+
* 8. Generates .env.example with configuration template
|
|
607
|
+
* 9. Generates README.md with usage instructions
|
|
608
|
+
* 10. Optionally copies OpenAPI spec into app directory
|
|
609
|
+
*
|
|
610
|
+
* Files that already exist are skipped unless force is true.
|
|
517
611
|
*
|
|
518
612
|
* @param {GenerateAppOptions} opts - App generation options
|
|
519
613
|
* @returns {Promise<void>}
|
|
@@ -529,6 +623,7 @@ export async function generateApp(opts) {
|
|
|
529
623
|
catalogFile: path.resolve(opts.catalogFile),
|
|
530
624
|
appDir: path.resolve(opts.appDir),
|
|
531
625
|
};
|
|
626
|
+
const force = resolvedOpts.force ?? false;
|
|
532
627
|
// Validate required files and directories
|
|
533
628
|
validateRequiredFiles(resolvedOpts);
|
|
534
629
|
// Read catalog for metadata
|
|
@@ -537,17 +632,27 @@ export async function generateApp(opts) {
|
|
|
537
632
|
const defaultWsdlSource = getCatalogWsdlSource(catalog);
|
|
538
633
|
// Create app directory
|
|
539
634
|
fs.mkdirSync(resolvedOpts.appDir, { recursive: true });
|
|
540
|
-
// Generate
|
|
541
|
-
generateServerFile(resolvedOpts.appDir, resolvedOpts, clientClassName);
|
|
542
|
-
generateConfigFile(resolvedOpts.appDir, resolvedOpts, defaultWsdlSource);
|
|
543
|
-
|
|
544
|
-
|
|
635
|
+
// Generate scaffold files (each checks for existing files unless force is set)
|
|
636
|
+
generateServerFile(resolvedOpts.appDir, resolvedOpts, clientClassName, force);
|
|
637
|
+
generateConfigFile(resolvedOpts.appDir, resolvedOpts, defaultWsdlSource, force);
|
|
638
|
+
generatePackageJson(resolvedOpts.appDir, force);
|
|
639
|
+
generateTsConfig(resolvedOpts.appDir, resolvedOpts, force);
|
|
640
|
+
generateEnvExample(resolvedOpts.appDir, resolvedOpts, defaultWsdlSource, force);
|
|
641
|
+
generateReadme(resolvedOpts.appDir, resolvedOpts, force);
|
|
545
642
|
// Handle OpenAPI file
|
|
546
643
|
const openapiMode = resolvedOpts.openapiMode || "copy";
|
|
547
644
|
if (openapiMode === "copy") {
|
|
548
645
|
const destPath = path.join(resolvedOpts.appDir, "openapi.json");
|
|
549
|
-
|
|
550
|
-
|
|
646
|
+
if (shouldWriteScaffoldFile(destPath, force)) {
|
|
647
|
+
const spec = JSON.parse(fs.readFileSync(resolvedOpts.openapiFile, "utf-8"));
|
|
648
|
+
const host = resolvedOpts.host || "127.0.0.1";
|
|
649
|
+
const port = resolvedOpts.port || 3000;
|
|
650
|
+
const prefix = resolvedOpts.prefix || "";
|
|
651
|
+
const urlHost = ["0.0.0.0", "127.0.0.1", "::", "::1"].includes(host) ? "localhost" : host;
|
|
652
|
+
spec.servers = [{ url: `http://${urlHost}:${port}${prefix}` }];
|
|
653
|
+
fs.writeFileSync(destPath, JSON.stringify(spec, null, 2) + "\n", "utf-8");
|
|
654
|
+
success(`Copied OpenAPI spec to ${destPath}`);
|
|
655
|
+
}
|
|
551
656
|
}
|
|
552
|
-
success(`
|
|
657
|
+
success(`Scaffolded Fastify app in ${resolvedOpts.appDir}`);
|
|
553
658
|
}
|