@techspokes/typescript-wsdl-client 1.1.8 → 1.3.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 +22 -3
- package/dist/checkedPlanning.js +27 -4
- package/dist/cli.js +17 -1
- package/dist/client/checkedDirectRuntime.d.ts +11 -1
- package/dist/client/checkedDirectRuntime.js +19 -2
- package/dist/client/directClientRuntime.d.ts +18 -1
- package/dist/client/directClientRuntime.js +15 -3
- package/dist/client/directClientVersion.d.ts +2 -0
- package/dist/client/directClientVersion.js +2 -0
- package/dist/client/generateClient.js +72 -16
- package/dist/client/generateDirectClient.js +14 -9
- package/dist/client/generateOperations.js +30 -11
- package/dist/client/generateUtils.js +5 -1
- package/dist/compiler/canonicalGraph.d.ts +4 -15
- package/dist/compiler/canonicalGraph.js +1 -8
- package/dist/compiler/componentRecordProjection.d.ts +3 -0
- package/dist/compiler/componentRecordProjection.js +10 -0
- package/dist/compiler/declarationIdentity.d.ts +22 -0
- package/dist/compiler/declarationIdentity.js +9 -0
- package/dist/compiler/directCodec.d.ts +22 -2
- package/dist/compiler/directCodec.js +40 -7
- package/dist/compiler/directOperation.d.ts +28 -5
- package/dist/compiler/directOperation.js +84 -14
- package/dist/compiler/directSoap.d.ts +40 -10
- package/dist/compiler/directSoap.js +151 -18
- package/dist/compiler/directSoapSource.js +3 -2
- package/dist/compiler/directSourceCapsule.js +69 -4
- package/dist/compiler/directStructure.js +6 -2
- package/dist/compiler/directStructurePlan.d.ts +10 -1
- package/dist/compiler/directStructurePlan.js +27 -7
- package/dist/compiler/directValues.d.ts +15 -3
- package/dist/compiler/directValues.js +20 -4
- package/dist/compiler/legacyDeclarations.d.ts +26 -0
- package/dist/compiler/legacyDeclarations.js +144 -0
- package/dist/compiler/projectionScalar.d.ts +18 -1
- package/dist/compiler/projectionScalar.js +47 -13
- package/dist/compiler/rpcStringValues.d.ts +30 -0
- package/dist/compiler/rpcStringValues.js +196 -0
- package/dist/compiler/rpcTypedValues.d.ts +48 -0
- package/dist/compiler/rpcTypedValues.js +190 -0
- package/dist/compiler/schemaCompiler.js +44 -109
- package/dist/compiler/semanticCatalog.d.ts +2 -2
- package/dist/compiler/soapHeaders.d.ts +10 -4
- package/dist/compiler/soapHeaders.js +28 -9
- package/dist/gateway/generateCheckedGateway.js +26 -6
- package/dist/gateway/generateComposedGateway.d.ts +16 -0
- package/dist/gateway/generateComposedGateway.js +65 -0
- package/dist/gateway/generateGateway.js +1 -0
- package/dist/gateway/generators.js +19 -4
- package/dist/gateway/helpers.d.ts +1 -0
- package/dist/gateway/helpers.js +15 -0
- package/dist/generation/componentShape.js +25 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/loader/orderedSyntax.d.ts +4 -0
- package/dist/loader/orderedSyntax.js +18 -4
- package/dist/multiServicePipeline.d.ts +41 -0
- package/dist/multiServicePipeline.js +287 -0
- package/dist/openapi/composeOpenAPI.d.ts +29 -0
- package/dist/openapi/composeOpenAPI.js +322 -0
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +2 -0
- package/docs/agent-skill.md +10 -1
- package/docs/api-reference.md +127 -9
- package/docs/checked-gateway.md +10 -0
- package/docs/checked-generation.md +11 -5
- package/docs/cli-reference.md +25 -5
- package/docs/concepts.md +4 -5
- package/docs/configuration.md +10 -3
- package/docs/corpus.md +1 -1
- package/docs/gateway-guide.md +104 -27
- package/docs/generated-code.md +12 -6
- package/docs/migration-playbook.md +4 -0
- package/docs/migration.md +134 -6
- package/docs/output-anatomy.md +20 -17
- package/docs/production.md +7 -5
- package/docs/start-here.md +12 -2
- package/docs/supported-patterns.md +23 -0
- package/docs/testing.md +2 -0
- package/docs/troubleshooting.md +3 -1
- package/llms.txt +9 -1
- package/package.json +2 -2
- package/src/runtime/clientStreamMethods.tpl.txt +156 -148
- package/src/runtime/operationsStreamHelper.tpl.txt +6 -6
- package/src/runtime/streamXml.ts +48 -20
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { types } from "node:util";
|
|
2
|
+
const methods = new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"]);
|
|
3
|
+
const categories = {
|
|
4
|
+
schemas: "schema", responses: "response", parameters: "parameter", examples: "example",
|
|
5
|
+
requestBodies: "requestBody", headers: "header", securitySchemes: "securityScheme", links: "link",
|
|
6
|
+
callbacks: "callback", pathItems: "pathItem",
|
|
7
|
+
};
|
|
8
|
+
const dict = () => Object.create(null);
|
|
9
|
+
const record = (value, context) => {
|
|
10
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
11
|
+
throw new TypeError(`${context} must be an object`);
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
const items = (value, context) => {
|
|
15
|
+
if (!Array.isArray(value))
|
|
16
|
+
throw new TypeError(`${context} must be an array`);
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
const pointerToken = (value) => value.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
20
|
+
/** Copy JSON data without invoking accessors or sharing mutable source objects.
|
|
21
|
+
* @throws For non-JSON values, accessors, proxies, sparse arrays or cycles.
|
|
22
|
+
*/
|
|
23
|
+
function copyJson(value, active = new Set()) {
|
|
24
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
25
|
+
return value;
|
|
26
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
27
|
+
return value;
|
|
28
|
+
if (!value || typeof value !== "object" || types.isProxy(value) || active.has(value))
|
|
29
|
+
throw new TypeError("Actual acyclic JSON data required");
|
|
30
|
+
if (!Array.isArray(value) && ![null, Object.prototype].includes(Object.getPrototypeOf(value)))
|
|
31
|
+
throw new TypeError("Plain JSON objects required");
|
|
32
|
+
active.add(value);
|
|
33
|
+
try {
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
if (Reflect.ownKeys(value).length !== value.length + 1)
|
|
36
|
+
throw new TypeError("Dense JSON arrays required");
|
|
37
|
+
return Array.from({ length: value.length }, (_, index) => {
|
|
38
|
+
const property = Object.getOwnPropertyDescriptor(value, String(index));
|
|
39
|
+
if (!property || !Object.hasOwn(property, "value"))
|
|
40
|
+
throw new TypeError("JSON array data properties required");
|
|
41
|
+
return copyJson(property.value, active);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const result = dict();
|
|
45
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
46
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
47
|
+
if (typeof key !== "string" || !property.enumerable || !Object.hasOwn(property, "value"))
|
|
48
|
+
throw new TypeError("JSON object data properties required");
|
|
49
|
+
result[key] = copyJson(property.value, active);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
active.delete(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function stable(value) {
|
|
58
|
+
if (Array.isArray(value))
|
|
59
|
+
return value.map(stable);
|
|
60
|
+
if (!value || typeof value !== "object")
|
|
61
|
+
return value;
|
|
62
|
+
return Object.fromEntries(Object.keys(value).sort().map(key => [key, stable(value[key])]));
|
|
63
|
+
}
|
|
64
|
+
/** Compose service-scoped OpenAPI contracts without changing SOAP client ownership.
|
|
65
|
+
* @param entries Self-contained OpenAPI 3.1 documents whose HTTP paths already contain their service prefixes.
|
|
66
|
+
* @param options Combined API metadata and default gateway servers; custom source servers remain operation-local.
|
|
67
|
+
* @returns A fresh deterministic document with qualified component names and operation identifiers.
|
|
68
|
+
* @throws For invalid JSON, duplicate service keys/paths/operation IDs, unresolved local references or unsupported relative references/schema resources.
|
|
69
|
+
* @constraints Only reference-bearing OpenAPI/JSON Schema positions are rewritten; examples, literal values and extensions remain data.
|
|
70
|
+
* @see https://spec.openapis.org/oas/v3.1.0.html Reference, Link, Security Requirement and Schema objects govern distinct reference positions.
|
|
71
|
+
*/
|
|
72
|
+
export function composeOpenApiDocuments(entries, options = {}) {
|
|
73
|
+
if (!Array.isArray(entries) || entries.length === 0)
|
|
74
|
+
throw new TypeError("At least one service document is required");
|
|
75
|
+
const input = items(copyJson(entries), "services");
|
|
76
|
+
const keys = new Set();
|
|
77
|
+
const paths = dict();
|
|
78
|
+
const components = dict();
|
|
79
|
+
const webhooks = dict();
|
|
80
|
+
const tags = dict();
|
|
81
|
+
const services = dict();
|
|
82
|
+
const rootServers = options.servers?.map(url => ({ url })) ?? [{ url: "/" }];
|
|
83
|
+
let dialect;
|
|
84
|
+
for (const entry of input.map(value => record(value, "service")).sort((left, right) => String(left.key).localeCompare(String(right.key)))) {
|
|
85
|
+
const key = entry.key;
|
|
86
|
+
if (typeof key !== "string" || !/^[a-z][a-z0-9-]*$/.test(key) || keys.has(key))
|
|
87
|
+
throw new TypeError("Unique lowercase service keys required");
|
|
88
|
+
keys.add(key);
|
|
89
|
+
const source = record(entry.document, `service ${key}`);
|
|
90
|
+
if (typeof source.openapi !== "string" || !/^3\.1\.\d+$/.test(source.openapi))
|
|
91
|
+
throw new TypeError("OpenAPI 3.1 documents required");
|
|
92
|
+
record(source.info, "OpenAPI info");
|
|
93
|
+
const sourcePaths = record(source.paths, "OpenAPI paths");
|
|
94
|
+
const sourceComponents = source.components === undefined ? dict() : record(source.components, "OpenAPI components");
|
|
95
|
+
const sourceDialect = source.jsonSchemaDialect ?? "https://spec.openapis.org/oas/3.1/dialect/base";
|
|
96
|
+
if (typeof sourceDialect !== "string" || dialect !== undefined && dialect !== sourceDialect)
|
|
97
|
+
throw new TypeError("Source schema dialects must agree");
|
|
98
|
+
dialect = sourceDialect;
|
|
99
|
+
const qualified = (name) => `${key}__${name}`;
|
|
100
|
+
const operationIds = new Map();
|
|
101
|
+
const operationNodes = new Set();
|
|
102
|
+
const sourceRootServers = source.servers === undefined ? undefined : items(source.servers, "servers");
|
|
103
|
+
const defaultSourceServer = JSON.stringify(sourceRootServers) === '[{"url":"/"}]';
|
|
104
|
+
const effectiveServers = options.servers && defaultSourceServer ? undefined : sourceRootServers;
|
|
105
|
+
const defaultSecurity = source.security;
|
|
106
|
+
function resolve(tokens, reference) {
|
|
107
|
+
let target = source;
|
|
108
|
+
for (const token of tokens) {
|
|
109
|
+
if (!target || typeof target !== "object" || !Object.hasOwn(target, token))
|
|
110
|
+
throw new TypeError(`Unresolved local reference ${reference} in ${key}`);
|
|
111
|
+
target = target[token];
|
|
112
|
+
}
|
|
113
|
+
return target;
|
|
114
|
+
}
|
|
115
|
+
function reference(value, operation = false) {
|
|
116
|
+
if (typeof value !== "string" || value.length === 0)
|
|
117
|
+
throw new TypeError("Reference must be a nonempty string");
|
|
118
|
+
if (!value.startsWith("#")) {
|
|
119
|
+
try {
|
|
120
|
+
new URL(value);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
throw new TypeError(`Relative external references need resolution before composition: ${value}`);
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
let fragment;
|
|
128
|
+
try {
|
|
129
|
+
fragment = decodeURIComponent(value.slice(1));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
throw new TypeError(`Invalid reference ${value}`);
|
|
133
|
+
}
|
|
134
|
+
if (!fragment.startsWith("/") || /~(?![01])/u.test(fragment))
|
|
135
|
+
throw new TypeError(`Unsupported local reference ${value}`);
|
|
136
|
+
const tokens = fragment.slice(1).split("/").map(token => token.replaceAll("~1", "/").replaceAll("~0", "~"));
|
|
137
|
+
const target = resolve(tokens, value);
|
|
138
|
+
if (operation && (!target || typeof target !== "object" || Array.isArray(target) || !operationNodes.has(target)))
|
|
139
|
+
throw new TypeError(`operationRef does not resolve to an operation: ${value}`);
|
|
140
|
+
if (tokens[0] === "components" && tokens.length >= 3)
|
|
141
|
+
tokens[2] = qualified(tokens[2]);
|
|
142
|
+
else if (tokens[0] === "webhooks" && tokens.length >= 2)
|
|
143
|
+
tokens[1] = qualified(tokens[1]);
|
|
144
|
+
else if (tokens[0] !== "paths")
|
|
145
|
+
throw new TypeError(`Reference target cannot be retained during composition: ${value}`);
|
|
146
|
+
return "#/" + tokens.map(token => encodeURIComponent(pointerToken(token))).join("/");
|
|
147
|
+
}
|
|
148
|
+
function security(value) {
|
|
149
|
+
return items(value, "security").map(requirement => Object.fromEntries(Object.entries(record(requirement, "security requirement")).map(([name, scopes]) => {
|
|
150
|
+
if (!Object.hasOwn(record(sourceComponents.securitySchemes, "securitySchemes"), name))
|
|
151
|
+
throw new TypeError(`Missing security scheme ${name}`);
|
|
152
|
+
if (!items(scopes, "security scopes").every(scope => typeof scope === "string"))
|
|
153
|
+
throw new TypeError("Security scopes must be strings");
|
|
154
|
+
return [qualified(name), scopes];
|
|
155
|
+
})));
|
|
156
|
+
}
|
|
157
|
+
function map(value, kind, scan, extensions = false) {
|
|
158
|
+
return Object.fromEntries(Object.entries(record(value, `${kind} map`)).map(([name, child]) => [name,
|
|
159
|
+
extensions && name.startsWith("x-") ? child : walk(child, kind, scan)]));
|
|
160
|
+
}
|
|
161
|
+
function walk(value, kind, scan, inheritedServers = effectiveServers) {
|
|
162
|
+
if (kind === "data")
|
|
163
|
+
return value;
|
|
164
|
+
if (kind === "schema" && typeof value === "boolean")
|
|
165
|
+
return value;
|
|
166
|
+
const node = record(value, kind);
|
|
167
|
+
const out = { ...node };
|
|
168
|
+
if (kind === "operation" && scan) {
|
|
169
|
+
operationNodes.add(node);
|
|
170
|
+
if (node.operationId !== undefined) {
|
|
171
|
+
if (typeof node.operationId !== "string" || !node.operationId || operationIds.has(node.operationId))
|
|
172
|
+
throw new TypeError(`Duplicate or invalid operationId in ${key}`);
|
|
173
|
+
operationIds.set(node.operationId, node);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (Object.hasOwn(node, "$ref") && !scan)
|
|
177
|
+
out.$ref = reference(node.$ref);
|
|
178
|
+
const array = (name, childKind) => { if (node[name] !== undefined)
|
|
179
|
+
out[name] = items(node[name], name).map(child => walk(child, childKind, scan)); };
|
|
180
|
+
const child = (name, childKind) => { if (node[name] !== undefined)
|
|
181
|
+
out[name] = walk(node[name], childKind, scan); };
|
|
182
|
+
const keyed = (name, childKind, extensions = false) => { if (node[name] !== undefined)
|
|
183
|
+
out[name] = map(node[name], childKind, scan, extensions); };
|
|
184
|
+
if (kind === "schema") {
|
|
185
|
+
if (["$id", "$anchor", "$dynamicAnchor", "$vocabulary"].some(name => Object.hasOwn(node, name)))
|
|
186
|
+
throw new TypeError("Resolve schema resources/anchors before OpenAPI composition");
|
|
187
|
+
if (node.$dynamicRef !== undefined && !scan)
|
|
188
|
+
out.$dynamicRef = reference(node.$dynamicRef);
|
|
189
|
+
for (const name of ["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"])
|
|
190
|
+
keyed(name, "schema");
|
|
191
|
+
for (const name of ["allOf", "anyOf", "oneOf", "prefixItems"])
|
|
192
|
+
array(name, "schema");
|
|
193
|
+
for (const name of ["items", "contains", "additionalProperties", "unevaluatedProperties", "unevaluatedItems", "propertyNames", "not", "if", "then", "else", "additionalItems"])
|
|
194
|
+
child(name, "schema");
|
|
195
|
+
if (node.discriminator !== undefined && !scan) {
|
|
196
|
+
const discriminator = record(node.discriminator, "discriminator");
|
|
197
|
+
const mapping = discriminator.mapping;
|
|
198
|
+
if (mapping !== undefined)
|
|
199
|
+
out.discriminator = { ...discriminator, mapping: Object.fromEntries(Object.entries(record(mapping, "discriminator mapping")).map(([name, target]) => {
|
|
200
|
+
if (typeof target !== "string")
|
|
201
|
+
throw new TypeError("Discriminator targets must be strings");
|
|
202
|
+
const schemas = record(sourceComponents.schemas, "schemas");
|
|
203
|
+
return [name, Object.hasOwn(schemas, target) ? qualified(target) : reference(target)];
|
|
204
|
+
})) };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (kind === "pathItem") {
|
|
208
|
+
array("parameters", "parameter");
|
|
209
|
+
const servers = node.servers === undefined ? inheritedServers : items(node.servers, "path servers");
|
|
210
|
+
for (const method of methods)
|
|
211
|
+
if (node[method] !== undefined)
|
|
212
|
+
out[method] = walk(node[method], "operation", scan, servers);
|
|
213
|
+
}
|
|
214
|
+
else if (kind === "operation") {
|
|
215
|
+
array("parameters", "parameter");
|
|
216
|
+
child("requestBody", "requestBody");
|
|
217
|
+
keyed("responses", "response", true);
|
|
218
|
+
keyed("callbacks", "callback");
|
|
219
|
+
if (!scan) {
|
|
220
|
+
if (node.operationId !== undefined)
|
|
221
|
+
out.operationId = `${key}.${node.operationId}`;
|
|
222
|
+
if (node.security !== undefined || defaultSecurity !== undefined)
|
|
223
|
+
out.security = security(node.security ?? defaultSecurity);
|
|
224
|
+
if (node.servers === undefined && inheritedServers !== undefined && JSON.stringify(inheritedServers) !== JSON.stringify(rootServers))
|
|
225
|
+
out.servers = inheritedServers;
|
|
226
|
+
if (node.tags !== undefined)
|
|
227
|
+
out.tags = items(node.tags, "tags").map(tag => { if (typeof tag !== "string")
|
|
228
|
+
throw new TypeError("Tags must be strings"); return `${key}.${tag}`; });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else if (kind === "parameter" || kind === "header" || kind === "mediaType") {
|
|
232
|
+
child("schema", "schema");
|
|
233
|
+
keyed("examples", "example");
|
|
234
|
+
keyed("content", "mediaType");
|
|
235
|
+
keyed("encoding", "encoding");
|
|
236
|
+
if (kind === "mediaType" && node["x-wsdl-tsc-stream"] !== undefined) {
|
|
237
|
+
// @why Generated stream consumers discover record schemas here; unrelated extension payloads may contain literal $ref data.
|
|
238
|
+
const stream = record(node["x-wsdl-tsc-stream"], "stream metadata");
|
|
239
|
+
out["x-wsdl-tsc-stream"] = { ...stream,
|
|
240
|
+
...(stream.itemSchema === undefined ? {} : { itemSchema: walk(stream.itemSchema, "schema", scan) }) };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else if (kind === "encoding")
|
|
244
|
+
keyed("headers", "header");
|
|
245
|
+
else if (kind === "requestBody")
|
|
246
|
+
keyed("content", "mediaType");
|
|
247
|
+
else if (kind === "response") {
|
|
248
|
+
keyed("headers", "header");
|
|
249
|
+
keyed("content", "mediaType");
|
|
250
|
+
keyed("links", "link");
|
|
251
|
+
}
|
|
252
|
+
else if (kind === "callback") {
|
|
253
|
+
for (const [expression, pathItem] of Object.entries(node))
|
|
254
|
+
if (expression !== "$ref" && !expression.startsWith("x-"))
|
|
255
|
+
out[expression] = walk(pathItem, "pathItem", scan);
|
|
256
|
+
}
|
|
257
|
+
else if (kind === "link" && !scan) {
|
|
258
|
+
if (node.operationId !== undefined && node.operationRef !== undefined)
|
|
259
|
+
throw new TypeError("A link cannot set both operationId and operationRef");
|
|
260
|
+
if (node.operationId !== undefined) {
|
|
261
|
+
if (typeof node.operationId !== "string" || !operationIds.has(node.operationId))
|
|
262
|
+
throw new TypeError(`Unresolved link operationId ${String(node.operationId)}`);
|
|
263
|
+
out.operationId = `${key}.${node.operationId}`;
|
|
264
|
+
}
|
|
265
|
+
if (node.operationRef !== undefined)
|
|
266
|
+
out.operationRef = reference(node.operationRef, true);
|
|
267
|
+
}
|
|
268
|
+
return out;
|
|
269
|
+
}
|
|
270
|
+
for (const [route, value] of Object.entries(sourcePaths))
|
|
271
|
+
if (!route.startsWith("x-"))
|
|
272
|
+
walk(value, "pathItem", true);
|
|
273
|
+
if (source.webhooks !== undefined)
|
|
274
|
+
map(source.webhooks, "pathItem", true);
|
|
275
|
+
for (const [category, values] of Object.entries(sourceComponents)) {
|
|
276
|
+
if (category.startsWith("x-"))
|
|
277
|
+
continue;
|
|
278
|
+
if (!Object.hasOwn(categories, category))
|
|
279
|
+
throw new TypeError(`Unsupported OpenAPI component category ${category}`);
|
|
280
|
+
map(values, categories[category], true);
|
|
281
|
+
}
|
|
282
|
+
for (const [route, value] of Object.entries(sourcePaths)) {
|
|
283
|
+
if (route.startsWith("x-"))
|
|
284
|
+
continue;
|
|
285
|
+
if (!route.startsWith("/") || Object.hasOwn(paths, route))
|
|
286
|
+
throw new TypeError(`Duplicate or invalid final path ${route}`);
|
|
287
|
+
paths[route] = record(walk(value, "pathItem", false), "path item");
|
|
288
|
+
}
|
|
289
|
+
if (source.webhooks !== undefined)
|
|
290
|
+
for (const [name, value] of Object.entries(record(source.webhooks, "webhooks")))
|
|
291
|
+
webhooks[qualified(name)] = walk(value, "pathItem", false);
|
|
292
|
+
for (const [category, values] of Object.entries(sourceComponents)) {
|
|
293
|
+
if (category.startsWith("x-"))
|
|
294
|
+
continue;
|
|
295
|
+
const destination = components[category] ??= dict();
|
|
296
|
+
for (const [name, value] of Object.entries(record(values, "components")))
|
|
297
|
+
destination[qualified(name)] = walk(value, categories[category], false);
|
|
298
|
+
}
|
|
299
|
+
if (source.tags !== undefined)
|
|
300
|
+
for (const value of items(source.tags, "tags")) {
|
|
301
|
+
const tag = record(value, "tag");
|
|
302
|
+
if (typeof tag.name !== "string")
|
|
303
|
+
throw new TypeError("Tag name must be a string");
|
|
304
|
+
tags[`${key}.${tag.name}`] = { ...tag, name: `${key}.${tag.name}` };
|
|
305
|
+
}
|
|
306
|
+
services[key] = { info: source.info,
|
|
307
|
+
extensions: Object.fromEntries(Object.entries(source).filter(([name]) => name.startsWith("x-"))),
|
|
308
|
+
componentExtensions: Object.fromEntries(Object.entries(sourceComponents).filter(([name]) => name.startsWith("x-"))),
|
|
309
|
+
pathExtensions: Object.fromEntries(Object.entries(sourcePaths).filter(([name]) => name.startsWith("x-"))) };
|
|
310
|
+
}
|
|
311
|
+
const info = { title: options.title ?? "Composed SOAP API", version: options.version ?? "0.0.0" };
|
|
312
|
+
if (options.description !== undefined)
|
|
313
|
+
info.description = options.description;
|
|
314
|
+
const result = { openapi: "3.1.0", info, servers: rootServers, paths, components, "x-wsdl-tsc-services": services };
|
|
315
|
+
if (Object.keys(webhooks).length)
|
|
316
|
+
result.webhooks = webhooks;
|
|
317
|
+
if (Object.keys(tags).length)
|
|
318
|
+
result.tags = Object.keys(tags).sort().map(name => tags[name]);
|
|
319
|
+
if (dialect !== "https://spec.openapis.org/oas/3.1/dialect/base")
|
|
320
|
+
result.jsonSchemaDialect = dialect;
|
|
321
|
+
return stable(result);
|
|
322
|
+
}
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface PipelineOptions {
|
|
|
53
53
|
/**
|
|
54
54
|
* Path to a stream configuration JSON file. Loaded and parsed once before
|
|
55
55
|
* compilation; the parsed value takes precedence over `streamConfig` when
|
|
56
|
-
* both are set.
|
|
56
|
+
* both are set. Unlisted operations retain their buffered API selection.
|
|
57
57
|
*/
|
|
58
58
|
streamConfigFile?: string;
|
|
59
59
|
/**
|
package/dist/pipeline.js
CHANGED
|
@@ -21,6 +21,7 @@ import { resolveCompilerOptions } from "./config.js";
|
|
|
21
21
|
import { emitClientArtifacts, reportCompilationStats, reportOpenApiSuccess, success } from "./util/cli.js";
|
|
22
22
|
import { loadStreamConfigFile } from "./util/streamConfig.js";
|
|
23
23
|
import { applyShapeCatalogs } from "./compiler/shapeResolver.js";
|
|
24
|
+
import { deriveClientName } from "./util/tools.js";
|
|
24
25
|
/**
|
|
25
26
|
* Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI/Gateway/App
|
|
26
27
|
*
|
|
@@ -129,6 +130,7 @@ export async function runGenerationPipeline(opts) {
|
|
|
129
130
|
clientDir: opts.clientOutDir ? path.resolve(opts.clientOutDir) : undefined,
|
|
130
131
|
// Reuse the same imports mode as the TypeScript client/types/utils emitters
|
|
131
132
|
imports: finalCompiler.imports,
|
|
133
|
+
clientClassName: opts.gateway.clientClassName ?? deriveClientName(compiled),
|
|
132
134
|
// Pass catalog for runtime unwrap generation
|
|
133
135
|
catalogFile: path.resolve(opts.catalogOut),
|
|
134
136
|
// Thread flatten flag from OpenAPI options
|
package/docs/agent-skill.md
CHANGED
|
@@ -4,6 +4,12 @@ Standalone agent skill artifact for consumer projects using `@techspokes/typescr
|
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
7
|
+
The skill is a light router to canonical product documentation. Its manual instructions cover input discovery, document selection and generated-file ownership; generation profiles, constructors, mappings, compatibility and transport rules remain in the core guides.
|
|
8
|
+
|
|
9
|
+
Agents prefer the installed package's documentation, which matches the code they are using. The ZIP also includes generated extracts of canonical docs for its recorded release version, including [migration](migration.md#from-11x-to-120), composition, checked generation and streaming. Those extracts are versioned snapshots, not separately authored sources of product truth.
|
|
10
|
+
|
|
11
|
+
Use a matching repository tag when the installed guide is unavailable, and use target-version docs deliberately during upgrades. Live `main` may contain unreleased behavior; a skill for a newer release must not establish capabilities of an older installed package. `references/SOURCE-MAP.json` records the snapshot's version, tag and source sections.
|
|
12
|
+
|
|
7
13
|
Release assets include `typescript-wsdl-client-agent-skill-vX.Y.Z.zip` and `SHA256SUMS`. GitHub artifact provenance attests the ZIP, which contains an installable `typescript-wsdl-client/` skill folder for consumer projects.
|
|
8
14
|
|
|
9
15
|
The skill is for consumer-project agents. It tells agents how to choose `wsdl-tsc` commands, handle generated output boundaries, load reference material, and validate generated code without treating generated files as hand-maintained source.
|
|
@@ -18,8 +24,11 @@ typescript-wsdl-client/
|
|
|
18
24
|
references/
|
|
19
25
|
architecture.md
|
|
20
26
|
consumer-workflows.md
|
|
27
|
+
checked-generation.md
|
|
21
28
|
cli-reference.md
|
|
29
|
+
composition.md
|
|
22
30
|
generated-code.md
|
|
31
|
+
migration.md
|
|
23
32
|
testing.md
|
|
24
33
|
troubleshooting.md
|
|
25
34
|
streaming.md
|
|
@@ -28,7 +37,7 @@ typescript-wsdl-client/
|
|
|
28
37
|
|
|
29
38
|
## Evergreen Content
|
|
30
39
|
|
|
31
|
-
Evergreen content lives under `agent-skill/` and
|
|
40
|
+
Evergreen content lives under `agent-skill/` and supplies stable routing and ownership instructions. Update product contracts in the canonical docs and select their sections through the reference manifest; do not copy those details into hand-maintained skill summaries.
|
|
32
41
|
|
|
33
42
|
The evergreen files are `agent-skill/SKILL.md`, `agent-skill/install.mjs`, `agent-skill/evergreen/architecture.md`, and `agent-skill/evergreen/consumer-workflows.md`.
|
|
34
43
|
|
package/docs/api-reference.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# Programmatic API
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This reference covers public generation, composition, stream and security APIs. Assessment and checked planning are separate diagnostic surfaces; see [checked generation](checked-generation.md#programmatic-planning-and-generation).
|
|
4
4
|
|
|
5
5
|
See the main [README](../README.md) for installation, CLI usage, and project overview.
|
|
6
6
|
|
|
7
|
-
All file-producing APIs automatically apply [TypeScript preambles](generated-code.md#generated-preambles-and-eslint).
|
|
7
|
+
All file-producing APIs automatically apply [TypeScript preambles](generated-code.md#generated-preambles-and-eslint). Adding a preamble does not change an API signature or option; existing scaffold skip/force behavior still applies. Resource and TypeScript file-write failures propagate to callers.
|
|
8
|
+
|
|
9
|
+
Generated-output compatibility is versioned separately from those preambles. Read the [migration guide for the intended version](migration.md), including [1.2.x-to-1.3.0 SOAP binding adoption](migration.md#from-12x-to-130), before regenerating existing clients or changing their selected profile.
|
|
8
10
|
|
|
9
11
|
## compileWsdlToProject
|
|
10
12
|
|
|
@@ -17,6 +19,8 @@ await compileWsdlToProject({
|
|
|
17
19
|
wsdl: "./wsdl/Hotel.wsdl",
|
|
18
20
|
outDir: "./src/services/hotel",
|
|
19
21
|
options: {
|
|
22
|
+
wsdl: "./wsdl/Hotel.wsdl",
|
|
23
|
+
out: "./src/services/hotel",
|
|
20
24
|
imports: "js",
|
|
21
25
|
catalog: true,
|
|
22
26
|
primitive: {
|
|
@@ -38,13 +42,13 @@ await compileWsdlToProject({
|
|
|
38
42
|
declare function compileWsdlToProject(input: {
|
|
39
43
|
wsdl: string;
|
|
40
44
|
outDir: string;
|
|
41
|
-
options?:
|
|
45
|
+
options?: CompilerOptions;
|
|
42
46
|
streamConfigFile?: string;
|
|
43
47
|
streamConfig?: StreamConfig;
|
|
44
48
|
}): Promise<void>;
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
`streamConfigFile` points at
|
|
51
|
+
`streamConfigFile` points at raw JSON parsed by `loadStreamConfigFile`; `streamConfig` accepts normalized metadata from `parseStreamConfig`. The file takes precedence when both are set. Omitting both retains buffered API selection, without a byte-for-byte output guarantee across upgrades. See [Stream Configuration](configuration.md#stream-configuration).
|
|
48
52
|
|
|
49
53
|
### CompilerOptions
|
|
50
54
|
|
|
@@ -120,6 +124,7 @@ interface GenerateOpenAPIOptions {
|
|
|
120
124
|
pathStyle?: "kebab" | "asis" | "lower";
|
|
121
125
|
defaultMethod?: string;
|
|
122
126
|
closedSchemas?: boolean;
|
|
127
|
+
flattenArrayWrappers?: boolean;
|
|
123
128
|
pruneUnusedSchemas?: boolean;
|
|
124
129
|
tagStyle?: "default" | "first" | "service";
|
|
125
130
|
tagsFile?: string;
|
|
@@ -169,10 +174,17 @@ interface GenerateGatewayOptions {
|
|
|
169
174
|
openapiDocument?: any;
|
|
170
175
|
outDir: string;
|
|
171
176
|
clientDir?: string;
|
|
172
|
-
versionSlug
|
|
173
|
-
serviceSlug
|
|
177
|
+
versionSlug: string;
|
|
178
|
+
serviceSlug: string;
|
|
174
179
|
defaultResponseStatusCodes?: number[];
|
|
175
180
|
imports?: "js" | "ts" | "bare";
|
|
181
|
+
clientClassName?: string;
|
|
182
|
+
clientDecoratorName?: string;
|
|
183
|
+
catalogFile?: string;
|
|
184
|
+
emitPlugin?: boolean;
|
|
185
|
+
emitRuntime?: boolean;
|
|
186
|
+
stubHandlers?: boolean;
|
|
187
|
+
flattenArrayWrappers?: boolean;
|
|
176
188
|
}
|
|
177
189
|
```
|
|
178
190
|
|
|
@@ -231,8 +243,10 @@ await runGenerationPipeline({
|
|
|
231
243
|
|
|
232
244
|
```typescript
|
|
233
245
|
declare function runGenerationPipeline(opts: PipelineOptions): Promise<{
|
|
234
|
-
compiled:
|
|
246
|
+
compiled: any;
|
|
235
247
|
openapiDoc?: any;
|
|
248
|
+
checked?: CheckedInventory;
|
|
249
|
+
planning?: CheckedComponentPlan;
|
|
236
250
|
}>;
|
|
237
251
|
```
|
|
238
252
|
|
|
@@ -240,8 +254,10 @@ declare function runGenerationPipeline(opts: PipelineOptions): Promise<{
|
|
|
240
254
|
|
|
241
255
|
```typescript
|
|
242
256
|
interface PipelineOptions {
|
|
257
|
+
model?: "legacy" | "checked-components-v1";
|
|
258
|
+
checkedComponents?: Partial<CheckedComponentOptions>;
|
|
243
259
|
wsdl: string;
|
|
244
|
-
catalogOut
|
|
260
|
+
catalogOut?: string;
|
|
245
261
|
clientOutDir?: string;
|
|
246
262
|
compiler?: Partial<CompilerOptions>;
|
|
247
263
|
openapi?: Omit<GenerateOpenAPIOptions, "wsdl" | "catalogFile" | "compiledCatalog"> & {
|
|
@@ -249,7 +265,19 @@ interface PipelineOptions {
|
|
|
249
265
|
};
|
|
250
266
|
gateway?: Omit<GenerateGatewayOptions, "openapiFile" | "openapiDocument"> & {
|
|
251
267
|
outDir?: string;
|
|
268
|
+
versionSlug: string;
|
|
269
|
+
serviceSlug: string;
|
|
270
|
+
};
|
|
271
|
+
app?: {
|
|
272
|
+
appDir: string;
|
|
273
|
+
openapiMode?: "copy" | "reference";
|
|
274
|
+
force?: boolean;
|
|
275
|
+
host?: string;
|
|
276
|
+
port?: number;
|
|
277
|
+
prefix?: string;
|
|
278
|
+
securityConfigFile?: string;
|
|
252
279
|
};
|
|
280
|
+
test?: {testDir: string; force?: boolean; flattenArrayWrappers?: boolean};
|
|
253
281
|
streamConfigFile?: string;
|
|
254
282
|
streamConfig?: StreamConfig;
|
|
255
283
|
}
|
|
@@ -257,6 +285,75 @@ interface PipelineOptions {
|
|
|
257
285
|
|
|
258
286
|
`streamConfigFile` and `streamConfig` are threaded through the compile step and into every downstream emitter via `catalog.json`. Relative paths in `shapeCatalogs` resolve against the stream-config file's directory when `streamConfigFile` is used, otherwise against the WSDL's directory.
|
|
259
287
|
|
|
288
|
+
Legacy calls require `catalogOut` at runtime. Checked component calls use `model: "checked-components-v1"` and optional `checkedComponents`; they return checked inventory/planning instead of a legacy catalog contract. See [the complete programmatic checked example](checked-generation.md#programmatic-planning-and-generation) for source policy and planning before generation. Checked RPC outputs remain opaque strings and buffered checked values; legacy stream configuration does not activate checked streaming.
|
|
289
|
+
|
|
290
|
+
## runMultiServicePipeline
|
|
291
|
+
|
|
292
|
+
Generate independent legacy clients and service routers, then publish one aggregate gateway plugin and combined OpenAPI JSON document. The [multi-service guide](gateway-guide.md#multi-service-setup) supplies a complete configuration with explicit, disjoint outputs:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import {loadMultiServiceConfig, runMultiServicePipeline} from "@techspokes/typescript-wsdl-client";
|
|
296
|
+
|
|
297
|
+
const options = await loadMultiServiceConfig("./services.json");
|
|
298
|
+
const result = await runMultiServicePipeline(options);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
interface MultiServiceEntry {
|
|
303
|
+
key: string;
|
|
304
|
+
generation: PipelineOptions;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface MultiServicePipelineOptions {
|
|
308
|
+
services: readonly MultiServiceEntry[];
|
|
309
|
+
openapi: OpenApiCompositionOptions & {outFile: string};
|
|
310
|
+
gateway: {
|
|
311
|
+
outDir: string;
|
|
312
|
+
imports?: "js" | "ts" | "bare";
|
|
313
|
+
openapiPath?: string | false;
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
interface MultiServicePipelineResult {
|
|
318
|
+
openapiDoc: ComposedOpenApiDocument;
|
|
319
|
+
services: Record<string, {compiled: CompiledCatalog; clientDir: string; gatewayDir: string}>;
|
|
320
|
+
pluginFile: string;
|
|
321
|
+
openapiFile: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
declare function runMultiServicePipeline(options: MultiServicePipelineOptions): Promise<MultiServicePipelineResult>;
|
|
325
|
+
declare function loadMultiServiceConfig(file: string): Promise<MultiServicePipelineOptions>;
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`loadMultiServiceConfig` resolves filesystem paths against the JSON configuration file; direct API callers supply paths relative to their process working directory. Every service needs `wsdl`, `catalogOut`, `clientOutDir`, `openapi.outFile`, `openapi.basePath`, `gateway.outDir`, `gateway.serviceSlug` and `gateway.versionSlug`. Service keys use unique lowercase letters, digits and hyphens, starting with a letter; gateway service slugs and decorators must remain unique after normalization.
|
|
329
|
+
|
|
330
|
+
The API preserves each source's compiler and stream settings, generated client, catalog and SOAP dispatch. It refuses overlapping output ownership, duplicate final paths, lost operation routes, and unsupported app/test/checked settings. A failed later service can leave earlier service artifacts, while the previously published aggregate spec/plugin pair remains intact.
|
|
331
|
+
|
|
332
|
+
The generated aggregate plugin exports `createClients`, `MultiServiceClientOptions`, `MultiServiceClients` and `openapi`. `createClients` requires exactly the service keys, each with its generated client's constructor options, including an explicit runtime `source`; register the plugin with `{clients}`. Prefixes come from each service's `openapi.basePath`, and the aggregate refuses runtime Fastify prefixes.
|
|
333
|
+
|
|
334
|
+
## composeOpenApiDocuments
|
|
335
|
+
|
|
336
|
+
Compose existing OpenAPI 3.1 documents without generating clients or routers:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
interface OpenApiCompositionEntry {key: string; document: unknown}
|
|
340
|
+
interface OpenApiCompositionOptions {
|
|
341
|
+
title?: string;
|
|
342
|
+
version?: string;
|
|
343
|
+
description?: string;
|
|
344
|
+
servers?: readonly string[];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
declare function composeOpenApiDocuments(
|
|
348
|
+
entries: readonly OpenApiCompositionEntry[],
|
|
349
|
+
options?: OpenApiCompositionOptions,
|
|
350
|
+
): ComposedOpenApiDocument;
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Components become `serviceKey__ComponentName`, while operation IDs and tags become `serviceKey.originalName`. The merger rewrites supported reference-bearing positions, link operation targets, discriminator targets and security scheme names, preserving service-local security and server semantics. It also qualifies the known `x-wsdl-tsc-stream.itemSchema` reference so clients can discover the stream record contract.
|
|
354
|
+
|
|
355
|
+
Literal examples and unrelated extension payloads remain data. Duplicate paths, unresolved local references, relative external references and unsupported schema resource/anchor contexts are refused.
|
|
356
|
+
|
|
260
357
|
## Stream Configuration Helpers
|
|
261
358
|
|
|
262
359
|
These exports back the `--stream-config` CLI flag and are also usable directly in programmatic flows.
|
|
@@ -415,4 +512,25 @@ const streamOp = compiled.operations.find((op) => op.stream);
|
|
|
415
512
|
console.log(streamOp?.stream?.mediaType); // "application/x-ndjson" by default
|
|
416
513
|
```
|
|
417
514
|
|
|
418
|
-
|
|
515
|
+
### Generated stream client contract
|
|
516
|
+
|
|
517
|
+
Generated `operations.ts` exports `StreamOperationResponse<RecordType>` and `StreamCallOptions`; `client.ts` also re-exports `StreamCallOptions`. Each stream-configured operation accepts `(args, options?)` and returns `Promise<StreamOperationResponse<RecordType>>` with `records: AsyncIterable<RecordType>`:
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
interface StreamCallOptions {
|
|
521
|
+
signal?: AbortSignal;
|
|
522
|
+
timeoutMs?: number;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
interface StreamOperationResponse<RecordType, HeadersType = Record<string, unknown>> {
|
|
526
|
+
records: AsyncIterable<RecordType>;
|
|
527
|
+
headers: HeadersType;
|
|
528
|
+
requestRaw?: string;
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Records use the public legacy DTO mapping: flattened attributes, projected scalar values, typed simple content, XML lists and declared repeated-element arrays. Each record is mapped after its closing XML tag; the low-level parser still returns lexical values and attribute bags. Gateway wrapper flattening applies per record when enabled, and checked value shapes are unchanged.
|
|
533
|
+
|
|
534
|
+
Only stream-enabled generated clients add constructor `streamRequestOptions?: IExOptions` from `soap`. These HTTP options are separate from WSDL loading and support a verified HTTPS agent, HTTP timeout and signal; `options.httpClient` must implement `requestStream`, or the standard node-soap HTTP client is used with `options.request`. Stream calls honor the current endpoint override, static headers and supported `ISecurity` hooks; see [transport support and refusals](gateway-guide.md#streaming-transport-settings).
|
|
535
|
+
|
|
536
|
+
Per-call `timeoutMs` is a positive integer deadline for the upstream HTTP exchange and record consumption, starting after metadata initialization. A per-call signal already aborted rejects before metadata initialization. Exhaust or return the iterator to release transport resources; generated gateways connect downstream disconnects to that signal before waiting for SOAP headers and throughout delivery. Dates retain the existing type-level mapping, and `bigint` has no additional JSON encoding convention.
|
package/docs/checked-gateway.md
CHANGED
|
@@ -4,8 +4,18 @@ Generation offers legacy, the default, and opt-in checked components. Start with
|
|
|
4
4
|
|
|
5
5
|
## Component service generation
|
|
6
6
|
|
|
7
|
+
SOAP 1.2 document/literal operations use the selected binding's envelope namespace and `application/soap+xml` action parameter. Select service, port and binding through the existing checked configuration when multiple ports are available; a SOAP version flag is unnecessary. SOAP 1.2 artifacts have their own compatibility scope, so regenerate matched clients, capsules and gateways together. Existing SOAP 1.1 and direct-runtime artifacts retain their contracts.
|
|
8
|
+
|
|
9
|
+
Generated SOAP 1.2 clients use the additive `checkedComponentRuntimeV2` package-root binding with protocol `checked-component-soap12-runtime-v1`. Existing SOAP 1.1 clients keep `checkedComponentRuntimeV1` and `checked-component-runtime-v2`, including their TypeScript injection contract. The older binding refuses SOAP 1.2 artifacts before SOAP dispatch.
|
|
10
|
+
|
|
11
|
+
SOAP 1.2 faults retain code, nested subcodes, language-tagged reasons and optional Node/Role metadata. Declared application detail uses the existing native mapping and gateway fault validation. A valid protocol fault without declared application detail carries fault metadata without a typed application value; the client retains original response bytes. Gateways return HTTP 502 for upstream faults, including SOAP Sender faults received on HTTP 400. SOAP 1.2 protocol rules follow [the messaging framework](https://www.w3.org/TR/soap12-part1/#soapfault) and [HTTP binding](https://www.w3.org/TR/soap12-part2/#soapinhttp).
|
|
12
|
+
|
|
13
|
+
SOAP 1.2 header processing uses the default ultimate receiver and next roles. Unknown mandatory blocks targeted at this receiver refuse; other-role and optional blocks may be ignored. The `mustUnderstand` and `relay` attributes accept SOAP 1.2 Boolean lexical forms. This does not enable outgoing application headers, WS-Security, streaming or RPC/encoded SOAP 1.2.
|
|
14
|
+
|
|
7
15
|
The additive `checked-components-v1` mode uses the same command family and native owners for qualified structured operations, imported schema snapshots, attributes, recursive records, simple content and ordered values. It preserves the shipped direct runtime and legacy defaults. Source, mapping, artifact and transport checks remain authoritative; unsupported schema or security features refuse rather than disappearing from a successful service claim. This is a qualified component fragment, not universal XSD, streaming or provider acceptance.
|
|
8
16
|
|
|
17
|
+
The [bounded SOAP 1.1 RPC/encoded profile](supported-patterns.md#checked-soap-11-rpc-string-profile) adds source-owned scalar and flat Boolean record parts without document element roots. Component responses additionally accept exact Latin1 with encoding agreement. See the [NOAA qualification](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/docs/noaa-ndfd-qualification.md) for native restrictions, opaque string/reference results and separate owned-server/live evidence limits.
|
|
18
|
+
|
|
9
19
|
For a supported local WSDL or HTTP(S) root URL with one service/port/binding candidate, omit `--checked-config`. The planner selects all operations of that binding and derives supported mappings through the existing native owners. Local sources default to their containing directory; remote sources authorize their same-origin declared dependency graph. Canonical containment, redirect permission checks and native qualification still apply.
|
|
10
20
|
|
|
11
21
|
```bash
|