@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 +159 -51
- package/dist/cli.js +62 -0
- package/dist/compiler/schemaCompiler.d.ts +2 -0
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +4 -1
- package/dist/gateway/generateGateway.d.ts +12 -0
- package/dist/gateway/generateGateway.d.ts.map +1 -1
- package/dist/gateway/generateGateway.js +58 -7
- package/dist/gateway/generators.d.ts +66 -1
- package/dist/gateway/generators.d.ts.map +1 -1
- package/dist/gateway/generators.js +377 -0
- package/dist/gateway/helpers.d.ts +95 -0
- package/dist/gateway/helpers.d.ts.map +1 -1
- package/dist/gateway/helpers.js +84 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://github.com/techspokes)
|
|
10
10
|
[](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
|
|
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
|
|
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
|
|
110
|
-
|
|
111
|
-
| `compile`
|
|
112
|
-
| `client`
|
|
113
|
-
| `openapi`
|
|
114
|
-
| `gateway`
|
|
115
|
-
| `pipeline`
|
|
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
|
|
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
|
|
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
|
-
-
|
|
598
|
-
-
|
|
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/ #
|
|
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
|
-
|
|
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
|
|
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
|
|
711
|
-
import {
|
|
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
|
-
//
|
|
716
|
-
|
|
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
|
|
719
|
-
await
|
|
720
|
-
|
|
721
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
758
|
+
For more control, you can use the individual modules:
|
|
730
759
|
|
|
731
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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
|
-
**
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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
|
-
|
|
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);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AAIzD;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;QAC9B,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;QAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;QAC9B,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;QAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACJ,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;KAC/C,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,KAAK,CAAC;QACrB,aAAa,CAAC,EAAE,KAAK,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
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
|
-
|
|
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":"
|
|
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
|
|
131
|
-
// Step 3:
|
|
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
|
|
134
|
-
|
|
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;
|
|
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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"}
|
package/dist/gateway/helpers.js
CHANGED
|
@@ -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.
|
|
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"
|