@techspokes/typescript-wsdl-client 0.8.19 → 0.9.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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 TechSpokes, Inc.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 TechSpokes, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -621,10 +621,18 @@ npx wsdl-tsc gateway \
621
621
 
622
622
  ### Optional Flags
623
623
 
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 |
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 |
628
+ | `--catalog-file` | *(none)* | Path to `catalog.json` for operation metadata |
629
+ | `--gateway-client-class-name` | *(auto-detected)* | Override SOAP client class name |
630
+ | `--gateway-decorator-name` | `{serviceSlug}Client` | Fastify decorator name for client instance |
631
+ | `--gateway-stub-handlers` | `false` | Generate stub handlers instead of full implementations |
632
+ | `--gateway-skip-plugin` | `false` | Skip generating `plugin.ts` wrapper |
633
+ | `--gateway-skip-runtime` | `false` | Skip generating `runtime.ts` utilities |
634
+
635
+ > **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
636
 
629
637
  ### Generated Output Structure
630
638
 
@@ -640,11 +648,13 @@ npx wsdl-tsc gateway \
640
648
  │ ├── <operation2>.json
641
649
  │ └── ...
642
650
  ├── routes/ # Individual route registration files
643
- │ ├── <route1>.ts
651
+ │ ├── <route1>.ts # Full handler implementations
644
652
  │ ├── <route2>.ts
645
653
  │ └── ...
646
654
  ├── schemas.ts # Schema registration module
647
- └── routes.ts # Route aggregator module
655
+ ├── routes.ts # Route aggregator module
656
+ ├── runtime.ts # Envelope builders and error handler
657
+ └── plugin.ts # Fastify plugin wrapper (recommended entry point)
648
658
  ```
649
659
 
650
660
  ### URN-Based Schema IDs
@@ -703,56 +713,156 @@ npx wsdl-tsc gateway \
703
713
 
704
714
  ### Integration Pattern
705
715
 
706
- The generated gateway scaffolding provides the foundation for your Fastify application:
716
+ The generated gateway provides a Fastify plugin for simplified integration.
717
+
718
+ #### Prerequisites
719
+
720
+ Your host application needs these dependencies:
721
+
722
+ ```bash
723
+ npm install fastify fastify-plugin
724
+ ```
725
+
726
+ #### Using the Generated Plugin (Recommended)
707
727
 
708
728
  ```typescript
709
729
  import Fastify from 'fastify';
710
- import { registerSchemas } from './gateway/schemas.js';
711
- import { registerRoutes } from './gateway/routes.js';
730
+ import weatherGateway from './gateway/plugin.js';
731
+ import { Weather } from './client/client.js';
712
732
 
713
733
  const app = Fastify({ logger: true });
714
734
 
715
- // Register JSON Schemas (validation setup)
716
- await registerSchemas(app);
735
+ // Create and configure SOAP client
736
+ const weatherClient = new Weather({
737
+ source: 'https://example.com/weather.wsdl',
738
+ // security: new soap.WSSecurity('user', 'pass'), // if needed
739
+ });
717
740
 
718
- // Register routes (with handler stubs)
719
- await registerRoutes(app, {
720
- prefix: '/api/v1',
721
- // Pass any route-level options
741
+ // Register gateway plugin with client
742
+ await app.register(weatherGateway, {
743
+ client: weatherClient,
744
+ // Note: Route paths are determined by --openapi-base-path during generation.
745
+ // The prefix option here adds an ADDITIONAL runtime prefix on top of generated paths.
746
+ // Only use if you need to mount routes under a different sub-path at runtime.
722
747
  });
723
748
 
724
749
  await app.listen({ port: 3000 });
725
750
  ```
726
751
 
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.
752
+ The plugin automatically:
753
+ - Decorates Fastify with the SOAP client (`fastify.weatherClient`)
754
+ - Registers all JSON schemas for validation
755
+ - Installs a centralized error handler
756
+ - Registers all routes with full handler implementations
757
+
758
+ #### Using Individual Components (Advanced)
759
+
760
+ For more control, you can use the individual modules:
761
+
762
+ ```typescript
763
+ import Fastify from 'fastify';
764
+ import { registerSchemas_v1_weather } from './gateway/schemas.js';
765
+ import { registerRoutes_v1_weather } from './gateway/routes.js';
766
+ import { createGatewayErrorHandler_v1_weather } from './gateway/runtime.js';
767
+ import { Weather } from './client/client.js';
768
+
769
+ const app = Fastify({ logger: true });
770
+
771
+ // Manual setup
772
+ const weatherClient = new Weather({ source: 'weather.wsdl' });
773
+ app.decorate('weatherClient', weatherClient);
774
+
775
+ // Register schemas
776
+ await registerSchemas_v1_weather(app);
777
+
778
+ // Install error handler
779
+ app.setErrorHandler(createGatewayErrorHandler_v1_weather());
780
+
781
+ // Register routes (paths already include --openapi-base-path if configured)
782
+ await registerRoutes_v1_weather(app);
728
783
 
729
- ### Handler Implementation
784
+ await app.listen({ port: 3000 });
785
+ ```
730
786
 
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.
787
+ ### Generated Handler Implementation
732
788
 
733
- Each generated route file contains handler stubs that you need to implement:
789
+ Route handlers are fully implemented and call the SOAP client automatically:
734
790
 
735
791
  ```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');
792
+ // Generated: routes/get-city-forecast-by-zip.ts
793
+ import type { FastifyInstance } from "fastify";
794
+ import schema from "../schemas/operations/getcityforecastbyzip.json" with { type: "json" };
795
+ import { buildSuccessEnvelope } from "../runtime.js";
796
+
797
+ export async function registerRoute_v1_weather_getcityforecastbyzip(fastify: FastifyInstance) {
798
+ fastify.route({
799
+ method: "POST",
800
+ url: "/get-city-forecast-by-zip",
801
+ schema,
802
+ handler: async (request) => {
803
+ const client = fastify.weatherClient;
804
+ const result = await client.GetCityForecastByZIP(request.body);
805
+ return buildSuccessEnvelope(result.response);
806
+ },
807
+ });
808
+ }
809
+ ```
810
+
811
+ ### Response Envelope
812
+
813
+ All responses are wrapped in a standard envelope format:
814
+
815
+ **Success Response:**
816
+ ```
817
+ {
818
+ "status": "SUCCESS",
819
+ "message": null,
820
+ "data": { /* SOAP response data */ },
821
+ "error": null
822
+ }
823
+ ```
824
+
825
+ **Error Response:**
826
+ ```
827
+ {
828
+ "status": "ERROR",
829
+ "message": "Request validation failed",
830
+ "data": null,
831
+ "error": {
832
+ "code": "VALIDATION_ERROR",
833
+ "message": "Request validation failed",
834
+ "details": { /* validation errors */ }
835
+ }
745
836
  }
746
837
  ```
747
838
 
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
839
+ ### Error Handling
840
+
841
+ The centralized error handler (`runtime.ts`) automatically classifies errors:
842
+
843
+ | Error Type | HTTP Status | Error Code |
844
+ |-----------------------|-------------|-----------------------|
845
+ | Validation errors | 400 | `VALIDATION_ERROR` |
846
+ | SOAP faults | 502 | `SOAP_FAULT` |
847
+ | Connection refused | 503 | `SERVICE_UNAVAILABLE` |
848
+ | Timeout | 504 | `GATEWAY_TIMEOUT` |
849
+ | Other errors | 500 | `INTERNAL_ERROR` |
850
+
851
+ ### Stub Handler Mode (Legacy)
852
+
853
+ For backward compatibility or manual handler implementation, use stub mode:
854
+
855
+ ```bash
856
+ npx wsdl-tsc gateway \
857
+ --openapi-file ./docs/weather-api.json \
858
+ --client-dir ./src/services/weather \
859
+ --gateway-dir ./src/gateway/weather \
860
+ --gateway-service-name weather \
861
+ --gateway-version-prefix v1 \
862
+ --gateway-stub-handlers
863
+ ```
754
864
 
755
- **Future enhancement**: Automated handler generation will include SOAP client calls, response transformation, error handling, and envelope wrapping.
865
+ This generates handler stubs that throw "Not implemented" errors, allowing you to implement custom logic.
756
866
 
757
867
  ---
758
868
 
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":"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;AAED;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,UAAU,CAmC3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,iBAAiB,CAAC,EAAE,KAAK,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC,GACD,qBAAqB,CAmBvB"}
@@ -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.0",
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"