@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 +21 -21
- package/README.md +145 -35
- 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/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
|
-
|
|
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
|
|
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
|
|
711
|
-
import {
|
|
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
|
-
//
|
|
716
|
-
|
|
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
|
|
719
|
-
await
|
|
720
|
-
|
|
721
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
784
|
+
await app.listen({ port: 3000 });
|
|
785
|
+
```
|
|
730
786
|
|
|
731
|
-
|
|
787
|
+
### Generated Handler Implementation
|
|
732
788
|
|
|
733
|
-
|
|
789
|
+
Route handlers are fully implemented and call the SOAP client automatically:
|
|
734
790
|
|
|
735
791
|
```typescript
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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
|
-
|
|
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);
|
|
@@ -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.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"
|