@techspokes/typescript-wsdl-client 0.16.1 → 0.18.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 +5 -0
- package/dist/app/generateApp.d.ts.map +1 -1
- package/dist/app/generateApp.js +2 -1
- package/dist/cli.js +46 -2
- package/dist/client/generateClient.d.ts.map +1 -1
- package/dist/client/generateClient.js +62 -6
- package/dist/client/generateOperations.d.ts.map +1 -1
- package/dist/client/generateOperations.js +27 -4
- package/dist/compiler/schemaCompiler.d.ts +40 -11
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +81 -6
- package/dist/compiler/shapeResolver.d.ts +18 -0
- package/dist/compiler/shapeResolver.d.ts.map +1 -0
- package/dist/compiler/shapeResolver.js +280 -0
- package/dist/gateway/generateGateway.d.ts.map +1 -1
- package/dist/gateway/generateGateway.js +2 -1
- package/dist/gateway/generators.d.ts +13 -1
- package/dist/gateway/generators.d.ts.map +1 -1
- package/dist/gateway/generators.js +98 -13
- package/dist/gateway/helpers.d.ts +16 -0
- package/dist/gateway/helpers.d.ts.map +1 -1
- package/dist/gateway/helpers.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
- package/dist/openapi/generateOpenAPI.js +28 -0
- package/dist/pipeline.d.ts +13 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +17 -1
- package/dist/runtime/ndjson.d.ts +24 -0
- package/dist/runtime/ndjson.d.ts.map +1 -0
- package/dist/runtime/ndjson.js +30 -0
- package/dist/runtime/streamXml.d.ts +45 -0
- package/dist/runtime/streamXml.d.ts.map +1 -0
- package/dist/runtime/streamXml.js +212 -0
- package/dist/test/generators.d.ts.map +1 -1
- package/dist/test/generators.js +50 -0
- package/dist/test/mockData.d.ts +6 -0
- package/dist/test/mockData.d.ts.map +1 -1
- package/dist/test/mockData.js +6 -2
- package/dist/util/cli.d.ts.map +1 -1
- package/dist/util/cli.js +3 -1
- package/dist/util/runtimeSource.d.ts +2 -0
- package/dist/util/runtimeSource.d.ts.map +1 -0
- package/dist/util/runtimeSource.js +38 -0
- package/dist/util/streamConfig.d.ts +59 -0
- package/dist/util/streamConfig.d.ts.map +1 -0
- package/dist/util/streamConfig.js +230 -0
- package/docs/README.md +1 -0
- package/docs/api-reference.md +146 -0
- package/docs/architecture.md +27 -5
- package/docs/cli-reference.md +30 -0
- package/docs/concepts.md +51 -0
- package/docs/configuration.md +40 -0
- package/docs/decisions/002-streamable-responses.md +308 -0
- package/docs/gateway-guide.md +37 -0
- package/docs/generated-code.md +21 -0
- package/docs/migration-playbook.md +33 -0
- package/docs/migration.md +31 -6
- package/docs/output-anatomy.md +49 -0
- package/docs/production.md +32 -0
- package/docs/start-here.md +33 -0
- package/docs/supported-patterns.md +2 -0
- package/docs/testing.md +14 -0
- package/docs/troubleshooting.md +18 -0
- package/package.json +9 -5
- package/src/runtime/clientStreamMethods.tpl.txt +183 -0
- package/src/runtime/ndjson.ts +32 -0
- package/src/runtime/operationsStreamHelper.tpl.txt +13 -0
- package/src/runtime/streamXml.ts +293 -0
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Most tools in this space stop at one layer: a SOAP runtime, type generation, or
|
|
|
28
28
|
- Handles complex inheritance, `xs:attribute`, namespace collisions, nested XSD imports, and choice elements
|
|
29
29
|
- Generated `operations.ts` interface enables testing without importing `soap` or calling a live service
|
|
30
30
|
- OpenAPI is a first-class output, not an afterthought; types, schemas, and descriptions stay aligned
|
|
31
|
+
- Opt-in NDJSON streaming for large SOAP responses: client emits `AsyncIterable<RecordType>`, gateway flushes records incrementally, OpenAPI advertises the record schema via `x-wsdl-tsc-stream`
|
|
31
32
|
- MIT licensed; generated code is yours with no attribution required
|
|
32
33
|
|
|
33
34
|
## Installation
|
|
@@ -103,6 +104,7 @@ See [Output Anatomy](docs/output-anatomy.md) for a detailed walkthrough of each
|
|
|
103
104
|
- You want OpenAPI documentation that stays in sync with WSDL changes
|
|
104
105
|
- You need deterministic codegen output safe for CI/CD regeneration
|
|
105
106
|
- You are modernizing a legacy SOAP integration incrementally
|
|
107
|
+
- You have SOAP operations that return large payloads and need to stream records incrementally instead of buffering
|
|
106
108
|
|
|
107
109
|
## When NOT to Use This
|
|
108
110
|
|
|
@@ -137,6 +139,7 @@ Platform API gateways solve governance, policy, and multi-language SDK generatio
|
|
|
137
139
|
| REST gateway generation | no | no | no | yes |
|
|
138
140
|
| Runnable app scaffolding | no | no | no | yes |
|
|
139
141
|
| Mockable operations interface | no | no | no | yes |
|
|
142
|
+
| Streaming large responses (NDJSON) | no | no | no | yes |
|
|
140
143
|
|
|
141
144
|
Data as of April 2026.
|
|
142
145
|
|
|
@@ -152,6 +155,7 @@ Real-world WSDL/XSD files are rarely clean. This generator handles patterns that
|
|
|
152
155
|
- Correct optionality for nillable fields in both TypeScript and OpenAPI output
|
|
153
156
|
- The `$value` pattern for simple content with attributes, preserving text content alongside attribute properties
|
|
154
157
|
- `ArrayOf*` wrapper types, unwrapped automatically in OpenAPI with runtime bridging
|
|
158
|
+
- `xs:any` wildcard payloads mapped to concrete record shapes from a companion WSDL, enabling streaming over responses that the primary WSDL describes only as opaque wrappers
|
|
155
159
|
|
|
156
160
|
See [Core Concepts](docs/concepts.md) and [Supported Patterns](docs/supported-patterns.md) for details.
|
|
157
161
|
|
|
@@ -226,6 +230,7 @@ See [CLI Reference](docs/cli-reference.md) for all flags and examples.
|
|
|
226
230
|
| [Programmatic API](docs/api-reference.md) | TypeScript functions for build tools |
|
|
227
231
|
| [Core Concepts](docs/concepts.md) | Flattening, $value, primitives, determinism |
|
|
228
232
|
| [Architecture](docs/architecture.md) | Internal pipeline for contributors |
|
|
233
|
+
| [Streamable Responses (ADR-002)](docs/decisions/002-streamable-responses.md) | Opt-in streaming: client `AsyncIterable`, gateway NDJSON, `x-wsdl-tsc-stream` |
|
|
229
234
|
| [Version Migration](docs/migration.md) | Upgrading between package versions |
|
|
230
235
|
|
|
231
236
|
## Why This Exists
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateApp.d.ts","sourceRoot":"","sources":["../../src/app/generateApp.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"generateApp.d.ts","sourceRoot":"","sources":["../../src/app/generateApp.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA6kBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDzE"}
|
package/dist/app/generateApp.js
CHANGED
|
@@ -454,10 +454,11 @@ function generatePackageJson(appDir, force) {
|
|
|
454
454
|
dependencies: {
|
|
455
455
|
fastify: "^5.8.5",
|
|
456
456
|
"fastify-plugin": "^5.1.0",
|
|
457
|
+
saxes: "^6.0.0",
|
|
457
458
|
soap: "^1.9.1",
|
|
458
459
|
},
|
|
459
460
|
devDependencies: {
|
|
460
|
-
"@types/node": "^25.
|
|
461
|
+
"@types/node": "^25.7.0",
|
|
461
462
|
tsx: "^4.21.0",
|
|
462
463
|
typescript: "^6.0.3",
|
|
463
464
|
},
|
package/dist/cli.js
CHANGED
|
@@ -25,6 +25,25 @@ import { runGenerationPipeline } from "./pipeline.js";
|
|
|
25
25
|
import { resolveCompilerOptions } from "./config.js";
|
|
26
26
|
import { emitClientArtifacts, handleCLIError, parseServers, parseStatusCodes, reportCompilationStats, reportOpenApiSuccess, success, validateGatewayRequirements, } from "./util/cli.js";
|
|
27
27
|
import { buildCompilerOptionsFromArgv, buildOpenApiOptionsFromArgv } from "./util/builder.js";
|
|
28
|
+
import { loadStreamConfigFile, StreamConfigError } from "./util/streamConfig.js";
|
|
29
|
+
/**
|
|
30
|
+
* Load and parse a stream-config file, or call the shared CLI error handler
|
|
31
|
+
* (which prints the structured message and exits). Returns `undefined` when
|
|
32
|
+
* `filePath` is falsy so callers can chain with `argv["stream-config"]`.
|
|
33
|
+
*/
|
|
34
|
+
function loadStreamConfigOrExit(filePath) {
|
|
35
|
+
if (!filePath)
|
|
36
|
+
return undefined;
|
|
37
|
+
try {
|
|
38
|
+
return loadStreamConfigFile(filePath);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err instanceof StreamConfigError) {
|
|
42
|
+
handleCLIError(err);
|
|
43
|
+
}
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
28
47
|
// Process command line arguments, removing the first two elements (node executable and script path)
|
|
29
48
|
const rawArgs = hideBin(process.argv);
|
|
30
49
|
// Available commands
|
|
@@ -75,6 +94,7 @@ if (rawArgs[0] === "compile") {
|
|
|
75
94
|
.option("client-choice-mode", { type: "string", choices: ["all-optional", "union"], default: "all-optional" })
|
|
76
95
|
.option("client-fail-on-unresolved", { type: "boolean", default: false })
|
|
77
96
|
.option("client-nillable-as-optional", { type: "boolean", default: false })
|
|
97
|
+
.option("stream-config", { type: "string", desc: "Path to a stream configuration JSON file (ADR-002)." })
|
|
78
98
|
.strict()
|
|
79
99
|
.help()
|
|
80
100
|
.parse();
|
|
@@ -89,7 +109,17 @@ if (rawArgs[0] === "compile") {
|
|
|
89
109
|
wsdl: String(compileArgv["wsdl-source"]),
|
|
90
110
|
out: path.dirname(catalogOut),
|
|
91
111
|
});
|
|
92
|
-
const
|
|
112
|
+
const streamConfig = compileArgv["stream-config"]
|
|
113
|
+
? loadStreamConfigOrExit(String(compileArgv["stream-config"]))
|
|
114
|
+
: undefined;
|
|
115
|
+
const compiled = compileCatalog(wsdlCatalog, compilerOptions, streamConfig);
|
|
116
|
+
if (streamConfig) {
|
|
117
|
+
const { applyShapeCatalogs } = await import("./compiler/shapeResolver.js");
|
|
118
|
+
const shapeBaseDir = compileArgv["stream-config"]
|
|
119
|
+
? path.dirname(path.resolve(String(compileArgv["stream-config"])))
|
|
120
|
+
: path.dirname(path.resolve(String(compileArgv["wsdl-source"])));
|
|
121
|
+
await applyShapeCatalogs(compiled, streamConfig, { baseDir: shapeBaseDir });
|
|
122
|
+
}
|
|
93
123
|
// Report compilation statistics
|
|
94
124
|
reportCompilationStats(wsdlCatalog, compiled);
|
|
95
125
|
// Ensure output directory exists
|
|
@@ -136,6 +166,7 @@ if (rawArgs[0] === "client") {
|
|
|
136
166
|
.option("client-choice-mode", { type: "string", choices: ["all-optional", "union"], default: "all-optional" })
|
|
137
167
|
.option("client-fail-on-unresolved", { type: "boolean", default: false })
|
|
138
168
|
.option("client-nillable-as-optional", { type: "boolean", default: false })
|
|
169
|
+
.option("stream-config", { type: "string", desc: "Path to a stream configuration JSON file (ADR-002). Only applied when compiling from WSDL." })
|
|
139
170
|
.strict()
|
|
140
171
|
.help()
|
|
141
172
|
.parse();
|
|
@@ -175,7 +206,15 @@ if (rawArgs[0] === "client") {
|
|
|
175
206
|
wsdl: String(clientArgv["wsdl-source"]),
|
|
176
207
|
out: clientOutDir,
|
|
177
208
|
});
|
|
178
|
-
|
|
209
|
+
const clientStreamConfig = loadStreamConfigOrExit(clientArgv["stream-config"]);
|
|
210
|
+
compiled = compileCatalog(wsdlCatalog, compilerOptions, clientStreamConfig);
|
|
211
|
+
if (clientStreamConfig) {
|
|
212
|
+
const { applyShapeCatalogs } = await import("./compiler/shapeResolver.js");
|
|
213
|
+
const shapeBaseDir = clientArgv["stream-config"]
|
|
214
|
+
? path.dirname(path.resolve(String(clientArgv["stream-config"])))
|
|
215
|
+
: path.dirname(path.resolve(String(clientArgv["wsdl-source"])));
|
|
216
|
+
await applyShapeCatalogs(compiled, clientStreamConfig, { baseDir: shapeBaseDir });
|
|
217
|
+
}
|
|
179
218
|
// Report compilation statistics
|
|
180
219
|
reportCompilationStats(wsdlCatalog, compiled);
|
|
181
220
|
// Emit catalog
|
|
@@ -675,6 +714,10 @@ if (rawArgs[0] === "pipeline") {
|
|
|
675
714
|
type: "boolean",
|
|
676
715
|
default: false,
|
|
677
716
|
desc: "Overwrite existing test files when using --test-dir"
|
|
717
|
+
})
|
|
718
|
+
.option("stream-config", {
|
|
719
|
+
type: "string",
|
|
720
|
+
desc: "Path to a stream configuration JSON file (ADR-002). Applied to every stage that consumes the compiled catalog."
|
|
678
721
|
})
|
|
679
722
|
.strict()
|
|
680
723
|
.help()
|
|
@@ -764,6 +807,7 @@ if (rawArgs[0] === "pipeline") {
|
|
|
764
807
|
clientOutDir: clientOut ? path.resolve(clientOut) : undefined,
|
|
765
808
|
catalogOut,
|
|
766
809
|
compiler: compilerOptions,
|
|
810
|
+
streamConfigFile: pipelineArgv["stream-config"],
|
|
767
811
|
openapi: openApiOptions ? {
|
|
768
812
|
...openApiOptions,
|
|
769
813
|
outFile: path.resolve(openapiOut),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateClient.d.ts","sourceRoot":"","sources":["../../src/client/generateClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generateClient.d.ts","sourceRoot":"","sources":["../../src/client/generateClient.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAKnE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,QAsaxE"}
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* - Consistent error handling and client configuration
|
|
14
14
|
*/
|
|
15
15
|
import fs from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
16
17
|
import { deriveClientName, pascal, pascalToSnakeCase } from "../util/tools.js";
|
|
17
18
|
import { error, warn } from "../util/cli.js";
|
|
19
|
+
import { loadRuntimeSource } from "../util/runtimeSource.js";
|
|
18
20
|
/**
|
|
19
21
|
* Generates a TypeScript SOAP client class from a compiled WSDL catalog
|
|
20
22
|
*
|
|
@@ -53,6 +55,9 @@ export function generateClient(outFile, compiled) {
|
|
|
53
55
|
// get the class name for the client
|
|
54
56
|
const clientName = deriveClientName(compiled);
|
|
55
57
|
const clientConstant = pascalToSnakeCase(clientName).toUpperCase();
|
|
58
|
+
// Track whether any operation opts into streaming, so we can emit the
|
|
59
|
+
// supporting runtime helpers only when they're actually used.
|
|
60
|
+
let anyStream = false;
|
|
56
61
|
// Build the dynamic methods for the client class
|
|
57
62
|
for (const op of compiled.operations) {
|
|
58
63
|
const m = isValidIdent(op.name) && !reserved.has(op.name)
|
|
@@ -70,6 +75,38 @@ export function generateClient(outFile, compiled) {
|
|
|
70
75
|
const opDocLines = op.doc ? normalizeDocLines(op.doc) : [];
|
|
71
76
|
const opDocStr = opDocLines.length ? `\n *\n${opDocLines.map(line => ` * ${line}`).join("\n")}` : "";
|
|
72
77
|
const secHintsStr = secHints.length ? `\n *\n * Security (WSDL policy hint): ${secHints.join(", ")}` : "";
|
|
78
|
+
if (op.stream) {
|
|
79
|
+
anyStream = true;
|
|
80
|
+
const recordTs = op.stream.recordTypeName;
|
|
81
|
+
const inputElementLocal = op.inputElement?.local ?? op.name;
|
|
82
|
+
const inputElementNs = op.inputElement?.ns ?? compiled.wsdlTargetNS;
|
|
83
|
+
const methodTemplate = `
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Streams records from the ${m} operation of the ${clientName}.${opDocStr}${secHintsStr}
|
|
87
|
+
*
|
|
88
|
+
* @param args - The request arguments for the ${m} operation.
|
|
89
|
+
* @returns A promise resolving to a streaming response whose \`records\` is an
|
|
90
|
+
* async iterable of ${recordTs} objects parsed from the SOAP body as
|
|
91
|
+
* chunks arrive. Iteration pulls bytes on demand.
|
|
92
|
+
*/
|
|
93
|
+
async ${m}<HeadersType = Record<string, string>>(
|
|
94
|
+
args: ${inTs}
|
|
95
|
+
): Promise<StreamOperationResponse<T.${recordTs}, HeadersType>> {
|
|
96
|
+
return this.callStream<${inTs}, T.${recordTs}, HeadersType>(
|
|
97
|
+
args,
|
|
98
|
+
${JSON.stringify(m)},
|
|
99
|
+
${inTypeName ? JSON.stringify(inTypeName) : "undefined"},
|
|
100
|
+
${JSON.stringify(recordTs)},
|
|
101
|
+
${JSON.stringify(inputElementLocal)},
|
|
102
|
+
${JSON.stringify(inputElementNs)},
|
|
103
|
+
${JSON.stringify(op.soapAction ?? "")},
|
|
104
|
+
${JSON.stringify(op.stream.recordPath)}
|
|
105
|
+
);
|
|
106
|
+
}`;
|
|
107
|
+
methods.push(methodTemplate);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
73
110
|
const methodTemplate = `
|
|
74
111
|
|
|
75
112
|
/**
|
|
@@ -91,6 +128,11 @@ export function generateClient(outFile, compiled) {
|
|
|
91
128
|
methods.push(methodTemplate);
|
|
92
129
|
}
|
|
93
130
|
const methodsBody = methods.join("\n");
|
|
131
|
+
// The streaming transport + envelope builders are carried in a sibling
|
|
132
|
+
// *.tpl file so the IDE does not try to parse their content as code nested
|
|
133
|
+
// inside this template literal (which would flag `this`, `await`, `yield`
|
|
134
|
+
// outside-of-function false positives).
|
|
135
|
+
const streamMethodsBlock = anyStream ? "\n" + loadRuntimeSource("clientStreamMethods.tpl.txt") : "";
|
|
94
136
|
// noinspection JSFileReferences,JSUnresolvedReference,CommaExpressionJS,JSDuplicatedDeclaration,ReservedWordAsName,JSCommentMatchesSignature,JSValidateTypes,JSIgnoredPromiseFromCall,BadExpressionStatementJS,ES6UnusedImports,JSUnnecessarySemicolon,UnreachableCodeJS,JSUnusedLocalSymbols
|
|
95
137
|
const classTemplate = `// noinspection JSAnnotator
|
|
96
138
|
|
|
@@ -101,7 +143,9 @@ export function generateClient(outFile, compiled) {
|
|
|
101
143
|
import * as soap from "soap";
|
|
102
144
|
import type * as T from "./types${suffix}";
|
|
103
145
|
import type {${clientName}DataTypes} from "./utils${suffix}";
|
|
104
|
-
import {${clientConstant}_DATA_TYPES} from "./utils${suffix}"
|
|
146
|
+
import {${clientConstant}_DATA_TYPES} from "./utils${suffix}";${anyStream ? `
|
|
147
|
+
import {parseRecords, type RecordParseSpec} from "./streamXml${suffix}";
|
|
148
|
+
import type {StreamOperationResponse} from "./operations${suffix}";` : ""}
|
|
105
149
|
|
|
106
150
|
/**
|
|
107
151
|
* Represents the response structure for ${clientName} operations.
|
|
@@ -287,7 +331,7 @@ ${methodsBody}
|
|
|
287
331
|
|
|
288
332
|
// Get metadata for this specific type to know which props are attributes
|
|
289
333
|
const attributesList = (typeName && this.dataTypes?.Attributes?.[typeName]) || [];
|
|
290
|
-
const childrenTypes = (typeName && this.dataTypes?.ChildrenTypes?.[typeName]) || {};
|
|
334
|
+
const childrenTypes: Readonly<Record<string, string>> = (typeName && this.dataTypes?.ChildrenTypes?.[typeName]) || {};
|
|
291
335
|
|
|
292
336
|
const out: any = {};
|
|
293
337
|
const attributesBag: Record<string, any> = {};
|
|
@@ -320,7 +364,7 @@ ${methodsBody}
|
|
|
320
364
|
}
|
|
321
365
|
|
|
322
366
|
// Everything else becomes a child element, recursively processed
|
|
323
|
-
const childType =
|
|
367
|
+
const childType: string | undefined = childrenTypes[k];
|
|
324
368
|
out[k] = Array.isArray(v)
|
|
325
369
|
? v.map(node => this.toSoapArgs(node, childType))
|
|
326
370
|
: this.toSoapArgs(v, childType);
|
|
@@ -356,7 +400,7 @@ ${methodsBody}
|
|
|
356
400
|
}
|
|
357
401
|
|
|
358
402
|
// Get child type mapping for recursive processing with correct types
|
|
359
|
-
const childrenTypes = (typeName && this.dataTypes?.ChildrenTypes?.[typeName]) || {};
|
|
403
|
+
const childrenTypes: Readonly<Record<string, string>> = (typeName && this.dataTypes?.ChildrenTypes?.[typeName]) || {};
|
|
360
404
|
const result: any = {};
|
|
361
405
|
|
|
362
406
|
// Preserve text content for mixed XML elements
|
|
@@ -379,14 +423,14 @@ ${methodsBody}
|
|
|
379
423
|
continue;
|
|
380
424
|
}
|
|
381
425
|
// Recursively convert child elements with their specific type info
|
|
382
|
-
const childType =
|
|
426
|
+
const childType: string | undefined = childrenTypes[k];
|
|
383
427
|
result[k] = Array.isArray(v)
|
|
384
428
|
? v.map(node => this.fromSoapResult(node, childType))
|
|
385
429
|
: this.fromSoapResult(v, childType);
|
|
386
430
|
}
|
|
387
431
|
|
|
388
432
|
return result;
|
|
389
|
-
}
|
|
433
|
+
}${streamMethodsBlock}
|
|
390
434
|
}
|
|
391
435
|
`;
|
|
392
436
|
try {
|
|
@@ -395,4 +439,16 @@ ${methodsBody}
|
|
|
395
439
|
catch (e) {
|
|
396
440
|
error(`Failed to write client to ${outFile}`);
|
|
397
441
|
}
|
|
442
|
+
// If any operation opted into streaming, drop the runtime XML parser
|
|
443
|
+
// alongside the client so the generated class can import it without
|
|
444
|
+
// depending on a `@techspokes/typescript-wsdl-client/runtime/...` subpath.
|
|
445
|
+
if (anyStream) {
|
|
446
|
+
try {
|
|
447
|
+
const streamXmlOut = path.join(path.dirname(outFile), "streamXml.ts");
|
|
448
|
+
fs.writeFileSync(streamXmlOut, loadRuntimeSource("streamXml.ts"), "utf-8");
|
|
449
|
+
}
|
|
450
|
+
catch (e) {
|
|
451
|
+
error(`Failed to emit streamXml.ts next to ${outFile}: ${e instanceof Error ? e.message : String(e)}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
398
454
|
}
|
|
@@ -1 +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;
|
|
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;AAKrE;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CA8FnF"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import fs from "node:fs";
|
|
9
9
|
import { deriveClientName, pascal } from "../util/tools.js";
|
|
10
10
|
import { error } from "../util/cli.js";
|
|
11
|
+
import { loadRuntimeSource } from "../util/runtimeSource.js";
|
|
11
12
|
/**
|
|
12
13
|
* Generates an operations.ts file with a fully-typed interface for all SOAP operations
|
|
13
14
|
*
|
|
@@ -31,6 +32,9 @@ export function generateOperations(outFile, compiled) {
|
|
|
31
32
|
// Collect type names used in method signatures for the import statement
|
|
32
33
|
const importedTypes = new Set();
|
|
33
34
|
const methods = [];
|
|
35
|
+
// Does any operation opt into streaming? If so we also emit the
|
|
36
|
+
// StreamOperationResponse helper type below.
|
|
37
|
+
let anyStream = false;
|
|
34
38
|
for (const op of compiled.operations) {
|
|
35
39
|
const inTypeName = op.inputTypeName ?? (op.inputElement ? pascal(op.inputElement.local) : undefined);
|
|
36
40
|
const outTypeName = op.outputTypeName ?? (op.outputElement ? pascal(op.outputElement.local) : undefined);
|
|
@@ -47,15 +51,34 @@ export function generateOperations(outFile, compiled) {
|
|
|
47
51
|
const docBlock = docLines.length > 0
|
|
48
52
|
? ` /**\n${docLines.map(line => ` * ${line}`).join("\n")}\n */\n`
|
|
49
53
|
: "";
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
if (op.stream) {
|
|
55
|
+
anyStream = true;
|
|
56
|
+
const recordTs = op.stream.recordTypeName;
|
|
57
|
+
importedTypes.add(recordTs);
|
|
58
|
+
methods.push(`${docBlock} ${op.name}(\n` +
|
|
59
|
+
` args: ${inTs}\n` +
|
|
60
|
+
` ): Promise<StreamOperationResponse<${recordTs}>>;\n`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
methods.push(`${docBlock} ${op.name}(\n` +
|
|
64
|
+
` args: ${inTs}\n` +
|
|
65
|
+
` ): Promise<{ response: ${outTs}; headers: unknown }>;\n`);
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
// Build sorted import list for deterministic output
|
|
55
69
|
const sortedImports = Array.from(importedTypes).sort();
|
|
56
70
|
const typeImport = sortedImports.length > 0
|
|
57
71
|
? `import type {\n${sortedImports.map((t) => ` ${t},`).join("\n")}\n} from "./types${suffix}";\n\n`
|
|
58
72
|
: "";
|
|
73
|
+
// Emit the StreamOperationResponse helper type iff at least one operation
|
|
74
|
+
// is stream-configured. Kept inside operations.ts so that mocks and gateway
|
|
75
|
+
// code can import it without pulling in the SOAP runtime. The raw template
|
|
76
|
+
// lives in a sibling .tpl file so the IDE does not try to parse embedded
|
|
77
|
+
// TypeScript type-parameter defaults (HeadersType = Record<...>) as
|
|
78
|
+
// JavaScript assignment expressions.
|
|
79
|
+
const streamHelper = anyStream
|
|
80
|
+
? loadRuntimeSource("operationsStreamHelper.tpl.txt").replace(/__CLIENT_NAME__/g, clientName)
|
|
81
|
+
: "";
|
|
59
82
|
const content = `/**
|
|
60
83
|
* Typed operations interface for the ${clientName} service.
|
|
61
84
|
*
|
|
@@ -64,7 +87,7 @@ export function generateOperations(outFile, compiled) {
|
|
|
64
87
|
*
|
|
65
88
|
* Auto-generated - do not edit manually.
|
|
66
89
|
*/
|
|
67
|
-
${typeImport}/**
|
|
90
|
+
${typeImport}${streamHelper}/**
|
|
68
91
|
* All operations exposed by the ${clientName} SOAP service.
|
|
69
92
|
*
|
|
70
93
|
* The concrete ${clientName} class satisfies this interface.
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { CompilerOptions } from "../config.js";
|
|
13
13
|
import type { WsdlCatalog } from "../loader/wsdlLoader.js";
|
|
14
|
+
import type { OperationStreamMetadata, StreamConfig } from "../util/streamConfig.js";
|
|
14
15
|
/**
|
|
15
16
|
* Qualified name with namespace and local part
|
|
16
17
|
*
|
|
@@ -22,6 +23,21 @@ export type QName = {
|
|
|
22
23
|
ns: string;
|
|
23
24
|
local: string;
|
|
24
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Represents an xs:any wildcard particle retained on a complex type.
|
|
28
|
+
*
|
|
29
|
+
* The base compiler previously dropped wildcards silently. Keeping them as
|
|
30
|
+
* explicit markers lets downstream tools (stream-candidate detection,
|
|
31
|
+
* streaming XML converter) reason honestly about types whose payload lives
|
|
32
|
+
* outside the statically-typed schema — e.g. Escapia's content-service
|
|
33
|
+
* stream wrappers that mix an xs:schema marker with an xs:any payload.
|
|
34
|
+
*/
|
|
35
|
+
export type CompiledWildcard = {
|
|
36
|
+
min: number;
|
|
37
|
+
max: number | "unbounded";
|
|
38
|
+
namespace?: string;
|
|
39
|
+
processContents?: "lax" | "strict" | "skip";
|
|
40
|
+
};
|
|
25
41
|
/**
|
|
26
42
|
* Represents a compiled complex type with attributes and elements
|
|
27
43
|
*
|
|
@@ -34,6 +50,7 @@ export type QName = {
|
|
|
34
50
|
* @property {string} [base] - Base type name for extension/inheritance
|
|
35
51
|
* @property {Array<Object>} [localAttrs] - Attributes added in extension (not inherited)
|
|
36
52
|
* @property {Array<Object>} [localElems] - Elements added in extension (not inherited)
|
|
53
|
+
* @property {Array<Object>} [wildcards] - xs:any wildcard particles retained on the type
|
|
37
54
|
*/
|
|
38
55
|
export type CompiledType = {
|
|
39
56
|
name: string;
|
|
@@ -73,6 +90,7 @@ export type CompiledType = {
|
|
|
73
90
|
declaredType: string;
|
|
74
91
|
doc?: string;
|
|
75
92
|
}>;
|
|
93
|
+
wildcards?: CompiledWildcard[];
|
|
76
94
|
};
|
|
77
95
|
/**
|
|
78
96
|
* Represents a compiled type alias (simple type or restricted type)
|
|
@@ -126,6 +144,26 @@ export type CompiledWsdlDocs = {
|
|
|
126
144
|
export type CompiledDiagnostics = {
|
|
127
145
|
notes?: string[];
|
|
128
146
|
};
|
|
147
|
+
/**
|
|
148
|
+
* Compiled SOAP operation shape. Extracted so that extended fields (stream
|
|
149
|
+
* metadata, future delivery modes) live in one named type instead of being
|
|
150
|
+
* buried inline on CompiledCatalog.operations.
|
|
151
|
+
*/
|
|
152
|
+
export type CompiledOperation = {
|
|
153
|
+
name: string;
|
|
154
|
+
soapAction: string;
|
|
155
|
+
inputElement?: QName;
|
|
156
|
+
outputElement?: QName;
|
|
157
|
+
security?: string[];
|
|
158
|
+
inputTypeName?: string;
|
|
159
|
+
outputTypeName?: string;
|
|
160
|
+
doc?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Stream delivery metadata; populated from a StreamConfig when the operation
|
|
163
|
+
* is opted in. Absent for ordinary buffered operations.
|
|
164
|
+
*/
|
|
165
|
+
stream?: OperationStreamMetadata;
|
|
166
|
+
};
|
|
129
167
|
/**
|
|
130
168
|
* Complete compiled catalog with all types, aliases, operations and metadata
|
|
131
169
|
*
|
|
@@ -149,16 +187,7 @@ export type CompiledCatalog = {
|
|
|
149
187
|
childType: Record<string, Record<string, string>>;
|
|
150
188
|
propMeta: Record<string, Record<string, any>>;
|
|
151
189
|
};
|
|
152
|
-
operations:
|
|
153
|
-
name: string;
|
|
154
|
-
soapAction: string;
|
|
155
|
-
inputElement?: QName;
|
|
156
|
-
outputElement?: QName;
|
|
157
|
-
security?: string[];
|
|
158
|
-
inputTypeName?: string;
|
|
159
|
-
outputTypeName?: string;
|
|
160
|
-
doc?: string;
|
|
161
|
-
}>;
|
|
190
|
+
operations: CompiledOperation[];
|
|
162
191
|
wsdlTargetNS: string;
|
|
163
192
|
wsdlUri: string;
|
|
164
193
|
serviceName?: string;
|
|
@@ -175,5 +204,5 @@ export type CompiledCatalog = {
|
|
|
175
204
|
* 4. Extract WSDL operations: pick the appropriate SOAP binding (v1.1 or v1.2), resolve its
|
|
176
205
|
* portType reference, then enumerate operations and their soapAction URIs.
|
|
177
206
|
*/
|
|
178
|
-
export declare function compileCatalog(cat: WsdlCatalog, options: CompilerOptions): CompiledCatalog;
|
|
207
|
+
export declare function compileCatalog(cat: WsdlCatalog, options: CompilerOptions, streamConfig?: StreamConfig): CompiledCatalog;
|
|
179
208
|
//# sourceMappingURL=schemaCompiler.d.ts.map
|
|
@@ -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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"}
|
|
@@ -153,7 +153,7 @@ function extractAnnotationDocumentation(node) {
|
|
|
153
153
|
* 4. Extract WSDL operations: pick the appropriate SOAP binding (v1.1 or v1.2), resolve its
|
|
154
154
|
* portType reference, then enumerate operations and their soapAction URIs.
|
|
155
155
|
*/
|
|
156
|
-
export function compileCatalog(cat, options) {
|
|
156
|
+
export function compileCatalog(cat, options, streamConfig) {
|
|
157
157
|
// symbol tables discovered across all schemas
|
|
158
158
|
const complexTypes = new Map();
|
|
159
159
|
const simpleTypes = new Map();
|
|
@@ -353,6 +353,33 @@ export function compileCatalog(cat, options) {
|
|
|
353
353
|
}
|
|
354
354
|
return out;
|
|
355
355
|
};
|
|
356
|
+
// Walks the same compositor structure as collectParticles and returns any
|
|
357
|
+
// xs:any wildcard particles found. Kept as a sibling to avoid reshaping
|
|
358
|
+
// collectParticles' return type at the three call sites below.
|
|
359
|
+
const collectWildcards = (node) => {
|
|
360
|
+
const out = [];
|
|
361
|
+
const recurse = (groupNode) => {
|
|
362
|
+
for (const a of getChildrenWithLocalName(groupNode, "any")) {
|
|
363
|
+
const min = a["@_minOccurs"] ? Number(a["@_minOccurs"]) : 1;
|
|
364
|
+
const maxAttr = a["@_maxOccurs"];
|
|
365
|
+
const max = maxAttr === "unbounded" ? "unbounded" : maxAttr ? Number(maxAttr) : 1;
|
|
366
|
+
const pc = a["@_processContents"];
|
|
367
|
+
out.push({
|
|
368
|
+
min,
|
|
369
|
+
max,
|
|
370
|
+
...(a["@_namespace"] ? { namespace: a["@_namespace"] } : {}),
|
|
371
|
+
...(pc === "lax" || pc === "strict" || pc === "skip" ? { processContents: pc } : {}),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
for (const comp of ["sequence", "all", "choice"]) {
|
|
375
|
+
for (const sub of getChildrenWithLocalName(groupNode, comp)) {
|
|
376
|
+
recurse(sub);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
recurse(node);
|
|
381
|
+
return out;
|
|
382
|
+
};
|
|
356
383
|
const collectParticles = (ownerTypeName, node) => {
|
|
357
384
|
const out = [];
|
|
358
385
|
// process a compositor or element container recursively
|
|
@@ -424,8 +451,12 @@ export function compileCatalog(cat, options) {
|
|
|
424
451
|
// On duplicate definitions, merge new attributes and elements
|
|
425
452
|
const newAttrs = collectAttributes(cnode);
|
|
426
453
|
const newElems = collectParticles(outName, cnode);
|
|
454
|
+
const newWildcards = collectWildcards(cnode);
|
|
427
455
|
mergeAttrs(present.attrs, newAttrs);
|
|
428
456
|
mergeElems(present.elems, newElems);
|
|
457
|
+
if (newWildcards.length > 0) {
|
|
458
|
+
present.wildcards = [...(present.wildcards ?? []), ...newWildcards];
|
|
459
|
+
}
|
|
429
460
|
if (!present.doc && typeDoc) {
|
|
430
461
|
present.doc = typeDoc;
|
|
431
462
|
}
|
|
@@ -460,6 +491,7 @@ export function compileCatalog(cat, options) {
|
|
|
460
491
|
// collect local additions/overrides
|
|
461
492
|
const locals = collectAttributes(node);
|
|
462
493
|
const localElems = collectParticles(outName, node);
|
|
494
|
+
const localWildcards = collectWildcards(node);
|
|
463
495
|
attrs.push(...locals);
|
|
464
496
|
elems.push(...localElems);
|
|
465
497
|
const result = {
|
|
@@ -470,7 +502,8 @@ export function compileCatalog(cat, options) {
|
|
|
470
502
|
doc: typeDoc,
|
|
471
503
|
base: baseName,
|
|
472
504
|
localAttrs: locals,
|
|
473
|
-
localElems
|
|
505
|
+
localElems,
|
|
506
|
+
...(localWildcards.length > 0 ? { wildcards: localWildcards } : {}),
|
|
474
507
|
};
|
|
475
508
|
compiledMap.set(key, result);
|
|
476
509
|
inProgress.delete(key);
|
|
@@ -513,7 +546,15 @@ export function compileCatalog(cat, options) {
|
|
|
513
546
|
// Attributes + particles
|
|
514
547
|
mergeAttrs(attrs, collectAttributes(cnode));
|
|
515
548
|
mergeElems(elems, collectParticles(outName, cnode));
|
|
516
|
-
const
|
|
549
|
+
const wildcards = collectWildcards(cnode);
|
|
550
|
+
const result = {
|
|
551
|
+
name: outName,
|
|
552
|
+
ns: schemaNS,
|
|
553
|
+
attrs,
|
|
554
|
+
elems,
|
|
555
|
+
doc: typeDoc,
|
|
556
|
+
...(wildcards.length > 0 ? { wildcards } : {}),
|
|
557
|
+
};
|
|
517
558
|
compiledMap.set(key, result);
|
|
518
559
|
inProgress.delete(rawKey);
|
|
519
560
|
return result;
|
|
@@ -832,7 +873,7 @@ export function compileCatalog(cat, options) {
|
|
|
832
873
|
.filter((x) => x != null)
|
|
833
874
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
834
875
|
// build operations list
|
|
835
|
-
const ops =
|
|
876
|
+
const ops = pOps
|
|
836
877
|
.map(po => {
|
|
837
878
|
const name = po?.["@_name"];
|
|
838
879
|
if (!name)
|
|
@@ -845,9 +886,18 @@ export function compileCatalog(cat, options) {
|
|
|
845
886
|
// Derive TypeScript type names from element local names
|
|
846
887
|
const inputTypeName = inputElement ? pascal(inputElement.local) : undefined;
|
|
847
888
|
const outputTypeName = outputElement ? pascal(outputElement.local) : undefined;
|
|
848
|
-
|
|
889
|
+
const op = {
|
|
890
|
+
name,
|
|
891
|
+
soapAction: bOps.get(name) || "",
|
|
892
|
+
...(inputElement ? { inputElement } : {}),
|
|
893
|
+
...(outputElement ? { outputElement } : {}),
|
|
894
|
+
...(inputTypeName ? { inputTypeName } : {}),
|
|
895
|
+
...(outputTypeName ? { outputTypeName } : {}),
|
|
896
|
+
...(doc ? { doc } : {}),
|
|
897
|
+
};
|
|
898
|
+
return op;
|
|
849
899
|
})
|
|
850
|
-
.filter((x) => x != null)
|
|
900
|
+
.filter((x) => x != null);
|
|
851
901
|
// --- WS-Policy: scan for security requirements (inline policies only) ---
|
|
852
902
|
const bindingPolicies = getChildrenWithLocalName(soapBinding || {}, "Policy");
|
|
853
903
|
const bindingSec = collectSecurityFromPolicyNodes(bindingPolicies);
|
|
@@ -858,6 +908,31 @@ export function compileCatalog(cat, options) {
|
|
|
858
908
|
const secSet = new Set([...bindingSec, ...opSec]);
|
|
859
909
|
op.security = Array.from(secSet);
|
|
860
910
|
}
|
|
911
|
+
// --- Stream config application ---
|
|
912
|
+
// A stream-configured operation is matched by name against the WSDL's
|
|
913
|
+
// portType operations. Unknown operation names are fatal so that consumers
|
|
914
|
+
// find out about stale configs at generation time, not at runtime.
|
|
915
|
+
if (streamConfig) {
|
|
916
|
+
const opByName = new Map(ops.map((op) => [op.name, op]));
|
|
917
|
+
for (const [opName, meta] of Object.entries(streamConfig.operations)) {
|
|
918
|
+
const op = opByName.get(opName);
|
|
919
|
+
if (!op) {
|
|
920
|
+
throw new WsdlCompilationError(`Stream config references operation "${opName}" but the WSDL portType does not declare it.`, {
|
|
921
|
+
element: opName,
|
|
922
|
+
suggestion: `Remove the entry under "operations.${opName}" in the stream config, or correct the operation name to match one defined in the WSDL.`,
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
op.stream = {
|
|
926
|
+
mode: meta.mode,
|
|
927
|
+
format: meta.format,
|
|
928
|
+
mediaType: meta.mediaType,
|
|
929
|
+
recordPath: [...meta.recordPath],
|
|
930
|
+
recordTypeName: meta.recordTypeName,
|
|
931
|
+
...(meta.shapeCatalogName ? { shapeCatalogName: meta.shapeCatalogName } : {}),
|
|
932
|
+
...(op.outputTypeName ? { sourceOutputTypeName: op.outputTypeName } : {}),
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
}
|
|
861
936
|
// --- Service discovery (for client naming) ---
|
|
862
937
|
let serviceName;
|
|
863
938
|
const soapBindingName = soapBinding?.["@_name"];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type CompiledCatalog } from "./schemaCompiler.js";
|
|
2
|
+
import type { StreamConfig } from "../util/streamConfig.js";
|
|
3
|
+
export interface ApplyShapeCatalogsOptions {
|
|
4
|
+
/** Directory against which relative `catalogFile`/`wsdlSource` paths resolve. Defaults to process.cwd(). */
|
|
5
|
+
baseDir?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Apply a parsed StreamConfig to a compiled catalog:
|
|
9
|
+
* 1. Verify every opted-in operation's record type is resolvable.
|
|
10
|
+
* 2. For each operation that names a shapeCatalog, load the companion
|
|
11
|
+
* catalog (once, cached) and copy the reachable record-type graph.
|
|
12
|
+
*
|
|
13
|
+
* Mutates `compiled` in place. Safe to call with a StreamConfig that has
|
|
14
|
+
* zero shapeCatalogs — it will still validate record-type presence against
|
|
15
|
+
* the current catalog.
|
|
16
|
+
*/
|
|
17
|
+
export declare function applyShapeCatalogs(compiled: CompiledCatalog, streamConfig: StreamConfig, options?: ApplyShapeCatalogsOptions): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=shapeResolver.d.ts.map
|