@techspokes/typescript-wsdl-client 0.17.0 → 0.19.2

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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/app/generateApp.d.ts +1 -0
  3. package/dist/app/generateApp.d.ts.map +1 -1
  4. package/dist/app/generateApp.js +74 -7
  5. package/dist/cli.js +12 -2
  6. package/dist/gateway/helpers.js +2 -2
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/openapi/casing.d.ts +0 -14
  11. package/dist/openapi/casing.d.ts.map +1 -1
  12. package/dist/openapi/casing.js +3 -3
  13. package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
  14. package/dist/openapi/generateOpenAPI.js +3 -0
  15. package/dist/openapi/security.d.ts +1 -81
  16. package/dist/openapi/security.d.ts.map +1 -1
  17. package/dist/openapi/security.js +1 -146
  18. package/dist/pipeline.d.ts +1 -0
  19. package/dist/pipeline.d.ts.map +1 -1
  20. package/dist/pipeline.js +1 -0
  21. package/dist/test/generateTests.js +2 -2
  22. package/dist/util/builder.js +1 -1
  23. package/dist/util/runtimeSource.d.ts +1 -0
  24. package/dist/util/runtimeSource.d.ts.map +1 -1
  25. package/dist/util/runtimeSource.js +4 -0
  26. package/dist/util/securityConfig.d.ts +115 -0
  27. package/dist/util/securityConfig.d.ts.map +1 -0
  28. package/dist/util/securityConfig.js +274 -0
  29. package/dist/util/tools.d.ts +8 -0
  30. package/dist/util/tools.d.ts.map +1 -1
  31. package/dist/util/tools.js +16 -0
  32. package/docs/README.md +2 -2
  33. package/docs/api-reference.md +28 -4
  34. package/docs/architecture.md +1 -1
  35. package/docs/cli-reference.md +4 -1
  36. package/docs/configuration.md +33 -18
  37. package/docs/gateway-guide.md +8 -8
  38. package/docs/generated-code.md +7 -0
  39. package/docs/migration-playbook.md +14 -5
  40. package/docs/troubleshooting.md +1 -1
  41. package/package.json +5 -4
  42. package/src/runtime/appSecurity.tpl.txt +108 -0
@@ -40,7 +40,7 @@ export function buildOpenApiOptionsFromArgv(argv, format, servers) {
40
40
  opsFile: argv["openapi-ops-file"],
41
41
  pathStyle: argv["openapi-path-style"],
42
42
  pruneUnusedSchemas: argv["openapi-prune-unused-schemas"],
43
- securityConfigFile: argv["openapi-security-file"],
43
+ securityConfigFile: (argv["openapi-security-config-file"] || argv["openapi-security-file"]),
44
44
  servers,
45
45
  skipValidate: false, // Always validate
46
46
  tagStyle: argv["openapi-tag-style"],
@@ -1,2 +1,3 @@
1
1
  export declare function loadRuntimeSource(filename: string): string;
2
+ export declare function loadRuntimeTemplate(filename: string): string;
2
3
  //# sourceMappingURL=runtimeSource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeSource.d.ts","sourceRoot":"","sources":["../../src/util/runtimeSource.ts"],"names":[],"mappings":"AAsCA,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D"}
1
+ {"version":3,"file":"runtimeSource.d.ts","sourceRoot":"","sources":["../../src/util/runtimeSource.ts"],"names":[],"mappings":"AAsCA,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5D"}
@@ -36,3 +36,7 @@ export function loadRuntimeSource(filename) {
36
36
  const raw = fs.readFileSync(abs, "utf-8");
37
37
  return HEADER(filename) + "\n" + raw;
38
38
  }
39
+ export function loadRuntimeTemplate(filename) {
40
+ const abs = resolveRuntimeSourcePath(filename);
41
+ return fs.readFileSync(abs, "utf-8");
42
+ }
@@ -0,0 +1,115 @@
1
+ export type GatewayAuthScheme = "none" | "basic" | "bearer" | "apiKey" | "oauth2" | "mutualTLS" | "openIdConnect";
2
+ export type SecurityHeaderConfig = {
3
+ name: string;
4
+ required?: boolean;
5
+ schema?: any;
6
+ };
7
+ export type GatewaySecurityGlobal = {
8
+ scheme?: GatewayAuthScheme;
9
+ apiKey?: {
10
+ in: "header" | "query" | "cookie";
11
+ name: string;
12
+ };
13
+ bearer?: {
14
+ bearerFormat?: string;
15
+ };
16
+ basic?: Record<string, never>;
17
+ oauth2?: {
18
+ flows: Record<string, any>;
19
+ };
20
+ openIdConnect?: {
21
+ openIdConnectUrl: string;
22
+ };
23
+ mutualTLS?: {
24
+ description?: string;
25
+ };
26
+ headers?: SecurityHeaderConfig[];
27
+ };
28
+ export type GatewaySecurityOperation = {
29
+ scheme?: GatewayAuthScheme;
30
+ headers?: SecurityHeaderConfig[];
31
+ };
32
+ export type UpstreamSecurityConfig = {
33
+ profile?: "none" | "basic" | "bearer" | "ws-security-username-token" | "client-ssl" | "client-ssl-pfx" | "x509" | "ntlm" | "custom";
34
+ usernameEnv?: string;
35
+ passwordEnv?: string;
36
+ tokenEnv?: string;
37
+ domainEnv?: string;
38
+ workstationEnv?: string;
39
+ keyFileEnv?: string;
40
+ certFileEnv?: string;
41
+ caFileEnv?: string;
42
+ pfxFileEnv?: string;
43
+ passphraseEnv?: string;
44
+ endpointEnv?: string;
45
+ wsdlHeaders?: Record<string, string>;
46
+ wsdlHeaderEnv?: Record<string, string>;
47
+ requestHeaders?: Record<string, string>;
48
+ requestHeaderEnv?: Record<string, string>;
49
+ wsSecurity?: {
50
+ passwordType?: "PasswordText" | "PasswordDigest";
51
+ hasTimeStamp?: boolean;
52
+ hasTokenCreated?: boolean;
53
+ hasNonce?: boolean;
54
+ mustUnderstand?: boolean;
55
+ actor?: string;
56
+ };
57
+ };
58
+ export type SecurityConfig = {
59
+ gateway?: {
60
+ global?: GatewaySecurityGlobal;
61
+ operations?: Record<string, GatewaySecurityOperation>;
62
+ overrides?: Record<string, GatewaySecurityOperation>;
63
+ };
64
+ upstream?: UpstreamSecurityConfig;
65
+ headers?: {
66
+ passThrough?: string[];
67
+ mappings?: Array<{
68
+ from: string;
69
+ to: string;
70
+ required?: boolean;
71
+ }>;
72
+ };
73
+ };
74
+ export type BuiltSecurity = {
75
+ securitySchemes?: Record<string, any>;
76
+ headerParameters: Record<string, any>;
77
+ opSecurity: Record<string, any[] | undefined>;
78
+ opHeaderParameters: Record<string, string[]>;
79
+ globalSecurity?: any[];
80
+ };
81
+ export declare class SecurityConfigError extends Error {
82
+ constructor(message: string);
83
+ }
84
+ /**
85
+ * Loads and normalizes a JSON security configuration file.
86
+ *
87
+ * @param filePath - Optional path to the JSON config. Undefined returns undefined.
88
+ * @returns Parsed security configuration, or undefined when no file is provided.
89
+ * @throws SecurityConfigError when the parsed JSON shape is invalid.
90
+ * @throws SyntaxError when the file is not valid JSON.
91
+ */
92
+ export declare function loadSecurityConfigFile(filePath?: string): SecurityConfig | undefined;
93
+ /**
94
+ * Normalizes the shared gateway and upstream SOAP security config.
95
+ *
96
+ * @param input - Raw JSON value from a security config file or programmatic caller.
97
+ * @returns Normalized security config with legacy OpenAPI-only shape mapped to `gateway`.
98
+ * @throws SecurityConfigError when a section or scheme is malformed.
99
+ */
100
+ export declare function parseSecurityConfig(input: unknown): SecurityConfig;
101
+ /**
102
+ * Builds the OpenAPI-facing security schemes, requirements, and header parameters.
103
+ *
104
+ * @param cfg - Normalized shared security config.
105
+ * @returns Deterministic OpenAPI security fragments consumed by the OpenAPI generator.
106
+ */
107
+ export declare function buildSecurity(cfg?: SecurityConfig): BuiltSecurity;
108
+ /**
109
+ * Checks whether a generated app needs an upstream SOAP security helper.
110
+ *
111
+ * @param cfg - Normalized shared security config.
112
+ * @returns True when `upstream.profile` is configured to anything other than `none`.
113
+ */
114
+ export declare function hasUpstreamRuntimeSecurity(cfg?: SecurityConfig): boolean;
115
+ //# sourceMappingURL=securityConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"securityConfig.d.ts","sourceRoot":"","sources":["../../src/util/securityConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,274 @@
1
+ import fs from "node:fs";
2
+ import { trimRepeatedEdgeChar } from "./tools.js";
3
+ export class SecurityConfigError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "SecurityConfigError";
7
+ }
8
+ }
9
+ /**
10
+ * Loads and normalizes a JSON security configuration file.
11
+ *
12
+ * @param filePath - Optional path to the JSON config. Undefined returns undefined.
13
+ * @returns Parsed security configuration, or undefined when no file is provided.
14
+ * @throws SecurityConfigError when the parsed JSON shape is invalid.
15
+ * @throws SyntaxError when the file is not valid JSON.
16
+ */
17
+ export function loadSecurityConfigFile(filePath) {
18
+ if (!filePath)
19
+ return undefined;
20
+ const raw = fs.readFileSync(filePath, "utf8");
21
+ return parseSecurityConfig(JSON.parse(raw));
22
+ }
23
+ /**
24
+ * Normalizes the shared gateway and upstream SOAP security config.
25
+ *
26
+ * @param input - Raw JSON value from a security config file or programmatic caller.
27
+ * @returns Normalized security config with legacy OpenAPI-only shape mapped to `gateway`.
28
+ * @throws SecurityConfigError when a section or scheme is malformed.
29
+ */
30
+ export function parseSecurityConfig(input) {
31
+ if (!isRecord(input)) {
32
+ throw new SecurityConfigError("Security config must be a JSON object.");
33
+ }
34
+ const root = input;
35
+ const gatewaySource = isRecord(root.gateway)
36
+ ? root.gateway
37
+ : hasLegacyGatewayShape(root)
38
+ ? { global: root.global, overrides: root.overrides }
39
+ : undefined;
40
+ const cfg = {};
41
+ if (gatewaySource) {
42
+ cfg.gateway = parseGatewaySecurity(gatewaySource);
43
+ }
44
+ if (root.upstream !== undefined) {
45
+ cfg.upstream = parseUpstreamSecurity(root.upstream);
46
+ }
47
+ if (root.headers !== undefined) {
48
+ cfg.headers = parseHeaderForwarding(root.headers);
49
+ }
50
+ return cfg;
51
+ }
52
+ /**
53
+ * Builds the OpenAPI-facing security schemes, requirements, and header parameters.
54
+ *
55
+ * @param cfg - Normalized shared security config.
56
+ * @returns Deterministic OpenAPI security fragments consumed by the OpenAPI generator.
57
+ */
58
+ export function buildSecurity(cfg) {
59
+ const securitySchemes = {};
60
+ const headerParameters = {};
61
+ const opSecurity = {};
62
+ const opHeaderParameters = {};
63
+ const gateway = cfg?.gateway;
64
+ const global = gateway?.global;
65
+ if (!global) {
66
+ return { securitySchemes: undefined, headerParameters, opSecurity, opHeaderParameters };
67
+ }
68
+ const schemeName = "defaultAuth";
69
+ const scheme = global.scheme || "none";
70
+ const hasGlobal = scheme !== "none";
71
+ if (hasGlobal) {
72
+ securitySchemes[schemeName] = buildSecurityScheme(scheme, global);
73
+ }
74
+ for (const h of global.headers || []) {
75
+ headerParameters[makeParamComponentName(h.name)] = buildHeaderParameter(h);
76
+ }
77
+ const operations = {
78
+ ...(gateway.overrides || {}),
79
+ ...(gateway.operations || {}),
80
+ };
81
+ for (const [opName, override] of Object.entries(operations)) {
82
+ const oScheme = override.scheme ?? scheme;
83
+ if (oScheme === "none") {
84
+ opSecurity[opName] = [{}];
85
+ }
86
+ else if (oScheme === scheme) {
87
+ opSecurity[opName] = hasGlobal ? [{ [schemeName]: [] }] : undefined;
88
+ }
89
+ else {
90
+ const altName = `${schemeName}_${oScheme}`;
91
+ if (!securitySchemes[altName]) {
92
+ securitySchemes[altName] = buildSecurityScheme(oScheme, global);
93
+ }
94
+ opSecurity[opName] = [{ [altName]: [] }];
95
+ }
96
+ const headers = [...(global.headers || []), ...(override.headers || [])];
97
+ opHeaderParameters[opName] = headers.map(h => makeParamComponentName(h.name));
98
+ for (const h of override.headers || []) {
99
+ const compName = makeParamComponentName(h.name);
100
+ if (!headerParameters[compName]) {
101
+ headerParameters[compName] = buildHeaderParameter(h);
102
+ }
103
+ }
104
+ }
105
+ return {
106
+ securitySchemes: Object.keys(securitySchemes).length ? securitySchemes : undefined,
107
+ headerParameters,
108
+ opSecurity,
109
+ opHeaderParameters,
110
+ globalSecurity: hasGlobal ? [{ [schemeName]: [] }] : undefined,
111
+ };
112
+ }
113
+ /**
114
+ * Checks whether a generated app needs an upstream SOAP security helper.
115
+ *
116
+ * @param cfg - Normalized shared security config.
117
+ * @returns True when `upstream.profile` is configured to anything other than `none`.
118
+ */
119
+ export function hasUpstreamRuntimeSecurity(cfg) {
120
+ return !!cfg?.upstream && (cfg.upstream.profile ?? "none") !== "none";
121
+ }
122
+ function parseGatewaySecurity(input) {
123
+ const global = input.global === undefined ? undefined : parseGatewayGlobal(input.global);
124
+ const operations = input.operations === undefined ? undefined : parseGatewayOperations(input.operations, "operations");
125
+ const overrides = input.overrides === undefined ? undefined : parseGatewayOperations(input.overrides, "overrides");
126
+ return { global, operations, overrides };
127
+ }
128
+ function parseGatewayGlobal(input) {
129
+ if (!isRecord(input)) {
130
+ throw new SecurityConfigError("gateway.global must be an object.");
131
+ }
132
+ const scheme = parseGatewayScheme(input.scheme, "gateway.global.scheme");
133
+ return {
134
+ ...(scheme ? { scheme } : {}),
135
+ ...(isRecord(input.apiKey) ? { apiKey: parseApiKey(input.apiKey) } : {}),
136
+ ...(isRecord(input.bearer) ? { bearer: input.bearer } : {}),
137
+ ...(isRecord(input.basic) ? { basic: {} } : {}),
138
+ ...(isRecord(input.oauth2) ? { oauth2: input.oauth2 } : {}),
139
+ ...(isRecord(input.openIdConnect) ? { openIdConnect: input.openIdConnect } : {}),
140
+ ...(isRecord(input.mutualTLS) ? { mutualTLS: input.mutualTLS } : {}),
141
+ ...(input.headers !== undefined ? { headers: parseHeaders(input.headers, "gateway.global.headers") } : {}),
142
+ };
143
+ }
144
+ function parseGatewayOperations(input, label) {
145
+ if (!isRecord(input)) {
146
+ throw new SecurityConfigError(`gateway.${label} must be an object keyed by operation name.`);
147
+ }
148
+ const out = {};
149
+ for (const [opName, value] of Object.entries(input)) {
150
+ if (!isRecord(value)) {
151
+ throw new SecurityConfigError(`gateway.${label}.${opName} must be an object.`);
152
+ }
153
+ const scheme = parseGatewayScheme(value.scheme, `gateway.${label}.${opName}.scheme`);
154
+ out[opName] = {
155
+ ...(scheme ? { scheme } : {}),
156
+ ...(value.headers !== undefined ? { headers: parseHeaders(value.headers, `gateway.${label}.${opName}.headers`) } : {}),
157
+ };
158
+ }
159
+ return out;
160
+ }
161
+ function parseUpstreamSecurity(input) {
162
+ if (!isRecord(input)) {
163
+ throw new SecurityConfigError("upstream must be an object.");
164
+ }
165
+ const profile = input.profile === undefined ? undefined : String(input.profile);
166
+ const allowed = new Set(["none", "basic", "bearer", "ws-security-username-token", "client-ssl", "client-ssl-pfx", "x509", "ntlm", "custom"]);
167
+ if (profile && !allowed.has(profile)) {
168
+ throw new SecurityConfigError(`Unsupported upstream.profile '${profile}'.`);
169
+ }
170
+ return input;
171
+ }
172
+ function parseHeaderForwarding(input) {
173
+ if (!isRecord(input)) {
174
+ throw new SecurityConfigError("headers must be an object.");
175
+ }
176
+ const passThrough = input.passThrough === undefined ? undefined : parseStringArray(input.passThrough, "headers.passThrough");
177
+ const mappings = input.mappings === undefined ? undefined : parseMappings(input.mappings);
178
+ return {
179
+ ...(passThrough ? { passThrough } : {}),
180
+ ...(mappings ? { mappings } : {}),
181
+ };
182
+ }
183
+ function parseHeaders(input, label) {
184
+ if (!Array.isArray(input)) {
185
+ throw new SecurityConfigError(`${label} must be an array.`);
186
+ }
187
+ return input.map((entry, index) => {
188
+ if (!isRecord(entry) || typeof entry.name !== "string" || !entry.name.trim()) {
189
+ throw new SecurityConfigError(`${label}[${index}].name must be a non-empty string.`);
190
+ }
191
+ return {
192
+ name: entry.name,
193
+ ...(entry.required !== undefined ? { required: Boolean(entry.required) } : {}),
194
+ ...(entry.schema !== undefined ? { schema: entry.schema } : {}),
195
+ };
196
+ });
197
+ }
198
+ function parseGatewayScheme(input, label) {
199
+ if (input === undefined)
200
+ return undefined;
201
+ const scheme = String(input);
202
+ if (!["none", "basic", "bearer", "apiKey", "oauth2", "mutualTLS", "openIdConnect"].includes(scheme)) {
203
+ throw new SecurityConfigError(`Unsupported ${label} '${scheme}'.`);
204
+ }
205
+ return scheme;
206
+ }
207
+ function parseApiKey(input) {
208
+ const location = input.in === undefined ? "header" : String(input.in);
209
+ if (!["header", "query", "cookie"].includes(location)) {
210
+ throw new SecurityConfigError("apiKey.in must be header, query, or cookie.");
211
+ }
212
+ const name = input.name === undefined ? "X-API-Key" : String(input.name);
213
+ if (!name) {
214
+ throw new SecurityConfigError("apiKey.name must be a non-empty string.");
215
+ }
216
+ return { in: location, name };
217
+ }
218
+ function parseStringArray(input, label) {
219
+ if (!Array.isArray(input) || input.some(v => typeof v !== "string" || !v)) {
220
+ throw new SecurityConfigError(`${label} must be an array of non-empty strings.`);
221
+ }
222
+ return input;
223
+ }
224
+ function parseMappings(input) {
225
+ if (!Array.isArray(input)) {
226
+ throw new SecurityConfigError("headers.mappings must be an array.");
227
+ }
228
+ return input.map((entry, index) => {
229
+ if (!isRecord(entry) || typeof entry.from !== "string" || typeof entry.to !== "string" || !entry.from || !entry.to) {
230
+ throw new SecurityConfigError(`headers.mappings[${index}] must define non-empty from and to strings.`);
231
+ }
232
+ return {
233
+ from: entry.from,
234
+ to: entry.to,
235
+ ...(entry.required !== undefined ? { required: Boolean(entry.required) } : {}),
236
+ };
237
+ });
238
+ }
239
+ function buildSecurityScheme(scheme, global) {
240
+ switch (scheme) {
241
+ case "basic":
242
+ return { type: "http", scheme: "basic" };
243
+ case "bearer":
244
+ return { type: "http", scheme: "bearer", ...(global.bearer || {}) };
245
+ case "apiKey":
246
+ return { type: "apiKey", ...(global.apiKey || { in: "header", name: "X-API-Key" }) };
247
+ case "oauth2":
248
+ return { type: "oauth2", ...(global.oauth2 || { flows: {} }) };
249
+ case "mutualTLS":
250
+ return { type: "mutualTLS", ...(global.mutualTLS || {}) };
251
+ case "openIdConnect":
252
+ return { type: "openIdConnect", ...(global.openIdConnect || { openIdConnectUrl: "" }) };
253
+ case "none":
254
+ return undefined;
255
+ }
256
+ }
257
+ function buildHeaderParameter(h) {
258
+ return {
259
+ name: h.name,
260
+ in: "header",
261
+ required: !!h.required,
262
+ schema: h.schema || { type: "string" },
263
+ };
264
+ }
265
+ function makeParamComponentName(headerName) {
266
+ return trimRepeatedEdgeChar(headerName.replace(/[^A-Za-z0-9]+/g, "_"), "_")
267
+ || "X_Header";
268
+ }
269
+ function hasLegacyGatewayShape(root) {
270
+ return root.global !== undefined || root.overrides !== undefined;
271
+ }
272
+ function isRecord(value) {
273
+ return !!value && typeof value === "object" && !Array.isArray(value);
274
+ }
@@ -25,6 +25,14 @@ import type { CompiledCatalog } from "../compiler/schemaCompiler.js";
25
25
  * @returns {T[]} - Array containing the value(s) or empty array if null/undefined
26
26
  */
27
27
  export declare function normalizeArray<T>(x: T | T[] | undefined | null): T[];
28
+ /**
29
+ * Removes one repeated edge character without regex backtracking.
30
+ *
31
+ * @param value - Value to trim.
32
+ * @param char - Single character to remove from both edges.
33
+ * @returns Input without repeated leading or trailing `char` values.
34
+ */
35
+ export declare function trimRepeatedEdgeChar(value: string, char: string): string;
28
36
  /**
29
37
  * Collects direct children whose local name matches (prefix-agnostic)
30
38
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/util/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAEnE;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,EAAE,CAGpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,CASxE;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAK/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAS/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,eAAe,GACxB,MAAM,CAWR;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAMjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAInD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/util/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAEnE;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,EAAE,CAGpE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMxE;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,CASxE;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAK/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAS/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,eAAe,GACxB,MAAM,CAWR;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAMjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAInD"}
@@ -13,6 +13,22 @@ export function normalizeArray(x) {
13
13
  return [];
14
14
  return Array.isArray(x) ? x : [x];
15
15
  }
16
+ /**
17
+ * Removes one repeated edge character without regex backtracking.
18
+ *
19
+ * @param value - Value to trim.
20
+ * @param char - Single character to remove from both edges.
21
+ * @returns Input without repeated leading or trailing `char` values.
22
+ */
23
+ export function trimRepeatedEdgeChar(value, char) {
24
+ let start = 0;
25
+ let end = value.length;
26
+ while (start < end && value[start] === char)
27
+ start += 1;
28
+ while (end > start && value[end - 1] === char)
29
+ end -= 1;
30
+ return value.slice(start, end);
31
+ }
16
32
  /**
17
33
  * Collects direct children whose local name matches (prefix-agnostic)
18
34
  *
package/docs/README.md CHANGED
@@ -12,7 +12,7 @@ Human-maintained reference documents for `@techspokes/typescript-wsdl-client`. T
12
12
 
13
13
  - [cli-reference.md](cli-reference.md): all 6 commands with flags and examples
14
14
  - [generated-code.md](generated-code.md): using clients, types, and operations
15
- - [configuration.md](configuration.md): security schemes, tags, operations config files
15
+ - [configuration.md](configuration.md): gateway security, upstream SOAP security, tags, operations config files
16
16
  - [migration-playbook.md](migration-playbook.md): end-to-end SOAP modernization guide
17
17
 
18
18
  ## Operate
@@ -42,7 +42,7 @@ Human-maintained reference documents for `@techspokes/typescript-wsdl-client`. T
42
42
  - [Root README](../README.md): project overview, quick start, and authoritative Documentation section
43
43
  - [CONTRIBUTING.md](../CONTRIBUTING.md): development setup and workflow
44
44
  - [CHANGELOG.md](../CHANGELOG.md): version history
45
- - [examples/](../examples/): sample WSDL files and generated output
45
+ - [examples](../examples/README.md): sample WSDL files and generated output
46
46
 
47
47
  ## Not Here
48
48
 
@@ -33,7 +33,7 @@ await compileWsdlToProject({
33
33
  ### Type Signature
34
34
 
35
35
  ```typescript
36
- function compileWsdlToProject(input: {
36
+ declare function compileWsdlToProject(input: {
37
37
  wsdl: string;
38
38
  outDir: string;
39
39
  options?: Partial<CompilerOptions>;
@@ -92,7 +92,7 @@ const { doc, jsonPath, yamlPath } = await generateOpenAPI({
92
92
  ### Type Signature
93
93
 
94
94
  ```typescript
95
- function generateOpenAPI(opts: GenerateOpenAPIOptions): Promise<{
95
+ declare function generateOpenAPI(opts: GenerateOpenAPIOptions): Promise<{
96
96
  doc: any;
97
97
  jsonPath?: string;
98
98
  yamlPath?: string;
@@ -129,6 +129,10 @@ interface GenerateOpenAPIOptions {
129
129
  }
130
130
  ```
131
131
 
132
+ `securityConfigFile` accepts the shared security configuration documented in
133
+ [Configuration](configuration.md#security-configuration). The config can describe
134
+ gateway OpenAPI security and upstream SOAP runtime security for generated apps.
135
+
132
136
  ## generateGateway
133
137
 
134
138
  Generate Fastify gateway code from an OpenAPI specification.
@@ -150,7 +154,7 @@ await generateGateway({
150
154
  ### Type Signature
151
155
 
152
156
  ```typescript
153
- function generateGateway(opts: GenerateGatewayOptions): Promise<void>;
157
+ declare function generateGateway(opts: GenerateGatewayOptions): Promise<void>;
154
158
  ```
155
159
 
156
160
  ### GenerateGatewayOptions
@@ -203,7 +207,7 @@ const { compiled, openapiDoc } = await runGenerationPipeline({
203
207
  ### Type Signature
204
208
 
205
209
  ```typescript
206
- function runGenerationPipeline(opts: PipelineOptions): Promise<{
210
+ declare function runGenerationPipeline(opts: PipelineOptions): Promise<{
207
211
  compiled: CompiledCatalog;
208
212
  openapiDoc?: any;
209
213
  }>;
@@ -332,6 +336,26 @@ interface OperationStreamMetadata {
332
336
 
333
337
  Exactly one of `wsdlSource` or `catalogFile` must be set on each `ShapeCatalogRef`. `OperationStreamMetadata` is produced by the parser; `sourceOutputTypeName` is populated by the compiler when it binds the operation to the main WSDL.
334
338
 
339
+ ## Security Configuration Helpers
340
+
341
+ Parse and build the shared security configuration used by OpenAPI generation and
342
+ the app scaffold.
343
+
344
+ ```typescript
345
+ import {
346
+ buildSecurity,
347
+ loadSecurityConfigFile,
348
+ parseSecurityConfig,
349
+ } from "@techspokes/typescript-wsdl-client";
350
+
351
+ const config = loadSecurityConfigFile("./security.json");
352
+ const built = buildSecurity(config);
353
+ console.log(built.securitySchemes);
354
+ ```
355
+
356
+ `parseSecurityConfig` accepts the same JSON shape as `loadSecurityConfigFile`.
357
+ `SecurityConfigError` is thrown for invalid schemes or malformed sections.
358
+
335
359
  ## End-to-End Example
336
360
 
337
361
  Compile with a stream config, verify the catalog carries the expected metadata, and run the full pipeline:
@@ -53,7 +53,7 @@ generateClient.ts emits the client class with one method per operation. generate
53
53
 
54
54
  ### openapi/
55
55
 
56
- generateOpenAPI.ts orchestrates the complete OpenAPI document. generateSchemas.ts converts compiled types to JSON Schema. generatePaths.ts generates path items from operations. security.ts processes security configuration files. casing.ts handles path style transformation.
56
+ generateOpenAPI.ts orchestrates the complete OpenAPI document. generateSchemas.ts converts compiled types to JSON Schema. generatePaths.ts generates path items from operations. security.ts adapts the shared security configuration model for OpenAPI. casing.ts handles path style transformation.
57
57
 
58
58
  ### gateway/
59
59
 
@@ -92,7 +92,7 @@ The catalog is auto-placed alongside the first available output directory: `{cli
92
92
  | `--openapi-envelope-namespace` | `ResponseEnvelope` | Envelope component name suffix |
93
93
  | `--openapi-error-namespace` | `ErrorObject` | Error object name suffix |
94
94
  | `--openapi-validate` | `true` | Validate spec with swagger-parser |
95
- | `--openapi-security-config-file` | | Path to security.json |
95
+ | `--openapi-security-config-file` | | Path to security.json; `--openapi-security-file` is accepted as an alias |
96
96
  | `--openapi-tags-file` | | Path to tags.json |
97
97
  | `--openapi-ops-file` | | Path to ops.json |
98
98
 
@@ -473,6 +473,7 @@ npx wsdl-tsc app \
473
473
  | `--prefix` | (empty) | Route prefix |
474
474
  | `--logger` | true | Enable Fastify logger |
475
475
  | `--openapi-mode` | copy | copy or reference |
476
+ | `--security-config-file` | (none) | Path to security.json for upstream SOAP security scaffold |
476
477
  | `--force` | false | Overwrite existing scaffold files |
477
478
 
478
479
  ### Generated Structure
@@ -499,6 +500,8 @@ app/
499
500
  | `LOGGER` | true | Fastify logger |
500
501
  | `OPENAPI_SERVER_URL` | (empty) | Override OpenAPI spec server URL at runtime |
501
502
 
503
+ When `--security-config-file` is supplied and it contains an `upstream` profile, `.env.example` also lists the environment variables referenced by that profile.
504
+
502
505
  ### Endpoints
503
506
 
504
507
  - `GET /health` returns `{ "ok": true }`
@@ -8,28 +8,43 @@ See [CLI Reference](cli-reference.md) for flag details and [README](../README.md
8
8
 
9
9
  Pass via `--openapi-security-config-file`.
10
10
 
11
- Defines security schemes, headers, and per-operation overrides.
11
+ Defines REST gateway security, request headers, and upstream SOAP security. The `gateway` section describes the generated REST API in OpenAPI and adds Fastify header validation. The `upstream` section is used by the generated app scaffold to build `node-soap` runtime options from environment variables.
12
12
 
13
13
  ```json
14
14
  {
15
- "global": {
16
- "scheme": "bearer",
17
- "bearer": { "bearerFormat": "JWT" },
18
- "headers": [
19
- {
20
- "name": "X-Correlation-Id",
21
- "required": false,
22
- "schema": { "type": "string" }
23
- }
24
- ]
15
+ "gateway": {
16
+ "global": {
17
+ "scheme": "bearer",
18
+ "bearer": { "bearerFormat": "JWT" },
19
+ "headers": [
20
+ {
21
+ "name": "X-Correlation-Id",
22
+ "required": false,
23
+ "schema": { "type": "string" }
24
+ }
25
+ ]
26
+ },
27
+ "operations": {
28
+ "CancelBooking": { "scheme": "apiKey" },
29
+ "HealthCheck": { "scheme": "none" }
30
+ }
25
31
  },
26
- "overrides": {
27
- "CancelBooking": { "scheme": "apiKey" }
32
+ "upstream": {
33
+ "profile": "ws-security-username-token",
34
+ "usernameEnv": "SOAP_USERNAME",
35
+ "passwordEnv": "SOAP_PASSWORD",
36
+ "endpointEnv": "SOAP_ENDPOINT"
28
37
  }
29
38
  }
30
39
  ```
31
40
 
32
- Supported schemes: none, basic, bearer, apiKey, oauth2.
41
+ Gateway schemes: none, basic, bearer, apiKey, oauth2, mutualTLS, openIdConnect.
42
+
43
+ Upstream SOAP profiles: none, basic, bearer, ws-security-username-token, client-ssl, client-ssl-pfx, x509, ntlm, custom.
44
+
45
+ The older OpenAPI-only shape with top-level `global` and `overrides` is still accepted. New projects should use `gateway.global` and `gateway.operations`.
46
+
47
+ The generated app scaffold reads upstream secrets from environment variables. It does not embed secret values in generated source and does not implement production JWT, OAuth, or API-key verification for inbound gateway requests. Add that verification in app hooks or your platform gateway.
33
48
 
34
49
  ## Tags Configuration
35
50
 
@@ -107,8 +122,8 @@ terminal-error policy.
107
122
 
108
123
  ## Example Files
109
124
 
110
- Example configuration files are available in the `examples/openapi/` directory:
125
+ Example configuration files are available in the `examples/config/` directory:
111
126
 
112
- - `examples/openapi/security.json` for security scheme configuration
113
- - `examples/openapi/tags.json` for tag mapping
114
- - `examples/openapi/ops.json` for operation overrides
127
+ - `examples/config/security.json` for gateway and upstream security configuration
128
+ - `examples/config/tags.json` for tag mapping
129
+ - `examples/config/ops.json` for operation overrides