@techspokes/typescript-wsdl-client 0.10.4 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/cli.js +18 -1
- package/dist/client/generateOperations.d.ts +13 -0
- package/dist/client/generateOperations.d.ts.map +1 -0
- package/dist/client/generateOperations.js +71 -0
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +15 -1
- package/dist/gateway/generateGateway.d.ts +1 -0
- package/dist/gateway/generateGateway.d.ts.map +1 -1
- package/dist/gateway/generateGateway.js +4 -2
- package/dist/gateway/generators.d.ts +2 -15
- package/dist/gateway/generators.d.ts.map +1 -1
- package/dist/gateway/generators.js +111 -27
- package/dist/gateway/helpers.d.ts +4 -2
- package/dist/gateway/helpers.d.ts.map +1 -1
- package/dist/gateway/helpers.js +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/loader/wsdlLoader.d.ts.map +1 -1
- package/dist/loader/wsdlLoader.js +30 -4
- package/dist/openapi/generateOpenAPI.d.ts +1 -0
- package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
- package/dist/openapi/generateOpenAPI.js +1 -0
- package/dist/openapi/generateSchemas.d.ts +1 -0
- package/dist/openapi/generateSchemas.d.ts.map +1 -1
- package/dist/openapi/generateSchemas.js +4 -3
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +6 -1
- package/dist/util/builder.d.ts.map +1 -1
- package/dist/util/builder.js +1 -0
- package/dist/util/cli.d.ts +3 -2
- package/dist/util/cli.d.ts.map +1 -1
- package/dist/util/cli.js +14 -4
- package/dist/util/errors.d.ts +37 -0
- package/dist/util/errors.d.ts.map +1 -0
- package/dist/util/errors.js +37 -0
- package/docs/README.md +1 -0
- package/docs/cli-reference.md +2 -0
- package/docs/concepts.md +29 -2
- package/docs/generated-code.md +24 -0
- package/docs/testing.md +193 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -62,6 +62,31 @@ curl -X POST http://localhost:3000/get-weather-information \
|
|
|
62
62
|
| Fastify Gateway | plugin.ts, routes/, schemas/ | Production REST handlers |
|
|
63
63
|
| Catalog | catalog.json | Compiled WSDL (debuggable, cacheable) |
|
|
64
64
|
|
|
65
|
+
## Testing With Generated Code
|
|
66
|
+
|
|
67
|
+
The generated `operations.ts` provides a typed interface for mocking the SOAP client without importing the concrete class or the `soap` package:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import type { WeatherOperations } from "./generated/client/operations.js";
|
|
71
|
+
|
|
72
|
+
const mockClient: WeatherOperations = {
|
|
73
|
+
GetCityWeatherByZIP: async (args) => ({
|
|
74
|
+
response: { GetCityWeatherByZIPResult: { Success: true, City: "Test" } },
|
|
75
|
+
headers: {},
|
|
76
|
+
}),
|
|
77
|
+
// ... other operations
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Use with the gateway plugin
|
|
81
|
+
import Fastify from "fastify";
|
|
82
|
+
import { weatherGateway } from "./generated/gateway/plugin.js";
|
|
83
|
+
|
|
84
|
+
const app = Fastify();
|
|
85
|
+
await app.register(weatherGateway, { client: mockClient, prefix: "/v1/weather" });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
See [Testing Guide](docs/testing.md) for integration test patterns and mock examples.
|
|
89
|
+
|
|
65
90
|
## Commands
|
|
66
91
|
|
|
67
92
|
| Command | Purpose |
|
|
@@ -85,6 +110,7 @@ See [CLI Reference](docs/cli-reference.md) for all flags and examples.
|
|
|
85
110
|
| [Gateway Guide](docs/gateway-guide.md) | Fastify integration and error handling |
|
|
86
111
|
| [Configuration](docs/configuration.md) | Security, tags, operations config files |
|
|
87
112
|
| [Production Guide](docs/production.md) | CI/CD, validation, logging, limitations |
|
|
113
|
+
| [Testing Guide](docs/testing.md) | Testing patterns and mock client examples |
|
|
88
114
|
| [Troubleshooting](docs/troubleshooting.md) | Common issues and debugging |
|
|
89
115
|
| [Working With Generated Code](docs/generated-code.md) | Using clients and types |
|
|
90
116
|
| [Architecture](docs/architecture.md) | Internal pipeline for contributors |
|
package/dist/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ import { generateTypes } from "./client/generateTypes.js";
|
|
|
19
19
|
import { generateUtils } from "./client/generateUtils.js";
|
|
20
20
|
import { generateCatalog } from "./compiler/generateCatalog.js";
|
|
21
21
|
import { generateClient } from "./client/generateClient.js";
|
|
22
|
+
import { generateOperations } from "./client/generateOperations.js";
|
|
22
23
|
import { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
23
24
|
import { runGenerationPipeline } from "./pipeline.js";
|
|
24
25
|
import { resolveCompilerOptions } from "./config.js";
|
|
@@ -183,7 +184,7 @@ if (rawArgs[0] === "client") {
|
|
|
183
184
|
success(`Compiled catalog written to ${catalogOutPath}`);
|
|
184
185
|
}
|
|
185
186
|
// Emit client artifacts (excluding catalog since we already emitted it above if needed)
|
|
186
|
-
emitClientArtifacts(clientOutDir, compiled, generateClient, generateTypes, generateUtils);
|
|
187
|
+
emitClientArtifacts(clientOutDir, compiled, generateClient, generateTypes, generateUtils, generateOperations);
|
|
187
188
|
process.exit(0);
|
|
188
189
|
}
|
|
189
190
|
/**
|
|
@@ -237,6 +238,11 @@ if (rawArgs[0] === "openapi") {
|
|
|
237
238
|
type: "boolean",
|
|
238
239
|
default: false,
|
|
239
240
|
desc: "Emit additionalProperties:false for object schemas"
|
|
241
|
+
})
|
|
242
|
+
.option("openapi-flatten-array-wrappers", {
|
|
243
|
+
type: "boolean",
|
|
244
|
+
default: true,
|
|
245
|
+
desc: "Flatten ArrayOf* wrapper types to plain arrays in schemas (default: true)"
|
|
240
246
|
})
|
|
241
247
|
.option("openapi-prune-unused-schemas", {
|
|
242
248
|
type: "boolean",
|
|
@@ -357,6 +363,11 @@ if (rawArgs[0] === "gateway") {
|
|
|
357
363
|
type: "boolean",
|
|
358
364
|
default: false,
|
|
359
365
|
desc: "Skip generating runtime.ts utilities"
|
|
366
|
+
})
|
|
367
|
+
.option("openapi-flatten-array-wrappers", {
|
|
368
|
+
type: "boolean",
|
|
369
|
+
default: true,
|
|
370
|
+
desc: "Generate runtime unwrap for ArrayOf* wrapper types (default: true)"
|
|
360
371
|
})
|
|
361
372
|
.strict()
|
|
362
373
|
.help()
|
|
@@ -388,6 +399,7 @@ if (rawArgs[0] === "gateway") {
|
|
|
388
399
|
// Only override defaults if explicitly skipping (otherwise let generateGateway decide based on stubHandlers)
|
|
389
400
|
emitPlugin: gatewayArgv["gateway-skip-plugin"] ? false : undefined,
|
|
390
401
|
emitRuntime: gatewayArgv["gateway-skip-runtime"] ? false : undefined,
|
|
402
|
+
flattenArrayWrappers: gatewayArgv["openapi-flatten-array-wrappers"],
|
|
391
403
|
});
|
|
392
404
|
success(`Gateway code generated in ${outDir}`);
|
|
393
405
|
process.exit(0);
|
|
@@ -569,6 +581,11 @@ if (rawArgs[0] === "pipeline") {
|
|
|
569
581
|
.option("openapi-tags-file", { type: "string" })
|
|
570
582
|
.option("openapi-ops-file", { type: "string" })
|
|
571
583
|
.option("openapi-closed-schemas", { type: "boolean", default: false })
|
|
584
|
+
.option("openapi-flatten-array-wrappers", {
|
|
585
|
+
type: "boolean",
|
|
586
|
+
default: true,
|
|
587
|
+
desc: "Flatten ArrayOf* wrapper types to plain arrays in schemas (default: true)"
|
|
588
|
+
})
|
|
572
589
|
.option("openapi-prune-unused-schemas", { type: "boolean", default: false })
|
|
573
590
|
.option("openapi-envelope-namespace", { type: "string" })
|
|
574
591
|
.option("openapi-error-namespace", { type: "string" })
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CompiledCatalog } from "../compiler/schemaCompiler.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generates an operations.ts file with a fully-typed interface for all SOAP operations
|
|
4
|
+
*
|
|
5
|
+
* The interface mirrors the async method signatures of the generated client class
|
|
6
|
+
* but uses concrete input/output types from types.ts instead of generic parameters.
|
|
7
|
+
* This enables type-safe mocking without SOAP runtime dependencies.
|
|
8
|
+
*
|
|
9
|
+
* @param outFile - Path to the output TypeScript file
|
|
10
|
+
* @param compiled - The compiled WSDL catalog
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateOperations(outFile: string, compiled: CompiledCatalog): void;
|
|
13
|
+
//# sourceMappingURL=generateOperations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateOperations.d.ts","sourceRoot":"","sources":["../../src/client/generateOperations.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIrE;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CA0DnF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operations Interface Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates a fully-typed operations interface for the SOAP client.
|
|
5
|
+
* This standalone interface enables mocking and testing without importing
|
|
6
|
+
* the concrete SOAP client class or its runtime dependencies.
|
|
7
|
+
*/
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import { deriveClientName, pascal } from "../util/tools.js";
|
|
10
|
+
import { error } from "../util/cli.js";
|
|
11
|
+
/**
|
|
12
|
+
* Generates an operations.ts file with a fully-typed interface for all SOAP operations
|
|
13
|
+
*
|
|
14
|
+
* The interface mirrors the async method signatures of the generated client class
|
|
15
|
+
* but uses concrete input/output types from types.ts instead of generic parameters.
|
|
16
|
+
* This enables type-safe mocking without SOAP runtime dependencies.
|
|
17
|
+
*
|
|
18
|
+
* @param outFile - Path to the output TypeScript file
|
|
19
|
+
* @param compiled - The compiled WSDL catalog
|
|
20
|
+
*/
|
|
21
|
+
export function generateOperations(outFile, compiled) {
|
|
22
|
+
const ext = compiled.options.imports ?? "bare";
|
|
23
|
+
const suffix = ext === "bare" ? "" : `.${ext}`;
|
|
24
|
+
const clientName = deriveClientName(compiled);
|
|
25
|
+
// Collect type names used in method signatures for the import statement
|
|
26
|
+
const importedTypes = new Set();
|
|
27
|
+
const methods = [];
|
|
28
|
+
for (const op of compiled.operations) {
|
|
29
|
+
const inTypeName = op.inputElement ? pascal(op.inputElement.local) : undefined;
|
|
30
|
+
const outTypeName = op.outputElement ? pascal(op.outputElement.local) : undefined;
|
|
31
|
+
if (!inTypeName && !outTypeName) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const inTs = inTypeName ?? "Record<string, unknown>";
|
|
35
|
+
const outTs = outTypeName ?? "unknown";
|
|
36
|
+
if (inTypeName)
|
|
37
|
+
importedTypes.add(inTypeName);
|
|
38
|
+
if (outTypeName)
|
|
39
|
+
importedTypes.add(outTypeName);
|
|
40
|
+
methods.push(` ${op.name}(\n` +
|
|
41
|
+
` args: ${inTs}\n` +
|
|
42
|
+
` ): Promise<{ response: ${outTs}; headers: unknown }>;\n`);
|
|
43
|
+
}
|
|
44
|
+
// Build sorted import list for deterministic output
|
|
45
|
+
const sortedImports = Array.from(importedTypes).sort();
|
|
46
|
+
const typeImport = sortedImports.length > 0
|
|
47
|
+
? `import type {\n${sortedImports.map((t) => ` ${t},`).join("\n")}\n} from "./types${suffix}";\n\n`
|
|
48
|
+
: "";
|
|
49
|
+
const content = `/**
|
|
50
|
+
* Typed operations interface for the ${clientName} service.
|
|
51
|
+
*
|
|
52
|
+
* Implement this interface to create mock clients or alternative
|
|
53
|
+
* transport layers without depending on the SOAP runtime.
|
|
54
|
+
*
|
|
55
|
+
* Auto-generated - do not edit manually.
|
|
56
|
+
*/
|
|
57
|
+
${typeImport}/**
|
|
58
|
+
* All operations exposed by the ${clientName} SOAP service.
|
|
59
|
+
*
|
|
60
|
+
* The concrete ${clientName} class satisfies this interface.
|
|
61
|
+
* Use this type for dependency injection, mocking, or testing.
|
|
62
|
+
*/
|
|
63
|
+
export interface ${clientName}Operations {
|
|
64
|
+
${methods.join("\n")}}\n`;
|
|
65
|
+
try {
|
|
66
|
+
fs.writeFileSync(outFile, content, "utf8");
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
error(`Failed to write operations interface to ${outFile}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getChildrenWithLocalName, getFirstWithLocalName, normalizeArray, pascal, resolveQName, } from "../util/tools.js";
|
|
2
2
|
import { xsdToTsPrimitive } from "../xsd/primitives.js";
|
|
3
|
+
import { WsdlCompilationError } from "../util/errors.js";
|
|
3
4
|
// XML Schema namespace constant
|
|
4
5
|
const XS = "http://www.w3.org/2001/XMLSchema";
|
|
5
6
|
/**
|
|
@@ -194,7 +195,14 @@ export function compileCatalog(cat, options) {
|
|
|
194
195
|
const t = getOrCompileComplex(q.local, crec.node, crec.tns, crec.prefixes);
|
|
195
196
|
return { tsType: t.name, declared: `{${t.ns}}${q.local}` };
|
|
196
197
|
}
|
|
197
|
-
//
|
|
198
|
+
// Unresolved type reference
|
|
199
|
+
if (options.failOnUnresolved) {
|
|
200
|
+
throw new WsdlCompilationError(`Unresolved type reference: "${q.local}" in namespace "${q.ns}".`, {
|
|
201
|
+
element: q.local,
|
|
202
|
+
namespace: q.ns,
|
|
203
|
+
suggestion: "Check that the XSD import for this namespace is included in the WSDL, or use --no-fail-on-unresolved to emit 'any'.",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
198
206
|
return { tsType: "any", declared: `{${q.ns}}${q.local}` };
|
|
199
207
|
}
|
|
200
208
|
function findComplexRec(q) {
|
|
@@ -565,6 +573,12 @@ export function compileCatalog(cat, options) {
|
|
|
565
573
|
const tns = defs?.["@_targetNamespace"] || "";
|
|
566
574
|
const bindingDefs = normalizeArray(defs?.["wsdl:binding"] || defs?.["binding"]);
|
|
567
575
|
const soapBinding = bindingDefs.find(b => Object.keys(b).some(k => k === "soap:binding" || k === "soap12:binding")) || bindingDefs[0];
|
|
576
|
+
if (!soapBinding) {
|
|
577
|
+
throw new WsdlCompilationError("No SOAP binding found in the WSDL document.", {
|
|
578
|
+
file: cat.wsdlUri,
|
|
579
|
+
suggestion: "Ensure the WSDL defines a <wsdl:binding> element with a <soap:binding> or <soap12:binding> child.",
|
|
580
|
+
});
|
|
581
|
+
}
|
|
568
582
|
// binding @type typically looks like "tns:MyPortType", so resolve via prefixes map
|
|
569
583
|
const portTypeAttr = soapBinding?.["@_type"];
|
|
570
584
|
const portTypeQName = portTypeAttr ? resolveQName(portTypeAttr, tns, cat.prefixMap) : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;;;;;;;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;
|
|
1
|
+
{"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;;;;;;;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;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyJjF"}
|
|
@@ -165,6 +165,8 @@ export async function generateGateway(opts) {
|
|
|
165
165
|
});
|
|
166
166
|
// Step 4: Emit schemas.ts module
|
|
167
167
|
emitSchemasModule(outDir, modelsDir, versionSlug, serviceSlug);
|
|
168
|
+
// Determine whether to generate unwrap code for ArrayOf* wrappers
|
|
169
|
+
const shouldUnwrap = opts.flattenArrayWrappers !== false && catalog;
|
|
168
170
|
// Step 5: Emit route files (with handlers or stubs)
|
|
169
171
|
// Note: Route URLs come from OpenAPI paths which already include any base path
|
|
170
172
|
if (stubHandlers) {
|
|
@@ -173,11 +175,11 @@ export async function generateGateway(opts) {
|
|
|
173
175
|
}
|
|
174
176
|
else {
|
|
175
177
|
// Full handler mode: emit working implementations
|
|
176
|
-
emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode, clientMeta);
|
|
178
|
+
emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode, clientMeta, shouldUnwrap ? catalog : undefined);
|
|
177
179
|
}
|
|
178
180
|
// Step 6: Emit runtime.ts (if enabled)
|
|
179
181
|
if (emitRuntime) {
|
|
180
|
-
emitRuntimeModule(outDir, versionSlug, serviceSlug);
|
|
182
|
+
emitRuntimeModule(outDir, versionSlug, serviceSlug, shouldUnwrap ? catalog : undefined);
|
|
181
183
|
}
|
|
182
184
|
// Step 7: Emit plugin.ts and type-check fixture (if enabled)
|
|
183
185
|
if (emitPlugin) {
|
|
@@ -103,20 +103,7 @@ export declare function emitSchemasModule(outDir: string, modelsDir: string, ver
|
|
|
103
103
|
* @param {"js"|"ts"|"bare"} importsMode - Import-extension mode for generated TypeScript modules
|
|
104
104
|
*/
|
|
105
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;
|
|
106
|
+
export declare function emitRuntimeModule(outDir: string, versionSlug: string, serviceSlug: string, catalog?: any): void;
|
|
120
107
|
/**
|
|
121
108
|
* Emits plugin.ts module as the primary Fastify plugin wrapper
|
|
122
109
|
*
|
|
@@ -165,5 +152,5 @@ export declare function emitTypeCheckFixture(outDir: string, clientMeta: ClientM
|
|
|
165
152
|
* @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
|
|
166
153
|
* @param {ClientMeta} clientMeta - Client class metadata
|
|
167
154
|
*/
|
|
168
|
-
export declare function emitRouteFilesWithHandlers(outDir: string, routesDir: string, versionSlug: string, serviceSlug: string, operations: OperationMetadata[], importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta): void;
|
|
155
|
+
export declare function emitRouteFilesWithHandlers(outDir: string, routesDir: string, versionSlug: string, serviceSlug: string, operations: OperationMetadata[], importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta, catalog?: any): void;
|
|
169
156
|
//# 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,UAAU,EAAE,KAAK,eAAe,EAA6C,MAAM,cAAc,CAAC;AAE/G;;;;;;;;;;;;;;;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,CA6BxB;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;
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"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"}
|
|
@@ -288,9 +288,88 @@ export function emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, oper
|
|
|
288
288
|
* @param {string} versionSlug - Version slug for function naming
|
|
289
289
|
* @param {string} serviceSlug - Service slug for function naming
|
|
290
290
|
*/
|
|
291
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Detects ArrayOf* wrapper types from catalog type metadata.
|
|
293
|
+
*
|
|
294
|
+
* An ArrayOf* wrapper has exactly one element with max "unbounded" (or > 1)
|
|
295
|
+
* and no attributes — mirroring the logic in generateSchemas.ts isArrayWrapper().
|
|
296
|
+
*
|
|
297
|
+
* @param catalog - Compiled catalog object
|
|
298
|
+
* @returns Record mapping wrapper type name to inner element property name
|
|
299
|
+
*/
|
|
300
|
+
function detectArrayWrappers(catalog) {
|
|
301
|
+
const wrappers = {};
|
|
302
|
+
for (const t of catalog.types || []) {
|
|
303
|
+
if (t.attrs && t.attrs.length !== 0)
|
|
304
|
+
continue;
|
|
305
|
+
if (!t.elems || t.elems.length !== 1)
|
|
306
|
+
continue;
|
|
307
|
+
const e = t.elems[0];
|
|
308
|
+
if (e.max !== "unbounded" && !(e.max > 1))
|
|
309
|
+
continue;
|
|
310
|
+
wrappers[t.name] = e.name;
|
|
311
|
+
}
|
|
312
|
+
return wrappers;
|
|
313
|
+
}
|
|
314
|
+
export function emitRuntimeModule(outDir, versionSlug, serviceSlug, catalog) {
|
|
292
315
|
const vSlug = slugName(versionSlug);
|
|
293
316
|
const sSlug = slugName(serviceSlug);
|
|
317
|
+
// Build unwrap maps from catalog if provided
|
|
318
|
+
let unwrapSection = "";
|
|
319
|
+
if (catalog) {
|
|
320
|
+
const arrayWrappers = detectArrayWrappers(catalog);
|
|
321
|
+
const childTypes = catalog.meta?.childType || {};
|
|
322
|
+
// Only emit if there are actual wrapper types to unwrap
|
|
323
|
+
if (Object.keys(arrayWrappers).length > 0) {
|
|
324
|
+
unwrapSection = `
|
|
325
|
+
/**
|
|
326
|
+
* ArrayOf* wrapper type → inner element property name.
|
|
327
|
+
* These types are flattened to plain arrays in the OpenAPI schema,
|
|
328
|
+
* so the runtime must unwrap { InnerElement: [...] } → [...].
|
|
329
|
+
*/
|
|
330
|
+
const ARRAY_WRAPPERS: Record<string, string> = ${JSON.stringify(arrayWrappers, null, 2)};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Type name → { propertyName: propertyTypeName } for recursive unwrapping.
|
|
334
|
+
*/
|
|
335
|
+
const CHILDREN_TYPES: Record<string, Record<string, string>> = ${JSON.stringify(childTypes, null, 2)};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Recursively unwraps ArrayOf* wrapper objects in a SOAP response so the
|
|
339
|
+
* data matches the flattened OpenAPI array schemas.
|
|
340
|
+
*
|
|
341
|
+
* Safe to call on any response — returns data unchanged when the type
|
|
342
|
+
* has no wrapper fields.
|
|
343
|
+
*
|
|
344
|
+
* @param data - SOAP response data (potentially with wrapper objects)
|
|
345
|
+
* @param typeName - The type name for the current data level
|
|
346
|
+
* @returns Unwrapped data matching the OpenAPI schema shape
|
|
347
|
+
*/
|
|
348
|
+
export function unwrapArrayWrappers(data: unknown, typeName: string): unknown {
|
|
349
|
+
if (data == null || typeof data !== "object") return data;
|
|
350
|
+
|
|
351
|
+
// If this type is itself a wrapper, unwrap it
|
|
352
|
+
if (typeName in ARRAY_WRAPPERS) {
|
|
353
|
+
const innerKey = ARRAY_WRAPPERS[typeName];
|
|
354
|
+
return (data as Record<string, unknown>)[innerKey] ?? [];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Recurse into children whose types may contain wrappers
|
|
358
|
+
if (typeName in CHILDREN_TYPES) {
|
|
359
|
+
const children = CHILDREN_TYPES[typeName];
|
|
360
|
+
for (const [propName, propType] of Object.entries(children)) {
|
|
361
|
+
const val = (data as Record<string, unknown>)[propName];
|
|
362
|
+
if (val !== undefined) {
|
|
363
|
+
(data as Record<string, unknown>)[propName] = unwrapArrayWrappers(val, propType);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return data;
|
|
369
|
+
}
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
294
373
|
const runtimeTs = `/**
|
|
295
374
|
* Gateway Runtime Utilities
|
|
296
375
|
*
|
|
@@ -463,7 +542,7 @@ export function createGatewayErrorHandler_${vSlug}_${sSlug}() {
|
|
|
463
542
|
};
|
|
464
543
|
}
|
|
465
544
|
`;
|
|
466
|
-
fs.writeFileSync(path.join(outDir, "runtime.ts"), runtimeTs, "utf8");
|
|
545
|
+
fs.writeFileSync(path.join(outDir, "runtime.ts"), runtimeTs + unwrapSection, "utf8");
|
|
467
546
|
}
|
|
468
547
|
/**
|
|
469
548
|
* Emits plugin.ts module as the primary Fastify plugin wrapper
|
|
@@ -485,11 +564,6 @@ export function emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, i
|
|
|
485
564
|
const vSlug = slugName(versionSlug);
|
|
486
565
|
const sSlug = slugName(serviceSlug);
|
|
487
566
|
const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
|
|
488
|
-
// Build named operations interface methods from operation metadata
|
|
489
|
-
const operationMethods = operations
|
|
490
|
-
.filter(op => op.clientMethodName)
|
|
491
|
-
.map(op => ` ${op.clientMethodName}(args: unknown): Promise<{ response: unknown; headers: unknown }>;`)
|
|
492
|
-
.join("\n");
|
|
493
567
|
const pluginTs = `/**
|
|
494
568
|
* ${clientMeta.className} Gateway Plugin
|
|
495
569
|
*
|
|
@@ -499,10 +573,14 @@ export function emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, i
|
|
|
499
573
|
import fp from "fastify-plugin";
|
|
500
574
|
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
|
|
501
575
|
import type { ${clientMeta.className} } from "${clientMeta.pluginImportPath}";
|
|
576
|
+
import type { ${clientMeta.className}Operations } from "${clientMeta.operationsImportPath}";
|
|
502
577
|
import { registerSchemas_${vSlug}_${sSlug} } from "./schemas${suffix}";
|
|
503
578
|
import { registerRoutes_${vSlug}_${sSlug} } from "./routes${suffix}";
|
|
504
579
|
import { createGatewayErrorHandler_${vSlug}_${sSlug} } from "./runtime${suffix}";
|
|
505
580
|
|
|
581
|
+
// Re-export the operations interface for convenience
|
|
582
|
+
export type { ${clientMeta.className}Operations };
|
|
583
|
+
|
|
506
584
|
/**
|
|
507
585
|
* Options for the ${clientMeta.className} gateway plugin
|
|
508
586
|
*/
|
|
@@ -510,8 +588,9 @@ export interface ${clientMeta.className}GatewayOptions extends FastifyPluginOpti
|
|
|
510
588
|
/**
|
|
511
589
|
* SOAP client instance (pre-configured).
|
|
512
590
|
* The client should be instantiated with appropriate source and security settings.
|
|
591
|
+
* Accepts the concrete client class or any implementation of ${clientMeta.className}Operations.
|
|
513
592
|
*/
|
|
514
|
-
client: ${clientMeta.className};
|
|
593
|
+
client: ${clientMeta.className}Operations;
|
|
515
594
|
/**
|
|
516
595
|
* Optional additional route prefix applied at runtime.
|
|
517
596
|
* Note: If you used --openapi-base-path during generation, routes already have that prefix baked in.
|
|
@@ -520,21 +599,9 @@ export interface ${clientMeta.className}GatewayOptions extends FastifyPluginOpti
|
|
|
520
599
|
prefix?: string;
|
|
521
600
|
}
|
|
522
601
|
|
|
523
|
-
/**
|
|
524
|
-
* Convenience type listing all SOAP operations with \`args: unknown\` signatures.
|
|
525
|
-
*
|
|
526
|
-
* This interface is NOT used for the decorator type (which uses the concrete
|
|
527
|
-
* client class for full type safety). It is exported as a lightweight alternative
|
|
528
|
-
* for mocking or testing scenarios where importing the full client class is
|
|
529
|
-
* undesirable.
|
|
530
|
-
*/
|
|
531
|
-
export interface ${clientMeta.className}Operations {
|
|
532
|
-
${operationMethods}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
602
|
declare module "fastify" {
|
|
536
603
|
interface FastifyInstance {
|
|
537
|
-
${clientMeta.decoratorName}: ${clientMeta.className};
|
|
604
|
+
${clientMeta.decoratorName}: ${clientMeta.className}Operations;
|
|
538
605
|
}
|
|
539
606
|
}
|
|
540
607
|
|
|
@@ -598,11 +665,19 @@ export function emitTypeCheckFixture(outDir, clientMeta, importsMode) {
|
|
|
598
665
|
* Auto-generated. Not intended for runtime use.
|
|
599
666
|
*/
|
|
600
667
|
import type { ${clientMeta.className} } from "${clientMeta.pluginImportPath}";
|
|
668
|
+
import type { ${clientMeta.className}Operations } from "${clientMeta.operationsImportPath}";
|
|
601
669
|
import type { ${clientMeta.className}GatewayOptions } from "./plugin${suffix}";
|
|
602
670
|
|
|
603
|
-
//
|
|
604
|
-
// If the
|
|
605
|
-
// will produce a compile error
|
|
671
|
+
// Verify the concrete client class satisfies the operations interface.
|
|
672
|
+
// If the client class diverges from the operations interface, this
|
|
673
|
+
// will produce a compile error.
|
|
674
|
+
function _assertClientSatisfiesOps(client: ${clientMeta.className}): ${clientMeta.className}Operations {
|
|
675
|
+
return client;
|
|
676
|
+
}
|
|
677
|
+
void _assertClientSatisfiesOps;
|
|
678
|
+
|
|
679
|
+
// Verify the concrete client class is accepted by plugin options.
|
|
680
|
+
// This ensures the gateway plugin can be used with the generated client.
|
|
606
681
|
function _assertClientCompatible(client: ${clientMeta.className}): void {
|
|
607
682
|
const _opts: ${clientMeta.className}GatewayOptions = { client };
|
|
608
683
|
void _opts;
|
|
@@ -630,13 +705,15 @@ void _assertClientCompatible;
|
|
|
630
705
|
* @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
|
|
631
706
|
* @param {ClientMeta} clientMeta - Client class metadata
|
|
632
707
|
*/
|
|
633
|
-
export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode, clientMeta) {
|
|
708
|
+
export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode, clientMeta, catalog) {
|
|
634
709
|
fs.mkdirSync(routesDir, { recursive: true });
|
|
635
710
|
// Sort operations for deterministic output
|
|
636
711
|
operations.sort((a, b) => a.operationSlug.localeCompare(b.operationSlug));
|
|
637
712
|
const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
|
|
638
713
|
const vSlug = slugName(versionSlug);
|
|
639
714
|
const sSlug = slugName(serviceSlug);
|
|
715
|
+
// Detect if unwrap code should be emitted
|
|
716
|
+
const hasUnwrap = catalog ? Object.keys(detectArrayWrappers(catalog)).length > 0 : false;
|
|
640
717
|
let routesTs = `import type { FastifyInstance } from "fastify";\n`;
|
|
641
718
|
operations.forEach((op) => {
|
|
642
719
|
const fnName = `registerRoute_${vSlug}_${sSlug}_${op.operationSlug.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
@@ -661,6 +738,13 @@ export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, servi
|
|
|
661
738
|
const bodyArg = hasRequestType
|
|
662
739
|
? `request.body as ${reqTypeName}`
|
|
663
740
|
: "request.body";
|
|
741
|
+
// Build the runtime import and return expression based on unwrap availability
|
|
742
|
+
const runtimeImport = hasUnwrap
|
|
743
|
+
? `import { buildSuccessEnvelope, unwrapArrayWrappers } from "../runtime${suffix}";`
|
|
744
|
+
: `import { buildSuccessEnvelope } from "../runtime${suffix}";`;
|
|
745
|
+
const returnExpr = hasUnwrap
|
|
746
|
+
? `return buildSuccessEnvelope(unwrapArrayWrappers(result.response, "${resTypeName}"));`
|
|
747
|
+
: `return buildSuccessEnvelope(result.response);`;
|
|
664
748
|
// Note: op.path comes from OpenAPI and already includes any base path
|
|
665
749
|
let routeTs = `/**
|
|
666
750
|
* Route: ${op.method.toUpperCase()} ${op.path}
|
|
@@ -671,7 +755,7 @@ export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, servi
|
|
|
671
755
|
*/
|
|
672
756
|
import type { FastifyInstance } from "fastify";
|
|
673
757
|
${typeImport}import schema from "../schemas/operations/${op.operationSlug}.json" with { type: "json" };
|
|
674
|
-
|
|
758
|
+
${runtimeImport}
|
|
675
759
|
|
|
676
760
|
export async function ${fnName}(fastify: FastifyInstance) {
|
|
677
761
|
fastify.route${routeGeneric}({
|
|
@@ -681,7 +765,7 @@ export async function ${fnName}(fastify: FastifyInstance) {
|
|
|
681
765
|
handler: async (request) => {
|
|
682
766
|
const client = fastify.${clientMeta.decoratorName};
|
|
683
767
|
const result = await client.${clientMethod}(${bodyArg});
|
|
684
|
-
|
|
768
|
+
${returnExpr}
|
|
685
769
|
},
|
|
686
770
|
});
|
|
687
771
|
}
|
|
@@ -141,9 +141,10 @@ export declare function buildParamSchemasForOperation(pathItem: any, operation:
|
|
|
141
141
|
* @interface ClientMeta
|
|
142
142
|
* @property {string} className - Client class name (e.g., "EVRNService")
|
|
143
143
|
* @property {string} decoratorName - Fastify decorator name (e.g., "evrnserviceClient")
|
|
144
|
-
* @property {string} importPath - Import path relative to routes/ directory — for
|
|
145
|
-
* @property {string} typesImportPath - Types import path relative to routes/ directory — for
|
|
144
|
+
* @property {string} importPath - Import path relative to routes/ directory — for typed route handlers
|
|
145
|
+
* @property {string} typesImportPath - Types import path relative to routes/ directory — for typed route handlers
|
|
146
146
|
* @property {string} pluginImportPath - Import path relative to gateway output directory — used by emitPluginModule()
|
|
147
|
+
* @property {string} operationsImportPath - Operations interface import path relative to gateway output directory
|
|
147
148
|
*/
|
|
148
149
|
export interface ClientMeta {
|
|
149
150
|
className: string;
|
|
@@ -151,6 +152,7 @@ export interface ClientMeta {
|
|
|
151
152
|
importPath: string;
|
|
152
153
|
typesImportPath: string;
|
|
153
154
|
pluginImportPath: string;
|
|
155
|
+
operationsImportPath: string;
|
|
154
156
|
}
|
|
155
157
|
/**
|
|
156
158
|
* Extended operation metadata for full handler generation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;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;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,GAAG,EACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,GAC/B,GAAG,CA4GL;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAcxD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,GAAG,GAAG,CAiB3E;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,aAAa,CAAC,EAAE,GAAG,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,GAAG,EACb,SAAS,EAAE,GAAG,EACd,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,sBAAsB,CAmFxB;AAED;;;;GAIG;AAEH
|
|
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
|
@@ -390,12 +390,16 @@ export function resolveClientMeta(opts, catalog) {
|
|
|
390
390
|
const pluginImportPath = opts.clientDir
|
|
391
391
|
? `../client/client${suffix}`
|
|
392
392
|
: `./client/client${suffix}`;
|
|
393
|
+
const operationsImportPath = opts.clientDir
|
|
394
|
+
? `../client/operations${suffix}`
|
|
395
|
+
: `./client/operations${suffix}`;
|
|
393
396
|
return {
|
|
394
397
|
className,
|
|
395
398
|
decoratorName,
|
|
396
399
|
importPath,
|
|
397
400
|
typesImportPath,
|
|
398
401
|
pluginImportPath,
|
|
402
|
+
operationsImportPath,
|
|
399
403
|
};
|
|
400
404
|
}
|
|
401
405
|
/**
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAWjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GACjE,OAAO,CAAC,IAAI,CAAC,CAwDf"}
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { generateTypes } from "./client/generateTypes.js";
|
|
|
20
20
|
import { generateUtils } from "./client/generateUtils.js";
|
|
21
21
|
import { generateCatalog } from "./compiler/generateCatalog.js";
|
|
22
22
|
import { generateClient } from "./client/generateClient.js";
|
|
23
|
+
import { generateOperations } from "./client/generateOperations.js";
|
|
23
24
|
import { info } from "./util/cli.js";
|
|
24
25
|
export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
25
26
|
export { generateGateway } from "./gateway/generateGateway.js";
|
|
@@ -76,6 +77,7 @@ export async function compileWsdlToProject(input) {
|
|
|
76
77
|
// Emit artifacts
|
|
77
78
|
const typesFile = path.join(input.outDir, "types.ts");
|
|
78
79
|
const utilsFile = path.join(input.outDir, "utils.ts");
|
|
80
|
+
const operationsFile = path.join(input.outDir, "operations.ts");
|
|
79
81
|
const catalogFile = path.join(input.outDir, "catalog.json");
|
|
80
82
|
const clientFile = path.join(input.outDir, "client.ts");
|
|
81
83
|
// Prepare output dir
|
|
@@ -89,6 +91,7 @@ export async function compileWsdlToProject(input) {
|
|
|
89
91
|
generateClient(clientFile, compiled);
|
|
90
92
|
generateTypes(typesFile, compiled);
|
|
91
93
|
generateUtils(utilsFile, compiled);
|
|
94
|
+
generateOperations(operationsFile, compiled);
|
|
92
95
|
if (compiled.options.catalog) {
|
|
93
96
|
generateCatalog(catalogFile, compiled);
|
|
94
97
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wsdlLoader.d.ts","sourceRoot":"","sources":["../../src/loader/wsdlLoader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wsdlLoader.d.ts","sourceRoot":"","sources":["../../src/loader/wsdlLoader.ts"],"names":[],"mappings":"AAkBA;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,GAAG,CAAC;IACb,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAKF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAsE1E"}
|