@techspokes/typescript-wsdl-client 0.11.6 → 0.13.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 +2 -1
- package/dist/client/generateClient.d.ts.map +1 -1
- package/dist/client/generateClient.js +9 -1
- package/dist/client/generateOperations.d.ts.map +1 -1
- package/dist/client/generateOperations.js +11 -1
- package/dist/client/generateTypes.d.ts.map +1 -1
- package/dist/client/generateTypes.js +49 -6
- package/dist/compiler/schemaCompiler.d.ts +7 -0
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +101 -11
- package/dist/openapi/generatePaths.d.ts.map +1 -1
- package/dist/openapi/generatePaths.js +5 -1
- package/dist/openapi/generateSchemas.d.ts.map +1 -1
- package/dist/openapi/generateSchemas.js +29 -5
- package/docs/architecture.md +2 -0
- package/docs/concepts.md +2 -0
- package/docs/generated-code.md +31 -3
- package/docs/testing.md +10 -9
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Generate type-safe TypeScript SOAP clients, OpenAPI 3.1 specs, and production-re
|
|
|
13
13
|
- OpenAPI 3.1 specs that mirror your TypeScript types
|
|
14
14
|
- REST gateway over SOAP with automatic request/response transformation
|
|
15
15
|
- CI-friendly deterministic output for safe regeneration in version control
|
|
16
|
+
- WSDL/XSD documentation propagated into generated comments and OpenAPI descriptions
|
|
16
17
|
|
|
17
18
|
## Quick Start
|
|
18
19
|
|
|
@@ -57,7 +58,7 @@ curl -X POST http://localhost:3000/get-weather-information \
|
|
|
57
58
|
|
|
58
59
|
| Output | Files | Purpose |
|
|
59
60
|
|--------|-------|---------|
|
|
60
|
-
| TypeScript Client | client.ts, types.ts, utils.ts | Typed SOAP operations |
|
|
61
|
+
| TypeScript Client | client.ts, types.ts, utils.ts, operations.ts | Typed SOAP operations and mockable operations interface |
|
|
61
62
|
| OpenAPI 3.1 Spec | openapi.json or .yaml | REST API documentation |
|
|
62
63
|
| Fastify Gateway | plugin.ts, routes/, schemas/ | Production REST handlers |
|
|
63
64
|
| Catalog | catalog.json | Compiled WSDL (debuggable, cacheable) |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateClient.d.ts","sourceRoot":"","sources":["../../src/client/generateClient.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAInE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"generateClient.d.ts","sourceRoot":"","sources":["../../src/client/generateClient.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAInE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,QA+WxE"}
|
|
@@ -34,6 +34,12 @@ import { error, warn } from "../util/cli.js";
|
|
|
34
34
|
*/
|
|
35
35
|
export function generateClient(outFile, compiled) {
|
|
36
36
|
const isValidIdent = (name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
37
|
+
const normalizeDocLines = (text) => String(text)
|
|
38
|
+
.replace(/\r\n?/g, "\n")
|
|
39
|
+
.split("\n")
|
|
40
|
+
.map(line => line.trim())
|
|
41
|
+
.filter(Boolean)
|
|
42
|
+
.map(line => line.replace(/\*\//g, "*\\/"));
|
|
37
43
|
const reserved = new Set([
|
|
38
44
|
"break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete",
|
|
39
45
|
"do", "else", "enum", "export", "extends", "false", "finally", "for", "function", "if",
|
|
@@ -61,11 +67,13 @@ export function generateClient(outFile, compiled) {
|
|
|
61
67
|
const inTs = inTypeName ? `T.${inTypeName}` : `any`;
|
|
62
68
|
const outTs = outTypeName ? `T.${outTypeName}` : `any`;
|
|
63
69
|
const secHints = Array.isArray(op.security) && op.security.length ? op.security : [];
|
|
70
|
+
const opDocLines = op.doc ? normalizeDocLines(op.doc) : [];
|
|
71
|
+
const opDocStr = opDocLines.length ? `\n *\n${opDocLines.map(line => ` * ${line}`).join("\n")}` : "";
|
|
64
72
|
const secHintsStr = secHints.length ? `\n *\n * Security (WSDL policy hint): ${secHints.join(", ")}` : "";
|
|
65
73
|
const methodTemplate = `
|
|
66
74
|
|
|
67
75
|
/**
|
|
68
|
-
* Calls the ${m} operation of the ${clientName}.${secHintsStr}
|
|
76
|
+
* Calls the ${m} operation of the ${clientName}.${opDocStr}${secHintsStr}
|
|
69
77
|
*
|
|
70
78
|
* @param args - The request arguments for the ${m} operation.
|
|
71
79
|
* @returns A promise resolving to the operation response containing data, headers, response raw XML, and request raw XML.
|
|
@@ -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;AAIrE;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,
|
|
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,CAsEnF"}
|
|
@@ -22,6 +22,12 @@ export function generateOperations(outFile, compiled) {
|
|
|
22
22
|
const ext = compiled.options.imports ?? "bare";
|
|
23
23
|
const suffix = ext === "bare" ? "" : `.${ext}`;
|
|
24
24
|
const clientName = deriveClientName(compiled);
|
|
25
|
+
const normalizeDocLines = (text) => String(text)
|
|
26
|
+
.replace(/\r\n?/g, "\n")
|
|
27
|
+
.split("\n")
|
|
28
|
+
.map(line => line.trim())
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.map(line => line.replace(/\*\//g, "*\\/"));
|
|
25
31
|
// Collect type names used in method signatures for the import statement
|
|
26
32
|
const importedTypes = new Set();
|
|
27
33
|
const methods = [];
|
|
@@ -37,7 +43,11 @@ export function generateOperations(outFile, compiled) {
|
|
|
37
43
|
importedTypes.add(inTypeName);
|
|
38
44
|
if (outTypeName)
|
|
39
45
|
importedTypes.add(outTypeName);
|
|
40
|
-
|
|
46
|
+
const docLines = op.doc ? normalizeDocLines(op.doc) : [];
|
|
47
|
+
const docBlock = docLines.length > 0
|
|
48
|
+
? ` /**\n${docLines.map(line => ` * ${line}`).join("\n")}\n */\n`
|
|
49
|
+
: "";
|
|
50
|
+
methods.push(`${docBlock} ${op.name}(\n` +
|
|
41
51
|
` args: ${inTs}\n` +
|
|
42
52
|
` ): Promise<{ response: ${outTs}; headers: unknown }>;\n`);
|
|
43
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateTypes.d.ts","sourceRoot":"","sources":["../../src/client/generateTypes.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAGjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"generateTypes.d.ts","sourceRoot":"","sources":["../../src/client/generateTypes.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAGjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,QAyLvE"}
|
|
@@ -35,6 +35,30 @@ import { error } from "../util/cli.js";
|
|
|
35
35
|
*/
|
|
36
36
|
export function generateTypes(outFile, compiled) {
|
|
37
37
|
const lines = [];
|
|
38
|
+
const normalizeDocLines = (text) => String(text)
|
|
39
|
+
.replace(/\r\n?/g, "\n")
|
|
40
|
+
.split("\n")
|
|
41
|
+
.map(line => line.trim())
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.map(line => line.replace(/\*\//g, "*\\/"));
|
|
44
|
+
const emitDocBlock = (indent, docText, xsdTag) => {
|
|
45
|
+
if (!docText && !xsdTag) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
lines.push(`${indent}/**`);
|
|
49
|
+
if (docText) {
|
|
50
|
+
for (const line of normalizeDocLines(docText)) {
|
|
51
|
+
lines.push(`${indent} * ${line}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (xsdTag) {
|
|
55
|
+
if (docText) {
|
|
56
|
+
lines.push(`${indent} *`);
|
|
57
|
+
}
|
|
58
|
+
lines.push(`${indent} * @xsd ${xsdTag}`);
|
|
59
|
+
}
|
|
60
|
+
lines.push(`${indent} */`);
|
|
61
|
+
};
|
|
38
62
|
// Convenience lookups
|
|
39
63
|
const typeNames = new Set(compiled.types.map((t) => t.name));
|
|
40
64
|
const isValidIdent = (name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
@@ -45,8 +69,14 @@ export function generateTypes(outFile, compiled) {
|
|
|
45
69
|
// sort aliases by name to ensure consistent order
|
|
46
70
|
compiled.aliases.sort((a, b) => a.name.localeCompare(b.name));
|
|
47
71
|
for (const a of compiled.aliases) {
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
if (a.doc) {
|
|
73
|
+
emitDocBlock("", a.doc, a.jsdoc);
|
|
74
|
+
lines.push(`export type ${a.name} = ${a.tsType};`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const ann = a.jsdoc ? `/** @xsd ${a.jsdoc} */\n` : "";
|
|
78
|
+
lines.push(`${ann}export type ${a.name} = ${a.tsType};`);
|
|
79
|
+
}
|
|
50
80
|
lines.push("");
|
|
51
81
|
}
|
|
52
82
|
//
|
|
@@ -64,6 +94,9 @@ export function generateTypes(outFile, compiled) {
|
|
|
64
94
|
typeNames.has(e.tsType));
|
|
65
95
|
const isSimpleContentExtension = !complexBase && (t.elems?.length || 0) === 1 && valueElems.length === 1;
|
|
66
96
|
const baseName = complexBase ?? (isSimpleContentExtension ? valueElems[0].tsType : undefined);
|
|
97
|
+
if (t.doc) {
|
|
98
|
+
emitDocBlock("", t.doc);
|
|
99
|
+
}
|
|
67
100
|
// Header: extend base type if applicable
|
|
68
101
|
if (baseName) {
|
|
69
102
|
lines.push(`export interface ${t.name} extends ${baseName} {`);
|
|
@@ -108,9 +141,14 @@ export function generateTypes(outFile, compiled) {
|
|
|
108
141
|
type: a.declaredType,
|
|
109
142
|
use: a.use || "optional",
|
|
110
143
|
};
|
|
111
|
-
const ann = ` /** @xsd ${JSON.stringify(annObj)} */`;
|
|
112
144
|
lines.push("");
|
|
113
|
-
|
|
145
|
+
if (a.doc) {
|
|
146
|
+
emitDocBlock(" ", a.doc, JSON.stringify(annObj));
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const ann = ` /** @xsd ${JSON.stringify(annObj)} */`;
|
|
150
|
+
lines.push(ann);
|
|
151
|
+
}
|
|
114
152
|
lines.push(` ${emitPropName(a.name)}${opt}: ${a.tsType};`);
|
|
115
153
|
}
|
|
116
154
|
//
|
|
@@ -153,9 +191,14 @@ export function generateTypes(outFile, compiled) {
|
|
|
153
191
|
if ((e.name === "$value") && (1 < elementsToEmit.length)) {
|
|
154
192
|
lines.push("");
|
|
155
193
|
}
|
|
156
|
-
const ann = ` /** @xsd ${JSON.stringify(annObj)} */`;
|
|
157
194
|
lines.push("");
|
|
158
|
-
|
|
195
|
+
if (e.doc) {
|
|
196
|
+
emitDocBlock(" ", e.doc, JSON.stringify(annObj));
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const ann = ` /** @xsd ${JSON.stringify(annObj)} */`;
|
|
200
|
+
lines.push(ann);
|
|
201
|
+
}
|
|
159
202
|
lines.push(` ${emitPropName(e.name)}${opt}: ${e.tsType}${arr};`);
|
|
160
203
|
}
|
|
161
204
|
lines.push("}");
|
|
@@ -43,6 +43,7 @@ export type CompiledType = {
|
|
|
43
43
|
tsType: string;
|
|
44
44
|
use?: "required" | "optional";
|
|
45
45
|
declaredType: string;
|
|
46
|
+
doc?: string;
|
|
46
47
|
}>;
|
|
47
48
|
elems: Array<{
|
|
48
49
|
name: string;
|
|
@@ -51,14 +52,17 @@ export type CompiledType = {
|
|
|
51
52
|
max: number | "unbounded";
|
|
52
53
|
nillable?: boolean;
|
|
53
54
|
declaredType: string;
|
|
55
|
+
doc?: string;
|
|
54
56
|
}>;
|
|
55
57
|
jsdoc?: string;
|
|
58
|
+
doc?: string;
|
|
56
59
|
base?: string;
|
|
57
60
|
localAttrs?: Array<{
|
|
58
61
|
name: string;
|
|
59
62
|
tsType: string;
|
|
60
63
|
use?: "required" | "optional";
|
|
61
64
|
declaredType: string;
|
|
65
|
+
doc?: string;
|
|
62
66
|
}>;
|
|
63
67
|
localElems?: Array<{
|
|
64
68
|
name: string;
|
|
@@ -67,6 +71,7 @@ export type CompiledType = {
|
|
|
67
71
|
max: number | "unbounded";
|
|
68
72
|
nillable?: boolean;
|
|
69
73
|
declaredType: string;
|
|
74
|
+
doc?: string;
|
|
70
75
|
}>;
|
|
71
76
|
};
|
|
72
77
|
/**
|
|
@@ -85,6 +90,7 @@ export type CompiledAlias = {
|
|
|
85
90
|
tsType: string;
|
|
86
91
|
declared: string;
|
|
87
92
|
jsdoc?: string;
|
|
93
|
+
doc?: string;
|
|
88
94
|
};
|
|
89
95
|
/**
|
|
90
96
|
* Complete compiled catalog with all types, aliases, operations and metadata
|
|
@@ -117,6 +123,7 @@ export type CompiledCatalog = {
|
|
|
117
123
|
security?: string[];
|
|
118
124
|
inputTypeName?: string;
|
|
119
125
|
outputTypeName?: string;
|
|
126
|
+
doc?: string;
|
|
120
127
|
}>;
|
|
121
128
|
wsdlTargetNS: string;
|
|
122
129
|
wsdlUri: string;
|
|
@@ -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;AAKzD;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;QAC9B,YAAY,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"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"}
|
|
@@ -92,6 +92,57 @@ function collectSecurityFromPolicyNodes(policyNodes) {
|
|
|
92
92
|
visit(p);
|
|
93
93
|
return Array.from(found);
|
|
94
94
|
}
|
|
95
|
+
function extractNodeText(node) {
|
|
96
|
+
if (node == null) {
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
if (typeof node === "string" || typeof node === "number" || typeof node === "boolean") {
|
|
100
|
+
return String(node);
|
|
101
|
+
}
|
|
102
|
+
if (Array.isArray(node)) {
|
|
103
|
+
return node.map(extractNodeText).join(" ");
|
|
104
|
+
}
|
|
105
|
+
if (typeof node === "object") {
|
|
106
|
+
const parts = [];
|
|
107
|
+
if (node["#text"] != null) {
|
|
108
|
+
parts.push(extractNodeText(node["#text"]));
|
|
109
|
+
}
|
|
110
|
+
for (const [k, v] of Object.entries(node)) {
|
|
111
|
+
if ("#text" === k || k.startsWith("@_")) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
parts.push(extractNodeText(v));
|
|
115
|
+
}
|
|
116
|
+
return parts.join(" ");
|
|
117
|
+
}
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
function normalizeDocText(text) {
|
|
121
|
+
const compact = text
|
|
122
|
+
.replace(/\r\n?/g, "\n")
|
|
123
|
+
.split("\n")
|
|
124
|
+
.map(line => line.trim())
|
|
125
|
+
.filter(Boolean)
|
|
126
|
+
.join(" ")
|
|
127
|
+
.replace(/\s+/g, " ")
|
|
128
|
+
.trim();
|
|
129
|
+
return compact || undefined;
|
|
130
|
+
}
|
|
131
|
+
function extractDirectDocumentation(node) {
|
|
132
|
+
const docs = getChildrenWithLocalName(node, "documentation");
|
|
133
|
+
const merged = docs
|
|
134
|
+
.map(extractNodeText)
|
|
135
|
+
.join(" ");
|
|
136
|
+
return normalizeDocText(merged);
|
|
137
|
+
}
|
|
138
|
+
function extractAnnotationDocumentation(node) {
|
|
139
|
+
const annotations = getChildrenWithLocalName(node, "annotation");
|
|
140
|
+
const docs = annotations.flatMap(a => getChildrenWithLocalName(a, "documentation"));
|
|
141
|
+
const merged = docs
|
|
142
|
+
.map(extractNodeText)
|
|
143
|
+
.join(" ");
|
|
144
|
+
return normalizeDocText(merged);
|
|
145
|
+
}
|
|
95
146
|
/**
|
|
96
147
|
* Compile a WSDL catalog into an internal representation (CompiledCatalog).
|
|
97
148
|
* Steps:
|
|
@@ -172,7 +223,14 @@ export function compileCatalog(cat, options) {
|
|
|
172
223
|
return present;
|
|
173
224
|
}
|
|
174
225
|
const { tsType, declared, jsdoc } = compileSimpleTypeNode(sNode, schemaNS, prefixes);
|
|
175
|
-
const alias = {
|
|
226
|
+
const alias = {
|
|
227
|
+
name: pascal(name),
|
|
228
|
+
ns: schemaNS,
|
|
229
|
+
tsType,
|
|
230
|
+
declared,
|
|
231
|
+
jsdoc,
|
|
232
|
+
doc: extractAnnotationDocumentation(sNode),
|
|
233
|
+
};
|
|
176
234
|
aliasMap.set(key, alias);
|
|
177
235
|
return alias;
|
|
178
236
|
}
|
|
@@ -221,6 +279,7 @@ export function compileCatalog(cat, options) {
|
|
|
221
279
|
}
|
|
222
280
|
inProgress.add(rawKey);
|
|
223
281
|
const outName = pascal(name);
|
|
282
|
+
const typeDoc = extractAnnotationDocumentation(cnode);
|
|
224
283
|
const key = `${schemaNS}|${outName}`;
|
|
225
284
|
// hoisted helpers for merging and collecting
|
|
226
285
|
const mergeAttrs = (into, list) => {
|
|
@@ -267,7 +326,8 @@ export function compileCatalog(cat, options) {
|
|
|
267
326
|
name: an,
|
|
268
327
|
tsType: r.tsType,
|
|
269
328
|
use: a["@_use"] === "required" ? "required" : "optional",
|
|
270
|
-
declaredType: r.declared
|
|
329
|
+
declaredType: r.declared,
|
|
330
|
+
doc: extractAnnotationDocumentation(a),
|
|
271
331
|
});
|
|
272
332
|
}
|
|
273
333
|
else {
|
|
@@ -279,7 +339,8 @@ export function compileCatalog(cat, options) {
|
|
|
279
339
|
name: an,
|
|
280
340
|
tsType: r.tsType,
|
|
281
341
|
use: a["@_use"] === "required" ? "required" : "optional",
|
|
282
|
-
declaredType: r.declared
|
|
342
|
+
declaredType: r.declared,
|
|
343
|
+
doc: extractAnnotationDocumentation(a),
|
|
283
344
|
});
|
|
284
345
|
}
|
|
285
346
|
}
|
|
@@ -310,18 +371,35 @@ export function compileCatalog(cat, options) {
|
|
|
310
371
|
min,
|
|
311
372
|
max,
|
|
312
373
|
nillable,
|
|
313
|
-
declaredType: `{${schemaNS}}${rec.name}
|
|
374
|
+
declaredType: `{${schemaNS}}${rec.name}`,
|
|
375
|
+
doc: extractAnnotationDocumentation(e),
|
|
314
376
|
});
|
|
315
377
|
}
|
|
316
378
|
else if (inlineSimple) {
|
|
317
379
|
const r = compileSimpleTypeNode(inlineSimple, schemaNS, prefixes);
|
|
318
|
-
out.push({
|
|
380
|
+
out.push({
|
|
381
|
+
name: propName || nameOrRef,
|
|
382
|
+
tsType: r.tsType,
|
|
383
|
+
min,
|
|
384
|
+
max,
|
|
385
|
+
nillable,
|
|
386
|
+
declaredType: r.declared,
|
|
387
|
+
doc: extractAnnotationDocumentation(e),
|
|
388
|
+
});
|
|
319
389
|
}
|
|
320
390
|
else {
|
|
321
391
|
const t = e["@_type"] || e["@_ref"];
|
|
322
392
|
const q = t ? resolveQName(t, schemaNS, prefixes) : { ns: XS, local: "string" };
|
|
323
393
|
const r = resolveTypeRef(q, schemaNS, prefixes);
|
|
324
|
-
out.push({
|
|
394
|
+
out.push({
|
|
395
|
+
name: propName || nameOrRef,
|
|
396
|
+
tsType: r.tsType,
|
|
397
|
+
min,
|
|
398
|
+
max,
|
|
399
|
+
nillable,
|
|
400
|
+
declaredType: r.declared,
|
|
401
|
+
doc: extractAnnotationDocumentation(e),
|
|
402
|
+
});
|
|
325
403
|
}
|
|
326
404
|
}
|
|
327
405
|
// recurse into nested compositor groups
|
|
@@ -341,6 +419,9 @@ export function compileCatalog(cat, options) {
|
|
|
341
419
|
const newElems = collectParticles(outName, cnode);
|
|
342
420
|
mergeAttrs(present.attrs, newAttrs);
|
|
343
421
|
mergeElems(present.elems, newElems);
|
|
422
|
+
if (!present.doc && typeDoc) {
|
|
423
|
+
present.doc = typeDoc;
|
|
424
|
+
}
|
|
344
425
|
// Remove from inProgress since we're done with this cycle
|
|
345
426
|
inProgress.delete(rawKey);
|
|
346
427
|
return present;
|
|
@@ -379,6 +460,7 @@ export function compileCatalog(cat, options) {
|
|
|
379
460
|
ns: schemaNS,
|
|
380
461
|
attrs,
|
|
381
462
|
elems,
|
|
463
|
+
doc: typeDoc,
|
|
382
464
|
base: baseName,
|
|
383
465
|
localAttrs: locals,
|
|
384
466
|
localElems
|
|
@@ -409,7 +491,7 @@ export function compileCatalog(cat, options) {
|
|
|
409
491
|
declaredType: r.declared,
|
|
410
492
|
}]);
|
|
411
493
|
mergeAttrs(attrs, collectAttributes(scNode));
|
|
412
|
-
const result = { name: outName, ns: schemaNS, attrs, elems };
|
|
494
|
+
const result = { name: outName, ns: schemaNS, attrs, elems, doc: typeDoc };
|
|
413
495
|
compiledMap.set(key, result);
|
|
414
496
|
inProgress.delete(rawKey);
|
|
415
497
|
return result;
|
|
@@ -424,7 +506,7 @@ export function compileCatalog(cat, options) {
|
|
|
424
506
|
// Attributes + particles
|
|
425
507
|
mergeAttrs(attrs, collectAttributes(cnode));
|
|
426
508
|
mergeElems(elems, collectParticles(outName, cnode));
|
|
427
|
-
const result = { name: outName, ns: schemaNS, attrs, elems };
|
|
509
|
+
const result = { name: outName, ns: schemaNS, attrs, elems, doc: typeDoc };
|
|
428
510
|
compiledMap.set(key, result);
|
|
429
511
|
inProgress.delete(rawKey);
|
|
430
512
|
return result;
|
|
@@ -432,6 +514,7 @@ export function compileCatalog(cat, options) {
|
|
|
432
514
|
// Helper: compile a global element into a surface type (wrapper)
|
|
433
515
|
function compileElementAsType(name, enode, schemaNS, prefixes) {
|
|
434
516
|
const outName = pascal(name);
|
|
517
|
+
const elementDoc = extractAnnotationDocumentation(enode);
|
|
435
518
|
const key = `${schemaNS}|${outName}`;
|
|
436
519
|
const present = compiledMap.get(key);
|
|
437
520
|
if (present)
|
|
@@ -448,6 +531,7 @@ export function compileCatalog(cat, options) {
|
|
|
448
531
|
ns: schemaNS,
|
|
449
532
|
attrs: [],
|
|
450
533
|
elems: [{ name: "$value", tsType: r.tsType, min: 0, max: 1, nillable: false, declaredType: r.declared }],
|
|
534
|
+
doc: elementDoc,
|
|
451
535
|
};
|
|
452
536
|
compiledMap.set(key, t);
|
|
453
537
|
return t;
|
|
@@ -470,6 +554,7 @@ export function compileCatalog(cat, options) {
|
|
|
470
554
|
nillable: false,
|
|
471
555
|
declaredType: label
|
|
472
556
|
}],
|
|
557
|
+
doc: elementDoc,
|
|
473
558
|
};
|
|
474
559
|
compiledMap.set(key, t);
|
|
475
560
|
return t;
|
|
@@ -485,10 +570,13 @@ export function compileCatalog(cat, options) {
|
|
|
485
570
|
const existingAlias = aliasMap.get(aliasKey);
|
|
486
571
|
const declared = `{${base.ns}}${base.name}`;
|
|
487
572
|
if (!existingAlias) {
|
|
488
|
-
aliasMap.set(aliasKey, { name: outName, ns: schemaNS, tsType: base.name, declared });
|
|
573
|
+
aliasMap.set(aliasKey, { name: outName, ns: schemaNS, tsType: base.name, declared, doc: elementDoc });
|
|
489
574
|
}
|
|
490
575
|
else {
|
|
491
576
|
// if an alias exists but points elsewhere, keep the first one (stable) and ignore
|
|
577
|
+
if (!existingAlias.doc && elementDoc) {
|
|
578
|
+
existingAlias.doc = elementDoc;
|
|
579
|
+
}
|
|
492
580
|
}
|
|
493
581
|
}
|
|
494
582
|
// Return base so callers have a CompiledType, but do not duplicate in compiledMap for wrapper
|
|
@@ -509,13 +597,14 @@ export function compileCatalog(cat, options) {
|
|
|
509
597
|
nillable: false,
|
|
510
598
|
declaredType: `{${a.ns}}${q.local}`
|
|
511
599
|
}],
|
|
600
|
+
doc: elementDoc,
|
|
512
601
|
};
|
|
513
602
|
compiledMap.set(key, t);
|
|
514
603
|
return t;
|
|
515
604
|
}
|
|
516
605
|
}
|
|
517
606
|
// default empty wrapper
|
|
518
|
-
const t = { name: outName, ns: schemaNS, attrs: [], elems: [] };
|
|
607
|
+
const t = { name: outName, ns: schemaNS, attrs: [], elems: [], doc: elementDoc };
|
|
519
608
|
compiledMap.set(key, t);
|
|
520
609
|
return t;
|
|
521
610
|
}
|
|
@@ -652,10 +741,11 @@ export function compileCatalog(cat, options) {
|
|
|
652
741
|
const outMsg = findMessage(getFirstWithLocalName(po, "output")?.["@_message"]);
|
|
653
742
|
const inputElement = elementOfMessage(inMsg);
|
|
654
743
|
const outputElement = elementOfMessage(outMsg);
|
|
744
|
+
const doc = extractDirectDocumentation(po);
|
|
655
745
|
// Derive TypeScript type names from element local names
|
|
656
746
|
const inputTypeName = inputElement ? pascal(inputElement.local) : undefined;
|
|
657
747
|
const outputTypeName = outputElement ? pascal(outputElement.local) : undefined;
|
|
658
|
-
return { name, soapAction: bOps.get(name) || "", inputElement, outputElement, inputTypeName, outputTypeName };
|
|
748
|
+
return { name, soapAction: bOps.get(name) || "", inputElement, outputElement, inputTypeName, outputTypeName, doc };
|
|
659
749
|
})
|
|
660
750
|
.filter((x) => x != null));
|
|
661
751
|
// --- WS-Policy: scan for security requirements (inline policies only) ---
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generatePaths.d.ts","sourceRoot":"","sources":["../../src/openapi/generatePaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAG3C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,EAAE,MAAM,GAAG;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAE9B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;IAC9C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"generatePaths.d.ts","sourceRoot":"","sources":["../../src/openapi/generatePaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAG3C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,EAAE,MAAM,GAAG;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAE9B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;IAC9C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,uBA6DlF"}
|
|
@@ -42,8 +42,12 @@ export function generatePaths(compiled, opts) {
|
|
|
42
42
|
};
|
|
43
43
|
if (override.summary)
|
|
44
44
|
operationObject.summary = override.summary;
|
|
45
|
-
if (override.description)
|
|
45
|
+
if (override.description) {
|
|
46
46
|
operationObject.description = override.description;
|
|
47
|
+
}
|
|
48
|
+
else if (op.doc) {
|
|
49
|
+
operationObject.description = op.doc;
|
|
50
|
+
}
|
|
47
51
|
if (override.deprecated)
|
|
48
52
|
operationObject.deprecated = true;
|
|
49
53
|
if (parameters.length)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateSchemas.d.ts","sourceRoot":"","sources":["../../src/openapi/generateSchemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAgB,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"generateSchemas.d.ts","sourceRoot":"","sources":["../../src/openapi/generateSchemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAgB,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAgKpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,sBAAsB,GAAG,iBAAiB,CA+C1G"}
|
|
@@ -28,14 +28,24 @@ function primitiveSchema(ts) {
|
|
|
28
28
|
function buildAliasSchema(a) {
|
|
29
29
|
const lit = isLiteralUnion(a.tsType);
|
|
30
30
|
if (lit) {
|
|
31
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
type: "string",
|
|
33
|
+
enum: lit,
|
|
34
|
+
...(a.doc ? { description: a.doc } : {}),
|
|
35
|
+
};
|
|
32
36
|
}
|
|
33
37
|
// If alias wraps primitive
|
|
34
38
|
if (["string", "number", "boolean", "any"].includes(a.tsType) || a.tsType.endsWith("[]")) {
|
|
35
|
-
return
|
|
39
|
+
return {
|
|
40
|
+
...primitiveSchema(a.tsType),
|
|
41
|
+
...(a.doc ? { description: a.doc } : {}),
|
|
42
|
+
};
|
|
36
43
|
}
|
|
37
44
|
// alias of another complex/alias type -> allOf wrapper preserves name
|
|
38
|
-
return {
|
|
45
|
+
return {
|
|
46
|
+
allOf: [{ $ref: `#/components/schemas/${a.tsType}` }],
|
|
47
|
+
...(a.doc ? { description: a.doc } : {}),
|
|
48
|
+
};
|
|
39
49
|
}
|
|
40
50
|
function isArrayWrapper(t) {
|
|
41
51
|
if (t.attrs.length !== 0)
|
|
@@ -80,13 +90,21 @@ function buildComplexSchema(t, closed, knownTypeNames, aliasNames, flattenWrappe
|
|
|
80
90
|
const arrayWrap = flattenWrappers ? isArrayWrapper(t) : null;
|
|
81
91
|
if (arrayWrap) {
|
|
82
92
|
const item = refOrPrimitive(String(arrayWrap.itemType));
|
|
83
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
type: "array",
|
|
95
|
+
items: item,
|
|
96
|
+
...(t.doc ? { description: t.doc } : {}),
|
|
97
|
+
};
|
|
84
98
|
}
|
|
85
99
|
const properties = {};
|
|
86
100
|
const required = [];
|
|
87
101
|
// attributes
|
|
88
102
|
for (const a of t.attrs) {
|
|
89
|
-
|
|
103
|
+
const propSchema = refOrPrimitive(a.tsType);
|
|
104
|
+
if (a.doc) {
|
|
105
|
+
propSchema.description = a.doc;
|
|
106
|
+
}
|
|
107
|
+
properties[a.name] = propSchema;
|
|
90
108
|
if (a.use === "required")
|
|
91
109
|
required.push(a.name);
|
|
92
110
|
}
|
|
@@ -100,6 +118,9 @@ function buildComplexSchema(t, closed, knownTypeNames, aliasNames, flattenWrappe
|
|
|
100
118
|
if (e.nillable) {
|
|
101
119
|
schema = { anyOf: [schema, { type: "null" }] };
|
|
102
120
|
}
|
|
121
|
+
if (e.doc) {
|
|
122
|
+
schema.description = e.doc;
|
|
123
|
+
}
|
|
103
124
|
properties[e.name] = schema;
|
|
104
125
|
if (e.name === "$value") {
|
|
105
126
|
// never required
|
|
@@ -130,6 +151,9 @@ function buildComplexSchema(t, closed, knownTypeNames, aliasNames, flattenWrappe
|
|
|
130
151
|
if (!closed)
|
|
131
152
|
delete obj.additionalProperties; // put closed only on leaf part
|
|
132
153
|
}
|
|
154
|
+
if (t.doc) {
|
|
155
|
+
obj.description = t.doc;
|
|
156
|
+
}
|
|
133
157
|
return obj;
|
|
134
158
|
}
|
|
135
159
|
/**
|
package/docs/architecture.md
CHANGED
|
@@ -83,6 +83,8 @@ CompiledCatalog {
|
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
`CompiledType`, type aliases, and operations may include optional `doc` fields populated from WSDL/XSD documentation nodes. Client emitters consume these values to generate comments in `types.ts`, `operations.ts`, and `client.ts`. OpenAPI emitters consume the same fields for schema, property, and operation `description` values.
|
|
87
|
+
|
|
86
88
|
Data flow through the pipeline:
|
|
87
89
|
|
|
88
90
|
1. schemaCompiler produces CompiledCatalog from WSDL XML
|
package/docs/concepts.md
CHANGED
|
@@ -67,6 +67,8 @@ cacheable, and reused across client, OpenAPI, and gateway generation.
|
|
|
67
67
|
Inspect types, operations, and metadata as plain JSON. The catalog is
|
|
68
68
|
automatically placed alongside generated output.
|
|
69
69
|
|
|
70
|
+
The catalog stores optional human-readable `doc` fields extracted from WSDL/XSD documentation nodes. These fields are additive metadata used by TypeScript and OpenAPI emitters and do not change runtime behavior.
|
|
71
|
+
|
|
70
72
|
### Catalog Locations by Command
|
|
71
73
|
|
|
72
74
|
| Command | Location |
|
package/docs/generated-code.md
CHANGED
|
@@ -73,6 +73,34 @@ result.GetCityWeatherByZIPResult.Temperature;
|
|
|
73
73
|
|
|
74
74
|
Autocomplete and type checking work across all generated interfaces.
|
|
75
75
|
|
|
76
|
+
## Documentation Comments
|
|
77
|
+
|
|
78
|
+
Generated `types.ts`, `operations.ts`, and `client.ts` include source documentation when present in WSDL/XSD.
|
|
79
|
+
|
|
80
|
+
`wsdl:documentation` on operations is emitted as method comments in `operations.ts` and `client.ts`. `xs:annotation/xs:documentation` on complex types, attributes, and elements is emitted as comments in `types.ts`.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
/**
|
|
84
|
+
* Thing payload.
|
|
85
|
+
*/
|
|
86
|
+
export interface Thing {
|
|
87
|
+
/**
|
|
88
|
+
* Display name.
|
|
89
|
+
*
|
|
90
|
+
* @xsd {"kind":"element","type":"xs:string","occurs":{"min":1,"max":1,"nillable":false}}
|
|
91
|
+
*/
|
|
92
|
+
name: string;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The existing `@xsd` metadata annotations are preserved for runtime marshaling and tooling.
|
|
97
|
+
|
|
98
|
+
OpenAPI generation also propagates these docs into `description` fields:
|
|
99
|
+
|
|
100
|
+
- Operation descriptions come from `wsdl:documentation` by default
|
|
101
|
+
- `--openapi-ops-file` `description` overrides operation documentation when provided
|
|
102
|
+
- Schema and property descriptions come from `xs:annotation/xs:documentation`
|
|
103
|
+
|
|
76
104
|
## Gateway Route Handlers
|
|
77
105
|
|
|
78
106
|
When generating a Fastify gateway (`--gateway-dir`), each SOAP operation gets a fully typed route handler. The handler imports the request type from the client, uses Fastify's `Body: T` generic for type inference, and wraps the SOAP response in a standard envelope.
|
|
@@ -99,9 +127,9 @@ export async function registerRoute_v1_weather_getcityforecastbyzip(fastify: Fas
|
|
|
99
127
|
|
|
100
128
|
Key features of the generated handlers:
|
|
101
129
|
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
130
|
+
- `Body: T` generic: Fastify infers `request.body` type from the route generic, enabling IDE autocomplete and compile-time checks
|
|
131
|
+
- JSON Schema validation: the `schema` import provides Fastify with request/response validation at runtime, before the handler runs
|
|
132
|
+
- Envelope wrapping: `buildSuccessEnvelope()` wraps the raw SOAP response in the standard `{ status, message, data, error }` envelope
|
|
105
133
|
|
|
106
134
|
See [Gateway Guide](gateway-guide.md) for the full architecture and [CLI Reference](cli-reference.md) for generation flags.
|
|
107
135
|
|
package/docs/testing.md
CHANGED
|
@@ -8,9 +8,9 @@ See [README](../README.md) for quick start and [CONTRIBUTING](../CONTRIBUTING.md
|
|
|
8
8
|
|
|
9
9
|
The project uses three layers of testing:
|
|
10
10
|
|
|
11
|
-
1.
|
|
12
|
-
2.
|
|
13
|
-
3.
|
|
11
|
+
1. Unit tests: Pure function tests for utilities, parsers, and type mapping
|
|
12
|
+
2. Snapshot tests: Baseline comparisons for all generated pipeline output
|
|
13
|
+
3. Integration tests: End-to-end gateway tests using Fastify's `inject()` with mock clients
|
|
14
14
|
|
|
15
15
|
All tests use [Vitest](https://vitest.dev/) and run in under 3 seconds.
|
|
16
16
|
|
|
@@ -34,12 +34,13 @@ npm run ci
|
|
|
34
34
|
|
|
35
35
|
Unit tests cover pure functions with no I/O or side effects:
|
|
36
36
|
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
37
|
+
- `tools.test.ts`: `pascal()`, `resolveQName()`, `explodePascal()`, `pascalToSnakeCase()`, `normalizeArray()`, `getChildrenWithLocalName()`, `getFirstWithLocalName()`
|
|
38
|
+
- `casing.test.ts`: `toPathSegment()` with kebab, asis, and lower styles
|
|
39
|
+
- `primitives.test.ts`: `xsdToTsPrimitive()` covering all XSD types (string-like, boolean, integers, decimals, floats, dates, any)
|
|
40
|
+
- `errors.test.ts`: `WsdlCompilationError` construction and `toUserMessage()` formatting
|
|
41
|
+
- `schema-alignment.test.ts`: Cross-validates TypeScript types, JSON schemas, and catalog.json for consistency
|
|
42
|
+
- `mock-data.test.ts`: `generateMockPrimitive()`, `generateMockData()`, `generateAllOperationMocks()` with cycle detection and array wrapping
|
|
43
|
+
- `wsdl-documentation.test.ts`: verifies doc flow to catalog, generated TS comments, and OpenAPI descriptions with override precedence
|
|
43
44
|
|
|
44
45
|
### Writing Unit Tests
|
|
45
46
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techspokes/typescript-wsdl-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Generate type-safe TypeScript SOAP clients, OpenAPI 3.1 specs, and production-ready Fastify REST gateways from WSDL/XSD definitions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wsdl",
|
|
@@ -76,20 +76,20 @@
|
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@types/js-yaml": "^4.0.9",
|
|
79
|
-
"@types/node": "^25.
|
|
79
|
+
"@types/node": "^25.3.3",
|
|
80
80
|
"@types/yargs": "^17.0.35",
|
|
81
|
-
"fastify": "^5.7.
|
|
81
|
+
"fastify": "^5.7.4",
|
|
82
82
|
"fastify-plugin": "^5.1.0",
|
|
83
|
-
"rimraf": "^6.1.
|
|
83
|
+
"rimraf": "^6.1.3",
|
|
84
84
|
"tsx": "^4.21.0",
|
|
85
|
-
"typescript": "^5.9.
|
|
85
|
+
"typescript": "^5.9.3",
|
|
86
86
|
"vitest": "^4.0.18"
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@apidevtools/swagger-parser": "^12.1.0",
|
|
90
|
-
"fast-xml-parser": "^5.
|
|
90
|
+
"fast-xml-parser": "^5.4.2",
|
|
91
91
|
"js-yaml": "^4.1.1",
|
|
92
|
-
"soap": "^1.
|
|
92
|
+
"soap": "^1.7.1",
|
|
93
93
|
"yargs": "^18.0.0"
|
|
94
94
|
},
|
|
95
95
|
"funding": {
|