@techspokes/typescript-wsdl-client 0.8.19 → 0.9.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 CHANGED
@@ -9,7 +9,7 @@
9
9
  [![TechSpokes Org](https://img.shields.io/badge/org-techspokes-181717?logo=github)](https://github.com/techspokes)
10
10
  [![Sponsor](https://img.shields.io/badge/sponsor-GitHub-blue?logo=github-sponsors)](https://github.com/sponsors/TechSpokes)
11
11
 
12
- > **Mission**: Transform complex WSDL/XSD definitions into ergonomic, type-safe TypeScript SOAP clients with optional OpenAPI 3.1 specs and Fastify REST gateway scaffolding — enabling confident integration with legacy enterprise services.
12
+ > **Mission**: Transform complex WSDL/XSD definitions into ergonomic, type-safe TypeScript SOAP clients with optional OpenAPI 3.1 specs and production-ready Fastify REST gateways — enabling confident integration with legacy enterprise services.
13
13
 
14
14
  ---
15
15
 
@@ -51,7 +51,7 @@ Most WSDL generators produce loosely typed stubs or expose raw XML complexity to
51
51
  | **Catalog Introspection** | One JSON artifact (`catalog.json`) to drive further tooling (including OpenAPI & gateway). |
52
52
  | **OpenAPI 3.1 Bridge** | Mirrors the exact TypeScript model with no divergence between runtime and spec. |
53
53
  | **Standard Response Envelope** | Always-on, debuggable envelope structure (status, message, data, error) for REST gateways. |
54
- | **Fastify Gateway Scaffolding** | Route and schema generation with JSON Schema validation (handler implementation in progress). |
54
+ | **Fastify Gateway Generation** | Production-ready route handlers with SOAP client integration and standardized envelope responses. |
55
55
  | **Multi-format Output** | `--openapi-format json\|yaml\|both` with always-on validation (unless disabled). |
56
56
  | **One-Shot Pipeline** | Single pass (parse to TS to OpenAPI to Gateway) for CI & automation. |
57
57
 
@@ -106,13 +106,13 @@ npx wsdl-tsc pipeline \
106
106
 
107
107
  The tool provides **five commands** for different integration scenarios:
108
108
 
109
- | Command | Purpose | Typical Use Case |
110
- |--------------|----------------------------------------------------------------|-----------------------------------------------------|
111
- | `compile` | Parse WSDL and emit `catalog.json` only | Debugging, inspection, or multi-stage builds |
112
- | `client` | Generate TypeScript SOAP client from WSDL or catalog | Standard SOAP integration (most common) |
113
- | `openapi` | Generate OpenAPI 3.1 spec from WSDL or catalog | Documentation, REST proxies, API gateways |
114
- | `gateway` | Generate Fastify gateway scaffolding from OpenAPI spec | REST gateway foundation (handler stubs) |
115
- | `pipeline` | Run full pipeline: client + OpenAPI + gateway in one pass | CI/CD automation, complete stack generation |
109
+ | Command | Purpose | Typical Use Case |
110
+ |------------|---------------------------------------------------------------|-----------------------------------------------|
111
+ | `compile` | Parse WSDL and emit `catalog.json` only | Debugging, inspection, or multi-stage builds |
112
+ | `client` | Generate TypeScript SOAP client from WSDL or catalog | Standard SOAP integration (most common) |
113
+ | `openapi` | Generate OpenAPI 3.1 spec from WSDL or catalog | Documentation, REST proxies, API gateways |
114
+ | `gateway` | Generate Fastify gateway with full handlers from OpenAPI spec | Production REST gateway with SOAP integration |
115
+ | `pipeline` | Run full pipeline: client + OpenAPI + gateway in one pass | CI/CD automation, complete stack generation |
116
116
 
117
117
  ---
118
118
 
@@ -579,23 +579,21 @@ This ensures diff-friendly output for version control.
579
579
 
580
580
  ## 8. Command: `gateway`
581
581
 
582
- **Purpose**: Generate Fastify gateway scaffolding (routes and schemas) from an OpenAPI 3.1 specification. This provides the foundation for building a REST API layer over your SOAP client.
583
-
584
- > **Current Status (v0.8.0)**: The gateway generator produces basic scaffolding including route registration, JSON Schema validation setup, and handler stubs. Full code generation with complete handler implementations is planned for future releases. You will need to implement the business logic that calls your SOAP client and transforms responses.
582
+ **Purpose**: Generate a production-ready Fastify gateway with fully functional route handlers from an OpenAPI 3.1 specification. This creates a complete REST API layer over your SOAP client with automatic request/response transformation and standardized envelope responses.
585
583
 
586
584
  **When to use**:
587
585
  - Building a REST API gateway for legacy SOAP services
588
586
  - Creating a modern HTTP/JSON interface for SOAP operations
589
587
  - Setting up request/response validation with JSON Schema
590
- - Establishing Fastify routing structure for SOAP operations
588
+ - Establishing Fastify routing structure with full handler implementations
591
589
 
592
590
  **What it generates**:
593
- - Fastify route registration files
591
+ - Fastify route registration files with complete handler implementations
594
592
  - JSON Schema models with URN-based IDs
595
593
  - Operation schemas (request/response validation)
596
594
  - Schema and route registration modules
597
- - Handler stubs (require manual implementation)
598
- - Full handler implementations (coming in future versions)
595
+ - Runtime utilities (envelope builders, error handlers)
596
+ - Fastify plugin wrapper for simplified integration
599
597
 
600
598
  ### Usage
601
599
 
@@ -621,10 +619,18 @@ npx wsdl-tsc gateway \
621
619
 
622
620
  ### Optional Flags
623
621
 
624
- | Flag | Default | Description |
625
- |----------------------------------|---------------------------------------------------|------------------------------------------|
626
- | `--import-extensions` | `js` | Import style: `js`, `ts`, or `bare` |
627
- | `--gateway-default-status-codes` | `200,400,401,403,404,409,422,429,500,502,503,504` | Comma-separated status codes to backfill |
622
+ | Flag | Default | Description |
623
+ |----------------------------------|---------------------------------------------------|-------------------------------------------------------------|
624
+ | `--import-extensions` | `js` | Import style: `js`, `ts`, or `bare` |
625
+ | `--gateway-default-status-codes` | `200,400,401,403,404,409,422,429,500,502,503,504` | Comma-separated status codes to backfill |
626
+ | `--catalog-file` | *(none)* | Path to `catalog.json` for operation metadata |
627
+ | `--gateway-client-class-name` | *(auto-detected)* | Override SOAP client class name |
628
+ | `--gateway-decorator-name` | `{serviceSlug}Client` | Fastify decorator name for client instance |
629
+ | `--gateway-stub-handlers` | `false` | Generate stub handlers instead of full implementations |
630
+ | `--gateway-skip-plugin` | `false` | Skip generating `plugin.ts` wrapper |
631
+ | `--gateway-skip-runtime` | `false` | Skip generating `runtime.ts` utilities |
632
+
633
+ > **Note**: Route URLs are derived from the OpenAPI document paths, which already include any base path configured during OpenAPI generation (via `--openapi-base-path`). There is no separate route prefix option for the gateway.
628
634
 
629
635
  ### Generated Output Structure
630
636
 
@@ -639,12 +645,14 @@ npx wsdl-tsc gateway \
639
645
  │ ├── <operation1>.json
640
646
  │ ├── <operation2>.json
641
647
  │ └── ...
642
- ├── routes/ # Individual route registration files
648
+ ├── routes/ # Route registration files with full handlers
643
649
  │ ├── <route1>.ts
644
650
  │ ├── <route2>.ts
645
651
  │ └── ...
646
652
  ├── schemas.ts # Schema registration module
647
- └── routes.ts # Route aggregator module
653
+ ├── routes.ts # Route aggregator module
654
+ ├── runtime.ts # Envelope builders and error handler
655
+ └── plugin.ts # Fastify plugin wrapper (recommended entry point)
648
656
  ```
649
657
 
650
658
  ### URN-Based Schema IDs
@@ -703,56 +711,156 @@ npx wsdl-tsc gateway \
703
711
 
704
712
  ### Integration Pattern
705
713
 
706
- The generated gateway scaffolding provides the foundation for your Fastify application:
714
+ The generated gateway provides a Fastify plugin for simplified integration.
715
+
716
+ #### Prerequisites
717
+
718
+ Your host application needs these dependencies:
719
+
720
+ ```bash
721
+ npm install fastify fastify-plugin
722
+ ```
723
+
724
+ #### Using the Generated Plugin (Recommended)
707
725
 
708
726
  ```typescript
709
727
  import Fastify from 'fastify';
710
- import { registerSchemas } from './gateway/schemas.js';
711
- import { registerRoutes } from './gateway/routes.js';
728
+ import weatherGateway from './gateway/plugin.js';
729
+ import { Weather } from './client/client.js';
712
730
 
713
731
  const app = Fastify({ logger: true });
714
732
 
715
- // Register JSON Schemas (validation setup)
716
- await registerSchemas(app);
733
+ // Create and configure SOAP client
734
+ const weatherClient = new Weather({
735
+ source: 'https://example.com/weather.wsdl',
736
+ // security: new soap.WSSecurity('user', 'pass'), // if needed
737
+ });
717
738
 
718
- // Register routes (with handler stubs)
719
- await registerRoutes(app, {
720
- prefix: '/api/v1',
721
- // Pass any route-level options
739
+ // Register gateway plugin with client
740
+ await app.register(weatherGateway, {
741
+ client: weatherClient,
742
+ // Note: Route paths are determined by --openapi-base-path during generation.
743
+ // The prefix option here adds an ADDITIONAL runtime prefix on top of generated paths.
744
+ // Only use if you need to mount routes under a different sub-path at runtime.
722
745
  });
723
746
 
724
747
  await app.listen({ port: 3000 });
725
748
  ```
726
749
 
727
- > **Note**: The registered routes contain handler stubs that throw "Not implemented" errors. You must implement the handler logic in each route file before the gateway becomes functional.
750
+ The plugin automatically:
751
+ - Decorates Fastify with the SOAP client (`fastify.weatherClient`)
752
+ - Registers all JSON schemas for validation
753
+ - Installs a centralized error handler
754
+ - Registers all routes with full handler implementations
755
+
756
+ #### Using Individual Components (Advanced)
728
757
 
729
- ### Handler Implementation
758
+ For more control, you can use the individual modules:
730
759
 
731
- > ** Manual Implementation Required**: Currently, the gateway generator produces handler stubs that you must implement. Future versions will include full code generation for handler logic.
760
+ ```typescript
761
+ import Fastify from 'fastify';
762
+ import { registerSchemas_v1_weather } from './gateway/schemas.js';
763
+ import { registerRoutes_v1_weather } from './gateway/routes.js';
764
+ import { createGatewayErrorHandler_v1_weather } from './gateway/runtime.js';
765
+ import { Weather } from './client/client.js';
732
766
 
733
- Each generated route file contains handler stubs that you need to implement:
767
+ const app = Fastify({ logger: true });
768
+
769
+ // Manual setup
770
+ const weatherClient = new Weather({ source: 'weather.wsdl' });
771
+ app.decorate('weatherClient', weatherClient);
772
+
773
+ // Register schemas
774
+ await registerSchemas_v1_weather(app);
775
+
776
+ // Install error handler
777
+ app.setErrorHandler(createGatewayErrorHandler_v1_weather());
778
+
779
+ // Register routes (paths already include --openapi-base-path if configured)
780
+ await registerRoutes_v1_weather(app);
781
+
782
+ await app.listen({ port: 3000 });
783
+ ```
784
+
785
+ ### Generated Handler Implementation
786
+
787
+ Route handlers are fully implemented and call the SOAP client automatically:
734
788
 
735
789
  ```typescript
736
- // Example: routes/get-weather.ts
737
- export async function handler(request, reply) {
738
- // TODO: Implement handler logic
739
- // 1. Extract validated input from request.body
740
- // 2. Call SOAP client
741
- // 3. Transform response
742
- // 4. Return standard envelope
743
-
744
- throw new Error('Not implemented');
790
+ // Generated: routes/get-city-forecast-by-zip.ts
791
+ import type { FastifyInstance } from "fastify";
792
+ import schema from "../schemas/operations/getcityforecastbyzip.json" with { type: "json" };
793
+ import { buildSuccessEnvelope } from "../runtime.js";
794
+
795
+ export async function registerRoute_v1_weather_getcityforecastbyzip(fastify: FastifyInstance) {
796
+ fastify.route({
797
+ method: "POST",
798
+ url: "/get-city-forecast-by-zip",
799
+ schema,
800
+ handler: async (request) => {
801
+ const client = fastify.weatherClient;
802
+ const result = await client.GetCityForecastByZIP(request.body);
803
+ return buildSuccessEnvelope(result.response);
804
+ },
805
+ });
806
+ }
807
+ ```
808
+
809
+ ### Response Envelope
810
+
811
+ All responses are wrapped in a standard envelope format:
812
+
813
+ **Success Response:**
814
+ ```
815
+ {
816
+ "status": "SUCCESS",
817
+ "message": null,
818
+ "data": { /* SOAP response data */ },
819
+ "error": null
745
820
  }
746
821
  ```
747
822
 
748
- **Current workflow**:
749
- 1. Generate gateway scaffolding with `wsdl-tsc gateway`
750
- 2. Implement handler functions manually in each route file
751
- 3. Import and call your SOAP client
752
- 4. Transform SOAP responses to match OpenAPI schemas
753
- 5. Return responses in the standard envelope format
823
+ **Error Response:**
824
+ ```
825
+ {
826
+ "status": "ERROR",
827
+ "message": "Request validation failed",
828
+ "data": null,
829
+ "error": {
830
+ "code": "VALIDATION_ERROR",
831
+ "message": "Request validation failed",
832
+ "details": { /* validation errors */ }
833
+ }
834
+ }
835
+ ```
836
+
837
+ ### Error Handling
838
+
839
+ The centralized error handler (`runtime.ts`) automatically classifies errors:
840
+
841
+ | Error Type | HTTP Status | Error Code |
842
+ |-----------------------|-------------|-----------------------|
843
+ | Validation errors | 400 | `VALIDATION_ERROR` |
844
+ | SOAP faults | 502 | `SOAP_FAULT` |
845
+ | Connection refused | 503 | `SERVICE_UNAVAILABLE` |
846
+ | Timeout | 504 | `GATEWAY_TIMEOUT` |
847
+ | Other errors | 500 | `INTERNAL_ERROR` |
848
+
849
+ ### Stub Handler Mode (Backward Compatible)
850
+
851
+ If you prefer to implement handler logic manually or need custom transformation logic beyond the standard SOAP-to-REST mapping, use stub mode:
852
+
853
+ ```bash
854
+ npx wsdl-tsc gateway \
855
+ --openapi-file ./docs/weather-api.json \
856
+ --client-dir ./src/services/weather \
857
+ --gateway-dir ./src/gateway/weather \
858
+ --gateway-service-name weather \
859
+ --gateway-version-prefix v1 \
860
+ --gateway-stub-handlers
861
+ ```
754
862
 
755
- **Future enhancement**: Automated handler generation will include SOAP client calls, response transformation, error handling, and envelope wrapping.
863
+ This generates minimal handler stubs that throw "Not implemented" errors, allowing you to implement fully custom logic while keeping the routing and validation infrastructure.
756
864
 
757
865
  ---
758
866
 
package/dist/cli.js CHANGED
@@ -327,6 +327,33 @@ if (rawArgs[0] === "gateway") {
327
327
  .option("gateway-default-status-codes", {
328
328
  type: "string",
329
329
  desc: "Comma-separated status codes to backfill with default response (default: 200,400,401,403,404,409,422,429,500,502,503,504)"
330
+ })
331
+ .option("catalog-file", {
332
+ type: "string",
333
+ desc: "Path to catalog.json for operation metadata (enables type-safe handlers)"
334
+ })
335
+ .option("gateway-client-class-name", {
336
+ type: "string",
337
+ desc: "Override auto-detected SOAP client class name"
338
+ })
339
+ .option("gateway-decorator-name", {
340
+ type: "string",
341
+ desc: "Fastify decorator name for client (default: derived from service slug)"
342
+ })
343
+ .option("gateway-stub-handlers", {
344
+ type: "boolean",
345
+ default: false,
346
+ desc: "Generate stub handlers instead of full implementations"
347
+ })
348
+ .option("gateway-skip-plugin", {
349
+ type: "boolean",
350
+ default: false,
351
+ desc: "Skip generating plugin.ts wrapper"
352
+ })
353
+ .option("gateway-skip-runtime", {
354
+ type: "boolean",
355
+ default: false,
356
+ desc: "Skip generating runtime.ts utilities"
330
357
  })
331
358
  .strict()
332
359
  .help()
@@ -351,6 +378,13 @@ if (rawArgs[0] === "gateway") {
351
378
  serviceSlug: gatewayArgv["gateway-service-name"],
352
379
  defaultResponseStatusCodes,
353
380
  imports: gatewayArgv["import-extensions"],
381
+ catalogFile: gatewayArgv["catalog-file"],
382
+ clientClassName: gatewayArgv["gateway-client-class-name"],
383
+ clientDecoratorName: gatewayArgv["gateway-decorator-name"],
384
+ stubHandlers: gatewayArgv["gateway-stub-handlers"],
385
+ // Only override defaults if explicitly skipping (otherwise let generateGateway decide based on stubHandlers)
386
+ emitPlugin: gatewayArgv["gateway-skip-plugin"] ? false : undefined,
387
+ emitRuntime: gatewayArgv["gateway-skip-runtime"] ? false : undefined,
354
388
  });
355
389
  success(`Gateway code generated in ${outDir}`);
356
390
  process.exit(0);
@@ -430,6 +464,29 @@ if (rawArgs[0] === "pipeline") {
430
464
  .option("gateway-default-status-codes", {
431
465
  type: "string",
432
466
  desc: "Comma-separated status codes for gateway (default: 200,400,401,403,404,409,422,429,500,502,503,504)"
467
+ })
468
+ .option("gateway-client-class-name", {
469
+ type: "string",
470
+ desc: "Override auto-detected SOAP client class name for gateway"
471
+ })
472
+ .option("gateway-decorator-name", {
473
+ type: "string",
474
+ desc: "Fastify decorator name for client (default: derived from service slug)"
475
+ })
476
+ .option("gateway-stub-handlers", {
477
+ type: "boolean",
478
+ default: false,
479
+ desc: "Generate stub handlers instead of full implementations"
480
+ })
481
+ .option("gateway-skip-plugin", {
482
+ type: "boolean",
483
+ default: false,
484
+ desc: "Skip generating plugin.ts wrapper"
485
+ })
486
+ .option("gateway-skip-runtime", {
487
+ type: "boolean",
488
+ default: false,
489
+ desc: "Skip generating runtime.ts utilities"
433
490
  })
434
491
  .strict()
435
492
  .help()
@@ -507,6 +564,11 @@ if (rawArgs[0] === "pipeline") {
507
564
  outDir: path.resolve(gatewayOut),
508
565
  serviceSlug: pipelineArgv["gateway-service-name"],
509
566
  versionSlug: pipelineArgv["gateway-version-prefix"],
567
+ clientClassName: pipelineArgv["gateway-client-class-name"],
568
+ clientDecoratorName: pipelineArgv["gateway-decorator-name"],
569
+ stubHandlers: pipelineArgv["gateway-stub-handlers"],
570
+ emitPlugin: pipelineArgv["gateway-skip-plugin"] ? false : undefined,
571
+ emitRuntime: pipelineArgv["gateway-skip-runtime"] ? false : undefined,
510
572
  } : undefined,
511
573
  });
512
574
  process.exit(0);
@@ -114,6 +114,8 @@ export type CompiledCatalog = {
114
114
  inputElement?: QName;
115
115
  outputElement?: QName;
116
116
  security?: string[];
117
+ inputTypeName?: string;
118
+ outputTypeName?: string;
117
119
  }>;
118
120
  wsdlTargetNS: string;
119
121
  wsdlUri: string;
@@ -1 +1 @@
1
- {"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"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"}
@@ -615,7 +615,10 @@ export function compileCatalog(cat, options) {
615
615
  const outMsg = findMessage(getFirstWithLocalName(po, "output")?.["@_message"]);
616
616
  const inputElement = elementOfMessage(inMsg);
617
617
  const outputElement = elementOfMessage(outMsg);
618
- return { name, soapAction: bOps.get(name) || "", inputElement, outputElement };
618
+ // Derive TypeScript type names from element local names
619
+ const inputTypeName = inputElement ? pascal(inputElement.local) : undefined;
620
+ const outputTypeName = outputElement ? pascal(outputElement.local) : undefined;
621
+ return { name, soapAction: bOps.get(name) || "", inputElement, outputElement, inputTypeName, outputTypeName };
619
622
  })
620
623
  .filter((x) => x != null));
621
624
  // --- WS-Policy: scan for security requirements (inline policies only) ---
@@ -10,6 +10,12 @@
10
10
  * @property {string} serviceSlug - Service identifier for URN generation (required)
11
11
  * @property {number[]} [defaultResponseStatusCodes] - Status codes to backfill with default response
12
12
  * @property {"js"|"ts"|"bare"} [imports] - Import-extension mode for generated TypeScript modules (mirrors global --imports)
13
+ * @property {string} [clientClassName] - Override auto-detected SOAP client class name
14
+ * @property {string} [clientDecoratorName] - Fastify decorator name for client (default: derived from serviceSlug)
15
+ * @property {string} [catalogFile] - Path to catalog.json for operation metadata
16
+ * @property {boolean} [emitPlugin] - Whether to emit plugin.ts (default: true)
17
+ * @property {boolean} [emitRuntime] - Whether to emit runtime.ts (default: true)
18
+ * @property {boolean} [stubHandlers] - If true, emit stubs instead of full handlers (default: false)
13
19
  */
14
20
  export interface GenerateGatewayOptions {
15
21
  openapiFile?: string;
@@ -20,6 +26,12 @@ export interface GenerateGatewayOptions {
20
26
  serviceSlug: string;
21
27
  defaultResponseStatusCodes?: number[];
22
28
  imports?: "js" | "ts" | "bare";
29
+ clientClassName?: string;
30
+ clientDecoratorName?: string;
31
+ catalogFile?: string;
32
+ emitPlugin?: boolean;
33
+ emitRuntime?: boolean;
34
+ stubHandlers?: boolean;
23
35
  }
24
36
  /**
25
37
  * Generates Fastify gateway code from an OpenAPI 3.1 specification
@@ -1 +1 @@
1
- {"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AAiCA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFjF"}
1
+ {"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AA4CA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAE/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqJjF"}
@@ -23,8 +23,8 @@
23
23
  import fs from "node:fs";
24
24
  import path from "node:path";
25
25
  import * as yaml from "js-yaml";
26
- import { buildParamSchemasForOperation, getJsonSchemaRefName, isNumericStatus, } from "./helpers.js";
27
- import { emitModelSchemas, emitOperationSchemas, emitRouteFiles, emitSchemasModule, } from "./generators.js";
26
+ import { buildParamSchemasForOperation, getJsonSchemaRefName, isNumericStatus, resolveClientMeta, resolveOperationMeta, } from "./helpers.js";
27
+ import { emitModelSchemas, emitOperationSchemas, emitPluginModule, emitRouteFiles, emitRouteFilesWithHandlers, emitRuntimeModule, emitSchemasModule, } from "./generators.js";
28
28
  /**
29
29
  * Generates Fastify gateway code from an OpenAPI 3.1 specification
30
30
  *
@@ -119,6 +119,25 @@ export async function generateGateway(opts) {
119
119
  ];
120
120
  // Determine import-extension mode (defaults to "js" like the client/compiler)
121
121
  const importsMode = opts.imports ?? "js";
122
+ // Determine if we should generate full handlers or stubs
123
+ const stubHandlers = opts.stubHandlers ?? false;
124
+ const emitPlugin = opts.emitPlugin ?? !stubHandlers;
125
+ const emitRuntime = opts.emitRuntime ?? !stubHandlers;
126
+ // Load catalog if provided (for operation metadata)
127
+ let catalog = undefined;
128
+ if (opts.catalogFile && fs.existsSync(opts.catalogFile)) {
129
+ const catalogRaw = fs.readFileSync(opts.catalogFile, "utf8");
130
+ catalog = JSON.parse(catalogRaw);
131
+ }
132
+ // Resolve client metadata for full handler generation
133
+ const clientMeta = resolveClientMeta({
134
+ clientDir: opts.clientDir,
135
+ catalogFile: opts.catalogFile,
136
+ clientClassName: opts.clientClassName,
137
+ clientDecoratorName: opts.clientDecoratorName,
138
+ serviceSlug,
139
+ importsMode,
140
+ }, catalog);
122
141
  // Prepare output directories
123
142
  const outDir = opts.outDir;
124
143
  const modelsDir = path.join(outDir, "schemas", "models");
@@ -126,10 +145,42 @@ export async function generateGateway(opts) {
126
145
  const routesDir = path.join(outDir, "routes");
127
146
  // Step 1: Generate model schemas and build URN mapping
128
147
  const schemaIdByName = emitModelSchemas(doc.components.schemas, modelsDir, versionSlug, serviceSlug);
129
- // Step 2: Generate operation schemas
130
- const operations = emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, schemaIdByName, defaultResponseStatusCodes, buildParamSchemasForOperation, getJsonSchemaRefName, isNumericStatus);
131
- // Step 3: Emit schemas.ts module
148
+ // Step 2: Generate operation schemas and collect basic metadata
149
+ const basicOperations = emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, schemaIdByName, defaultResponseStatusCodes, buildParamSchemasForOperation, getJsonSchemaRefName, isNumericStatus);
150
+ // Step 3: Enrich operations with type information for full handlers
151
+ const operations = basicOperations.map((op) => {
152
+ // Extract operationId from OpenAPI document
153
+ const pathItem = doc.paths[op.path];
154
+ const opDef = pathItem?.[op.method];
155
+ const operationId = opDef?.operationId || op.operationSlug;
156
+ // Resolve full operation metadata from catalog
157
+ const resolved = resolveOperationMeta(operationId, op.operationSlug, op.method, op.path, catalog?.operations);
158
+ return {
159
+ ...op,
160
+ operationId: resolved.operationId,
161
+ clientMethodName: resolved.clientMethodName,
162
+ requestTypeName: resolved.requestTypeName,
163
+ responseTypeName: resolved.responseTypeName,
164
+ };
165
+ });
166
+ // Step 4: Emit schemas.ts module
132
167
  emitSchemasModule(outDir, modelsDir, versionSlug, serviceSlug);
133
- // Step 4: Emit route files and routes.ts module
134
- emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode);
168
+ // Step 5: Emit route files (with handlers or stubs)
169
+ // Note: Route URLs come from OpenAPI paths which already include any base path
170
+ if (stubHandlers) {
171
+ // Legacy mode: emit stub handlers
172
+ emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode);
173
+ }
174
+ else {
175
+ // Full handler mode: emit working implementations
176
+ emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode, clientMeta);
177
+ }
178
+ // Step 6: Emit runtime.ts (if enabled)
179
+ if (emitRuntime) {
180
+ emitRuntimeModule(outDir, versionSlug, serviceSlug);
181
+ }
182
+ // Step 7: Emit plugin.ts (if enabled)
183
+ if (emitPlugin) {
184
+ emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, importsMode);
185
+ }
135
186
  }
@@ -1,4 +1,4 @@
1
- import { type OpenAPIDocument } from "./helpers.js";
1
+ import { type ClientMeta, type OpenAPIDocument } from "./helpers.js";
2
2
  /**
3
3
  * Emits individual JSON Schema files for each OpenAPI component schema
4
4
  *
@@ -18,11 +18,24 @@ import { type OpenAPIDocument } from "./helpers.js";
18
18
  export declare function emitModelSchemas(schemas: Record<string, any>, modelsDir: string, versionSlug: string, serviceSlug: string): Record<string, string>;
19
19
  /**
20
20
  * Operation metadata for route generation
21
+ *
22
+ * @interface OperationMetadata
23
+ * @property {string} operationSlug - Slugified operation name for filenames
24
+ * @property {string} method - HTTP method (lowercase)
25
+ * @property {string} path - URL path
26
+ * @property {string} [operationId] - OpenAPI operationId
27
+ * @property {string} [clientMethodName] - SOAP client method to call
28
+ * @property {string} [requestTypeName] - TypeScript request type name
29
+ * @property {string} [responseTypeName] - TypeScript response type name
21
30
  */
22
31
  export interface OperationMetadata {
23
32
  operationSlug: string;
24
33
  method: string;
25
34
  path: string;
35
+ operationId?: string;
36
+ clientMethodName?: string;
37
+ requestTypeName?: string;
38
+ responseTypeName?: string;
26
39
  }
27
40
  /**
28
41
  * Emits Fastify-compatible operation schema files
@@ -79,6 +92,9 @@ export declare function emitSchemasModule(outDir: string, modelsDir: string, ver
79
92
  * - Imports all route registration functions
80
93
  * - Exports registerRoutes_{version}_{service}(fastify) function
81
94
  *
95
+ * Note: Route URLs come from OpenAPI paths which already include any base path
96
+ * configured during OpenAPI generation (--openapi-base-path).
97
+ *
82
98
  * @param {string} outDir - Root output directory
83
99
  * @param {string} routesDir - Directory for individual route files
84
100
  * @param {string} versionSlug - Version slug for function naming
@@ -87,4 +103,53 @@ export declare function emitSchemasModule(outDir: string, modelsDir: string, ver
87
103
  * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode for generated TypeScript modules
88
104
  */
89
105
  export declare function emitRouteFiles(outDir: string, routesDir: string, versionSlug: string, serviceSlug: string, operations: OperationMetadata[], importsMode: "js" | "ts" | "bare"): void;
106
+ /**
107
+ * Emits runtime.ts module with envelope builders and error handling utilities
108
+ *
109
+ * Generated code includes:
110
+ * - Response envelope types (SuccessEnvelope, ErrorEnvelope)
111
+ * - buildSuccessEnvelope() and buildErrorEnvelope() functions
112
+ * - classifyError() for mapping errors to HTTP status codes
113
+ * - createGatewayErrorHandler_{version}_{service}() factory function
114
+ *
115
+ * @param {string} outDir - Root output directory
116
+ * @param {string} versionSlug - Version slug for function naming
117
+ * @param {string} serviceSlug - Service slug for function naming
118
+ */
119
+ export declare function emitRuntimeModule(outDir: string, versionSlug: string, serviceSlug: string): void;
120
+ /**
121
+ * Emits plugin.ts module as the primary Fastify plugin wrapper
122
+ *
123
+ * Generated code includes:
124
+ * - Plugin options interface with client and prefix support
125
+ * - Fastify decorator type augmentation
126
+ * - Plugin implementation that registers schemas, routes, and error handler
127
+ *
128
+ * @param {string} outDir - Root output directory
129
+ * @param {string} versionSlug - Version slug for function naming
130
+ * @param {string} serviceSlug - Service slug for function naming
131
+ * @param {ClientMeta} clientMeta - Client class metadata
132
+ * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
133
+ */
134
+ export declare function emitPluginModule(outDir: string, versionSlug: string, serviceSlug: string, clientMeta: ClientMeta, importsMode: "js" | "ts" | "bare"): void;
135
+ /**
136
+ * Emits individual route files with full handler implementations
137
+ *
138
+ * For each operation:
139
+ * - Creates routes/{slug}.ts with registerRoute_{slug} function
140
+ * - Imports operation schema, types, and runtime utilities
141
+ * - Implements handler that calls decorated client and returns envelope
142
+ *
143
+ * Note: Route URLs come from OpenAPI paths which already include any base path
144
+ * configured during OpenAPI generation (--openapi-base-path).
145
+ *
146
+ * @param {string} outDir - Root output directory
147
+ * @param {string} routesDir - Directory for individual route files
148
+ * @param {string} versionSlug - Version slug for function naming
149
+ * @param {string} serviceSlug - Service slug for function naming
150
+ * @param {OperationMetadata[]} operations - Array of operation metadata
151
+ * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
152
+ * @param {ClientMeta} clientMeta - Client class metadata
153
+ */
154
+ export declare function emitRouteFilesWithHandlers(outDir: string, routesDir: string, versionSlug: string, serviceSlug: string, operations: OperationMetadata[], importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta): void;
90
155
  //# sourceMappingURL=generators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"AAcA,OAAO,EAAC,KAAK,eAAe,EAA+B,MAAM,cAAc,CAAC;AAEhF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0BxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,0BAA0B,EAAE,MAAM,EAAE,EACpC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,GAAG,EACvH,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,EACnC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GACnC,iBAAiB,EAAE,CA2HrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,IAAI,CAsBN;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,iBAAiB,EAAE,EAC/B,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,IAAI,CA0CN"}
1
+ {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"AAcA,OAAO,EAAC,KAAK,UAAU,EAAE,KAAK,eAAe,EAA+B,MAAM,cAAc,CAAC;AAEjG;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0BxB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,0BAA0B,EAAE,MAAM,EAAE,EACpC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,GAAG,EACvH,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,EACnC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GACnC,iBAAiB,EAAE,CA2HrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,IAAI,CAsBN;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,iBAAiB,EAAE,EAC/B,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,IAAI,CA2CN;AAGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,IAAI,CAkLN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,IAAI,CAyFN;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,iBAAiB,EAAE,EAC/B,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,GACrB,IAAI,CAwEN"}
@@ -226,6 +226,9 @@ export function emitSchemasModule(outDir, modelsDir, versionSlug, serviceSlug) {
226
226
  * - Imports all route registration functions
227
227
  * - Exports registerRoutes_{version}_{service}(fastify) function
228
228
  *
229
+ * Note: Route URLs come from OpenAPI paths which already include any base path
230
+ * configured during OpenAPI generation (--openapi-base-path).
231
+ *
229
232
  * @param {string} outDir - Root output directory
230
233
  * @param {string} routesDir - Directory for individual route files
231
234
  * @param {string} versionSlug - Version slug for function naming
@@ -244,6 +247,7 @@ export function emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, oper
244
247
  const routeFileBase = op.operationSlug;
245
248
  routesTs += `import { ${fnName} } from "./routes/${routeFileBase}${suffix}";\n`;
246
249
  // Generate individual route file
250
+ // Note: op.path comes from OpenAPI and already includes any base path
247
251
  let routeTs = "";
248
252
  routeTs += `import type { FastifyInstance } from "fastify";\n`;
249
253
  routeTs += `import schema from "../schemas/operations/${op.operationSlug}.json" with { type: "json" };\n\n`;
@@ -268,3 +272,376 @@ export function emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, oper
268
272
  routesTs += `}\n`;
269
273
  fs.writeFileSync(path.join(outDir, "routes.ts"), routesTs, "utf8");
270
274
  }
275
+ /**
276
+ * Emits runtime.ts module with envelope builders and error handling utilities
277
+ *
278
+ * Generated code includes:
279
+ * - Response envelope types (SuccessEnvelope, ErrorEnvelope)
280
+ * - buildSuccessEnvelope() and buildErrorEnvelope() functions
281
+ * - classifyError() for mapping errors to HTTP status codes
282
+ * - createGatewayErrorHandler_{version}_{service}() factory function
283
+ *
284
+ * @param {string} outDir - Root output directory
285
+ * @param {string} versionSlug - Version slug for function naming
286
+ * @param {string} serviceSlug - Service slug for function naming
287
+ */
288
+ export function emitRuntimeModule(outDir, versionSlug, serviceSlug) {
289
+ const vSlug = slugName(versionSlug);
290
+ const sSlug = slugName(serviceSlug);
291
+ const runtimeTs = `/**
292
+ * Gateway Runtime Utilities
293
+ *
294
+ * Provides envelope builders and error handling for the generated gateway.
295
+ * Auto-generated - do not edit manually.
296
+ */
297
+ import type { FastifyReply, FastifyRequest } from "fastify";
298
+
299
+ /**
300
+ * Success response envelope
301
+ */
302
+ export interface SuccessEnvelope<T> {
303
+ status: "SUCCESS";
304
+ message: string | null;
305
+ data: T;
306
+ error: null;
307
+ }
308
+
309
+ /**
310
+ * Error response envelope
311
+ */
312
+ export interface ErrorEnvelope {
313
+ status: "ERROR";
314
+ message: string;
315
+ data: null;
316
+ error: {
317
+ code: string;
318
+ message: string;
319
+ details?: unknown;
320
+ };
321
+ }
322
+
323
+ /**
324
+ * Union type for response envelopes
325
+ */
326
+ export type ResponseEnvelope<T> = SuccessEnvelope<T> | ErrorEnvelope;
327
+
328
+ /**
329
+ * Builds a success response envelope
330
+ *
331
+ * @param data - The response data
332
+ * @param message - Optional success message
333
+ * @returns Success envelope wrapping the data
334
+ */
335
+ export function buildSuccessEnvelope<T>(data: T, message?: string): SuccessEnvelope<T> {
336
+ return {
337
+ status: "SUCCESS",
338
+ message: message ?? null,
339
+ data,
340
+ error: null,
341
+ };
342
+ }
343
+
344
+ /**
345
+ * Builds an error response envelope
346
+ *
347
+ * @param code - Error code (e.g., "VALIDATION_ERROR")
348
+ * @param message - Human-readable error message
349
+ * @param details - Optional error details
350
+ * @returns Error envelope with the error information
351
+ */
352
+ export function buildErrorEnvelope(
353
+ code: string,
354
+ message: string,
355
+ details?: unknown
356
+ ): ErrorEnvelope {
357
+ return {
358
+ status: "ERROR",
359
+ message,
360
+ data: null,
361
+ error: { code, message, details },
362
+ };
363
+ }
364
+
365
+ /**
366
+ * Classified error with HTTP status and details
367
+ */
368
+ export interface ClassifiedError {
369
+ httpStatus: number;
370
+ code: string;
371
+ message: string;
372
+ details?: unknown;
373
+ }
374
+
375
+ /**
376
+ * Classifies an error and maps it to an appropriate HTTP status code
377
+ *
378
+ * @param err - The error to classify
379
+ * @returns Classified error with HTTP status, code, and message
380
+ */
381
+ export function classifyError(err: unknown): ClassifiedError {
382
+ // Fastify validation errors
383
+ if (err && typeof err === "object" && "validation" in err) {
384
+ return {
385
+ httpStatus: 400,
386
+ code: "VALIDATION_ERROR",
387
+ message: "Request validation failed",
388
+ details: (err as Record<string, unknown>).validation,
389
+ };
390
+ }
391
+
392
+ // SOAP fault errors (node-soap throws these)
393
+ if (err && typeof err === "object" && "root" in err) {
394
+ const root = (err as Record<string, unknown>).root as Record<string, unknown> | undefined;
395
+ const envelope = root?.Envelope as Record<string, unknown> | undefined;
396
+ const body = envelope?.Body as Record<string, unknown> | undefined;
397
+ const fault = body?.Fault as Record<string, unknown> | undefined;
398
+ if (fault) {
399
+ return {
400
+ httpStatus: 502,
401
+ code: "SOAP_FAULT",
402
+ message: (fault.faultstring as string) || "SOAP service returned a fault",
403
+ details: fault,
404
+ };
405
+ }
406
+ }
407
+
408
+ // Connection/timeout errors
409
+ if (err instanceof Error) {
410
+ if (err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND")) {
411
+ return {
412
+ httpStatus: 503,
413
+ code: "SERVICE_UNAVAILABLE",
414
+ message: "Unable to connect to SOAP service",
415
+ details: err.message,
416
+ };
417
+ }
418
+ if (err.message.includes("ETIMEDOUT") || err.message.includes("timeout")) {
419
+ return {
420
+ httpStatus: 504,
421
+ code: "GATEWAY_TIMEOUT",
422
+ message: "SOAP service request timed out",
423
+ details: err.message,
424
+ };
425
+ }
426
+ }
427
+
428
+ // Generic error fallback
429
+ const message = err instanceof Error ? err.message : String(err);
430
+ return {
431
+ httpStatus: 500,
432
+ code: "INTERNAL_ERROR",
433
+ message,
434
+ details: process.env.NODE_ENV === "development" ? err : undefined,
435
+ };
436
+ }
437
+
438
+ /**
439
+ * Creates a gateway error handler for this service
440
+ *
441
+ * @returns Fastify error handler function
442
+ */
443
+ export function createGatewayErrorHandler_${vSlug}_${sSlug}() {
444
+ return async function gatewayErrorHandler(
445
+ error: Error,
446
+ request: FastifyRequest,
447
+ reply: FastifyReply
448
+ ): Promise<ErrorEnvelope> {
449
+ const classified = classifyError(error);
450
+
451
+ request.log.error({
452
+ err: error,
453
+ classified,
454
+ url: request.url,
455
+ method: request.method,
456
+ }, "Gateway error");
457
+
458
+ reply.status(classified.httpStatus);
459
+ return buildErrorEnvelope(classified.code, classified.message, classified.details);
460
+ };
461
+ }
462
+ `;
463
+ fs.writeFileSync(path.join(outDir, "runtime.ts"), runtimeTs, "utf8");
464
+ }
465
+ /**
466
+ * Emits plugin.ts module as the primary Fastify plugin wrapper
467
+ *
468
+ * Generated code includes:
469
+ * - Plugin options interface with client and prefix support
470
+ * - Fastify decorator type augmentation
471
+ * - Plugin implementation that registers schemas, routes, and error handler
472
+ *
473
+ * @param {string} outDir - Root output directory
474
+ * @param {string} versionSlug - Version slug for function naming
475
+ * @param {string} serviceSlug - Service slug for function naming
476
+ * @param {ClientMeta} clientMeta - Client class metadata
477
+ * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
478
+ */
479
+ export function emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, importsMode) {
480
+ const vSlug = slugName(versionSlug);
481
+ const sSlug = slugName(serviceSlug);
482
+ const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
483
+ const pluginTs = `/**
484
+ * ${clientMeta.className} Gateway Plugin
485
+ *
486
+ * Fastify plugin that registers the SOAP-to-REST gateway for ${serviceSlug}.
487
+ * Auto-generated - do not edit manually.
488
+ */
489
+ import fp from "fastify-plugin";
490
+ import type { FastifyInstance, FastifyPluginOptions } from "fastify";
491
+ import { registerSchemas_${vSlug}_${sSlug} } from "./schemas${suffix}";
492
+ import { registerRoutes_${vSlug}_${sSlug} } from "./routes${suffix}";
493
+ import { createGatewayErrorHandler_${vSlug}_${sSlug} } from "./runtime${suffix}";
494
+
495
+ /**
496
+ * Options for the ${clientMeta.className} gateway plugin
497
+ */
498
+ export interface ${clientMeta.className}GatewayOptions extends FastifyPluginOptions {
499
+ /**
500
+ * SOAP client instance (pre-configured).
501
+ * The client should be instantiated with appropriate source and security settings.
502
+ */
503
+ client: {
504
+ [method: string]: (args: unknown) => Promise<{ response: unknown; headers: unknown }>;
505
+ };
506
+ /**
507
+ * Optional additional route prefix applied at runtime.
508
+ * Note: If you used --openapi-base-path during generation, routes already have that prefix baked in.
509
+ * Only use this for additional runtime prefixing (e.g., mounting under a versioned sub-path).
510
+ */
511
+ prefix?: string;
512
+ }
513
+
514
+ /**
515
+ * Fastify decorator type augmentation
516
+ */
517
+ declare module "fastify" {
518
+ interface FastifyInstance {
519
+ ${clientMeta.decoratorName}: {
520
+ [method: string]: (args: unknown) => Promise<{ response: unknown; headers: unknown }>;
521
+ };
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Gateway plugin implementation
527
+ *
528
+ * @param fastify - Fastify instance
529
+ * @param opts - Plugin options including client and optional prefix
530
+ */
531
+ async function ${sSlug}GatewayPlugin(
532
+ fastify: FastifyInstance,
533
+ opts: ${clientMeta.className}GatewayOptions
534
+ ): Promise<void> {
535
+ // Decorate with SOAP client
536
+ if (!fastify.hasDecorator("${clientMeta.decoratorName}")) {
537
+ fastify.decorate("${clientMeta.decoratorName}", opts.client);
538
+ }
539
+
540
+ // Register model schemas
541
+ await registerSchemas_${vSlug}_${sSlug}(fastify);
542
+
543
+ // Register error handler (scoped to this plugin)
544
+ fastify.setErrorHandler(createGatewayErrorHandler_${vSlug}_${sSlug}());
545
+
546
+ // Register routes (optionally prefixed)
547
+ if (opts.prefix) {
548
+ await fastify.register(async (child) => {
549
+ await registerRoutes_${vSlug}_${sSlug}(child);
550
+ }, { prefix: opts.prefix });
551
+ } else {
552
+ await registerRoutes_${vSlug}_${sSlug}(fastify);
553
+ }
554
+ }
555
+
556
+ // Export as Fastify plugin (encapsulated)
557
+ export default fp(${sSlug}GatewayPlugin, {
558
+ fastify: "5.x",
559
+ name: "${sSlug}-gateway",
560
+ });
561
+
562
+ // Named export for convenience
563
+ export { ${sSlug}GatewayPlugin };
564
+ `;
565
+ fs.writeFileSync(path.join(outDir, "plugin.ts"), pluginTs, "utf8");
566
+ }
567
+ /**
568
+ * Emits individual route files with full handler implementations
569
+ *
570
+ * For each operation:
571
+ * - Creates routes/{slug}.ts with registerRoute_{slug} function
572
+ * - Imports operation schema, types, and runtime utilities
573
+ * - Implements handler that calls decorated client and returns envelope
574
+ *
575
+ * Note: Route URLs come from OpenAPI paths which already include any base path
576
+ * configured during OpenAPI generation (--openapi-base-path).
577
+ *
578
+ * @param {string} outDir - Root output directory
579
+ * @param {string} routesDir - Directory for individual route files
580
+ * @param {string} versionSlug - Version slug for function naming
581
+ * @param {string} serviceSlug - Service slug for function naming
582
+ * @param {OperationMetadata[]} operations - Array of operation metadata
583
+ * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
584
+ * @param {ClientMeta} clientMeta - Client class metadata
585
+ */
586
+ export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode, clientMeta) {
587
+ fs.mkdirSync(routesDir, { recursive: true });
588
+ // Sort operations for deterministic output
589
+ operations.sort((a, b) => a.operationSlug.localeCompare(b.operationSlug));
590
+ const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
591
+ const vSlug = slugName(versionSlug);
592
+ const sSlug = slugName(serviceSlug);
593
+ let routesTs = `import type { FastifyInstance } from "fastify";\n`;
594
+ operations.forEach((op) => {
595
+ const fnName = `registerRoute_${vSlug}_${sSlug}_${op.operationSlug.replace(/[^a-zA-Z0-9_]/g, "_")}`;
596
+ const routeFileBase = op.operationSlug;
597
+ routesTs += `import { ${fnName} } from "./routes/${routeFileBase}${suffix}";\n`;
598
+ // Generate individual route file with full handler
599
+ const clientMethod = op.clientMethodName || op.operationId || op.operationSlug;
600
+ // Type names for documentation; we use unknown at runtime since schema validation handles it
601
+ const reqTypeName = op.requestTypeName || "unknown";
602
+ const resTypeName = op.responseTypeName || "unknown";
603
+ // Note: op.path comes from OpenAPI and already includes any base path
604
+ let routeTs = `/**
605
+ * Route: ${op.method.toUpperCase()} ${op.path}
606
+ * Operation: ${op.operationId || op.operationSlug}
607
+ * Request Type: ${reqTypeName}
608
+ * Response Type: ${resTypeName} (wrapped in envelope)
609
+ * Auto-generated - do not edit manually.
610
+ */
611
+ import type { FastifyInstance } from "fastify";
612
+ import schema from "../schemas/operations/${op.operationSlug}.json" with { type: "json" };
613
+ import { buildSuccessEnvelope } from "../runtime${suffix}";
614
+
615
+ export async function ${fnName}(fastify: FastifyInstance) {
616
+ fastify.route({
617
+ method: "${op.method.toUpperCase()}",
618
+ url: "${op.path}",
619
+ schema,
620
+ handler: async (request) => {
621
+ const client = fastify.${clientMeta.decoratorName};
622
+ const result = await client.${clientMethod}(request.body);
623
+ return buildSuccessEnvelope(result.response);
624
+ },
625
+ });
626
+ }
627
+ `;
628
+ fs.writeFileSync(path.join(routesDir, `${routeFileBase}.ts`), routeTs, "utf8");
629
+ });
630
+ // Generate routes.ts aggregator module
631
+ const routeFnName = `registerRoutes_${vSlug}_${sSlug}`;
632
+ routesTs += `
633
+ /**
634
+ * Registers all ${serviceSlug} routes with the Fastify instance.
635
+ * Route paths are determined by the OpenAPI specification (--openapi-base-path).
636
+ */
637
+ export async function ${routeFnName}(fastify: FastifyInstance): Promise<void> {
638
+ `;
639
+ // Generate route registration calls
640
+ const routeCalls = operations.map((op) => {
641
+ const fnName = `registerRoute_${vSlug}_${sSlug}_${op.operationSlug.replace(/[^a-zA-Z0-9_]/g, "_")}`;
642
+ return ` await ${fnName}(fastify);`;
643
+ }).join("\n");
644
+ routesTs += ` // Register all routes\n${routeCalls}\n`;
645
+ routesTs += `}\n`;
646
+ fs.writeFileSync(path.join(outDir, "routes.ts"), routesTs, "utf8");
647
+ }
@@ -112,4 +112,99 @@ export interface ParamSchemaBuildResult {
112
112
  * @returns {ParamSchemaBuildResult} - Object with optional params/querystring/headers schemas
113
113
  */
114
114
  export declare function buildParamSchemasForOperation(pathItem: any, operation: any, doc: OpenAPIDocument, schemaIdByName: Record<string, string>): ParamSchemaBuildResult;
115
+ /**
116
+ * Client Metadata Resolution
117
+ *
118
+ * Types and functions for resolving SOAP client metadata used in handler generation.
119
+ */
120
+ /**
121
+ * Metadata about the SOAP client for handler generation
122
+ *
123
+ * @interface ClientMeta
124
+ * @property {string} className - Client class name (e.g., "EVRNService")
125
+ * @property {string} decoratorName - Fastify decorator name (e.g., "evrnserviceClient")
126
+ * @property {string} importPath - Import path relative to routes directory
127
+ * @property {string} typesImportPath - Import path for types
128
+ */
129
+ export interface ClientMeta {
130
+ className: string;
131
+ decoratorName: string;
132
+ importPath: string;
133
+ typesImportPath: string;
134
+ }
135
+ /**
136
+ * Extended operation metadata for full handler generation
137
+ *
138
+ * @interface ResolvedOperationMeta
139
+ * @property {string} operationId - OpenAPI operationId
140
+ * @property {string} operationSlug - Slugified for filenames
141
+ * @property {string} method - HTTP method
142
+ * @property {string} path - URL path
143
+ * @property {string} clientMethodName - SOAP client method to call
144
+ * @property {string} [requestTypeName] - TypeScript request type
145
+ * @property {string} [responseTypeName] - TypeScript response type
146
+ */
147
+ export interface ResolvedOperationMeta {
148
+ operationId: string;
149
+ operationSlug: string;
150
+ method: string;
151
+ path: string;
152
+ clientMethodName: string;
153
+ requestTypeName?: string;
154
+ responseTypeName?: string;
155
+ }
156
+ /**
157
+ * PascalCase conversion utility
158
+ *
159
+ * @param {string} str - String to convert to PascalCase
160
+ * @returns {string} - PascalCase version of the string
161
+ */
162
+ export declare function pascalCase(str: string): string;
163
+ /**
164
+ * Options for resolving client metadata
165
+ *
166
+ * @interface ResolveClientMetaOptions
167
+ * @property {string} [clientDir] - Path to client directory
168
+ * @property {string} [catalogFile] - Path to catalog.json
169
+ * @property {string} [clientClassName] - Override client class name
170
+ * @property {string} [clientDecoratorName] - Override decorator name
171
+ * @property {string} serviceSlug - Service slug for derivation
172
+ * @property {"js"|"ts"|"bare"} importsMode - Import extension mode
173
+ */
174
+ export interface ResolveClientMetaOptions {
175
+ clientDir?: string;
176
+ catalogFile?: string;
177
+ clientClassName?: string;
178
+ clientDecoratorName?: string;
179
+ serviceSlug: string;
180
+ importsMode: "js" | "ts" | "bare";
181
+ }
182
+ /**
183
+ * Resolves client metadata from available sources
184
+ *
185
+ * Resolution priority:
186
+ * 1. Explicit clientClassName/clientDecoratorName options
187
+ * 2. serviceName from catalog.json if catalogFile provided
188
+ * 3. Derive from serviceSlug
189
+ *
190
+ * @param {ResolveClientMetaOptions} opts - Options for resolving client metadata
191
+ * @param {any} [catalog] - Optional loaded catalog object
192
+ * @returns {ClientMeta} - Resolved client metadata
193
+ */
194
+ export declare function resolveClientMeta(opts: ResolveClientMetaOptions, catalog?: any): ClientMeta;
195
+ /**
196
+ * Resolves operation metadata by matching OpenAPI operationId to catalog operations
197
+ *
198
+ * @param {string} operationId - OpenAPI operation ID
199
+ * @param {string} operationSlug - Slugified operation name for filenames
200
+ * @param {string} method - HTTP method
201
+ * @param {string} path - URL path
202
+ * @param {Array} [catalogOperations] - Optional catalog operations to match against
203
+ * @returns {ResolvedOperationMeta} - Resolved operation metadata with type names
204
+ */
205
+ export declare function resolveOperationMeta(operationId: string, operationSlug: string, method: string, path: string, catalogOperations?: Array<{
206
+ name: string;
207
+ inputTypeName?: string;
208
+ outputTypeName?: string;
209
+ }>): ResolvedOperationMeta;
115
210
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"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"}
@@ -222,3 +222,87 @@ export function buildParamSchemasForOperation(pathItem, operation, doc, schemaId
222
222
  headersSchema,
223
223
  };
224
224
  }
225
+ /**
226
+ * PascalCase conversion utility
227
+ *
228
+ * @param {string} str - String to convert to PascalCase
229
+ * @returns {string} - PascalCase version of the string
230
+ */
231
+ export function pascalCase(str) {
232
+ return str
233
+ .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ""))
234
+ .replace(/^(.)/, (c) => c.toUpperCase());
235
+ }
236
+ /**
237
+ * Resolves client metadata from available sources
238
+ *
239
+ * Resolution priority:
240
+ * 1. Explicit clientClassName/clientDecoratorName options
241
+ * 2. serviceName from catalog.json if catalogFile provided
242
+ * 3. Derive from serviceSlug
243
+ *
244
+ * @param {ResolveClientMetaOptions} opts - Options for resolving client metadata
245
+ * @param {any} [catalog] - Optional loaded catalog object
246
+ * @returns {ClientMeta} - Resolved client metadata
247
+ */
248
+ export function resolveClientMeta(opts, catalog) {
249
+ const suffix = opts.importsMode === "bare" ? "" : `.${opts.importsMode}`;
250
+ // Determine client class name
251
+ let className;
252
+ if (opts.clientClassName) {
253
+ className = opts.clientClassName;
254
+ }
255
+ else if (catalog?.serviceName) {
256
+ className = pascalCase(catalog.serviceName);
257
+ }
258
+ else {
259
+ className = pascalCase(opts.serviceSlug);
260
+ }
261
+ // Determine decorator name
262
+ let decoratorName;
263
+ if (opts.clientDecoratorName) {
264
+ decoratorName = opts.clientDecoratorName;
265
+ }
266
+ else {
267
+ decoratorName = slugName(className) + "Client";
268
+ }
269
+ // Build import paths (relative from routes/ to client/)
270
+ const importPath = opts.clientDir
271
+ ? `../../client/client${suffix}`
272
+ : `../client/client${suffix}`;
273
+ const typesImportPath = opts.clientDir
274
+ ? `../../client/types${suffix}`
275
+ : `../client/types${suffix}`;
276
+ return {
277
+ className,
278
+ decoratorName,
279
+ importPath,
280
+ typesImportPath,
281
+ };
282
+ }
283
+ /**
284
+ * Resolves operation metadata by matching OpenAPI operationId to catalog operations
285
+ *
286
+ * @param {string} operationId - OpenAPI operation ID
287
+ * @param {string} operationSlug - Slugified operation name for filenames
288
+ * @param {string} method - HTTP method
289
+ * @param {string} path - URL path
290
+ * @param {Array} [catalogOperations] - Optional catalog operations to match against
291
+ * @returns {ResolvedOperationMeta} - Resolved operation metadata with type names
292
+ */
293
+ export function resolveOperationMeta(operationId, operationSlug, method, path, catalogOperations) {
294
+ // Try to find matching operation in catalog
295
+ const catalogOp = catalogOperations?.find(op => op.name === operationId || slugName(op.name) === operationSlug);
296
+ // Use catalog type names if available, otherwise derive by convention
297
+ const requestTypeName = catalogOp?.inputTypeName ?? pascalCase(operationId);
298
+ const responseTypeName = catalogOp?.outputTypeName ?? pascalCase(operationId);
299
+ return {
300
+ operationId,
301
+ operationSlug,
302
+ method,
303
+ path,
304
+ clientMethodName: operationId, // Client methods use the original operation name
305
+ requestTypeName,
306
+ responseTypeName,
307
+ };
308
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techspokes/typescript-wsdl-client",
3
- "version": "0.8.19",
3
+ "version": "0.9.1",
4
4
  "description": "TypeScript WSDL → SOAP client generator with full xs:attribute support, complex types, sequences, inheritance, and namespace-collision merging.",
5
5
  "keywords": [
6
6
  "wsdl",
@@ -65,6 +65,7 @@
65
65
  "@types/node": "^25.0.2",
66
66
  "@types/yargs": "^17.0.33",
67
67
  "fastify": "^5.2.0",
68
+ "fastify-plugin": "^5.1.0",
68
69
  "rimraf": "^6.0.0",
69
70
  "tsx": "^4.20.0",
70
71
  "typescript": "^5.6.3"