@techspokes/typescript-wsdl-client 0.6.3 → 0.7.1

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 (53) hide show
  1. package/README.md +296 -166
  2. package/dist/cli.js +239 -1
  3. package/dist/compiler/schemaCompiler.d.ts +54 -0
  4. package/dist/compiler/schemaCompiler.d.ts.map +1 -1
  5. package/dist/compiler/schemaCompiler.js +74 -7
  6. package/dist/config.d.ts +23 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/emit/catalogEmitter.d.ts +18 -0
  9. package/dist/emit/catalogEmitter.d.ts.map +1 -1
  10. package/dist/emit/catalogEmitter.js +31 -0
  11. package/dist/emit/clientEmitter.d.ts +17 -0
  12. package/dist/emit/clientEmitter.d.ts.map +1 -1
  13. package/dist/emit/clientEmitter.js +33 -3
  14. package/dist/emit/typesEmitter.d.ts +16 -5
  15. package/dist/emit/typesEmitter.d.ts.map +1 -1
  16. package/dist/emit/typesEmitter.js +30 -5
  17. package/dist/emit/utilsEmitter.d.ts +18 -0
  18. package/dist/emit/utilsEmitter.d.ts.map +1 -1
  19. package/dist/emit/utilsEmitter.js +30 -0
  20. package/dist/index.d.ts +22 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +36 -1
  23. package/dist/loader/fetch.d.ts +31 -0
  24. package/dist/loader/fetch.d.ts.map +1 -1
  25. package/dist/loader/fetch.js +31 -0
  26. package/dist/loader/wsdlLoader.d.ts +32 -0
  27. package/dist/loader/wsdlLoader.d.ts.map +1 -1
  28. package/dist/loader/wsdlLoader.js +80 -9
  29. package/dist/openapi/buildPaths.d.ts +74 -0
  30. package/dist/openapi/buildPaths.d.ts.map +1 -0
  31. package/dist/openapi/buildPaths.js +66 -0
  32. package/dist/openapi/buildSchemas.d.ts +44 -0
  33. package/dist/openapi/buildSchemas.d.ts.map +1 -0
  34. package/dist/openapi/buildSchemas.js +196 -0
  35. package/dist/openapi/casing.d.ts +38 -0
  36. package/dist/openapi/casing.d.ts.map +1 -0
  37. package/dist/openapi/casing.js +49 -0
  38. package/dist/openapi/generateOpenAPI.d.ts +61 -0
  39. package/dist/openapi/generateOpenAPI.d.ts.map +1 -0
  40. package/dist/openapi/generateOpenAPI.js +315 -0
  41. package/dist/openapi/security.d.ts +82 -0
  42. package/dist/openapi/security.d.ts.map +1 -0
  43. package/dist/openapi/security.js +145 -0
  44. package/dist/pipeline.d.ts +37 -0
  45. package/dist/pipeline.d.ts.map +1 -0
  46. package/dist/pipeline.js +72 -0
  47. package/dist/util/tools.d.ts +100 -7
  48. package/dist/util/tools.d.ts.map +1 -1
  49. package/dist/util/tools.js +85 -7
  50. package/dist/xsd/primitives.d.ts +33 -0
  51. package/dist/xsd/primitives.d.ts.map +1 -1
  52. package/dist/xsd/primitives.js +59 -7
  53. package/package.json +7 -2
@@ -0,0 +1,315 @@
1
+ /**
2
+ * OpenAPI 3.1 Generator from WSDL
3
+ *
4
+ * This module generates OpenAPI 3.1 specifications from WSDL documents or compiled catalogs.
5
+ * It bridges the gap between SOAP web services and REST APIs by creating an OpenAPI
6
+ * representation that mirrors the TypeScript model generated by the WSDL client generator.
7
+ *
8
+ * Core capabilities:
9
+ * - Multiple input sources (WSDL URL/path, pre-compiled catalog, or in-memory catalog)
10
+ * - Deterministic schema + path emission mirroring generated TypeScript
11
+ * - Security scheme + header parameter integration (via security.json)
12
+ * - Operation tagging heuristics + explicit tag + op override files
13
+ * - Multi-format output (json|yaml|both) with optional validation
14
+ * - Always-on Standard Response Envelope (since 0.7.1): automatically wraps all
15
+ * responses in a base envelope plus per-payload extensions, providing stable
16
+ * top-level fields (status, message, data, error). Naming is customizable via
17
+ * --envelope-namespace / --error-namespace.
18
+ *
19
+ * Naming Rules:
20
+ * - If a namespace flag is provided its value is used verbatim as the component name.
21
+ * - Otherwise `${serviceName}ResponseEnvelope` / `${serviceName}ErrorObject` are used.
22
+ * - Each operation output produces an extension schema named `<PayloadType|OpName><EnvelopeNamespace>`.
23
+ * - All schemas, paths, methods, securitySchemes, and parameters are alphabetically sorted for diff friendliness.
24
+ */
25
+ import * as fs from "fs";
26
+ import * as path from "path";
27
+ import * as yaml from "js-yaml";
28
+ import { loadWsdl } from "../loader/wsdlLoader.js";
29
+ import { compileCatalog } from "../compiler/schemaCompiler.js";
30
+ import { buildSchemas } from "./buildSchemas.js";
31
+ import { buildPaths } from "./buildPaths.js";
32
+ import { buildSecurity, loadSecurityConfig } from "./security.js";
33
+ export async function generateOpenAPI(opts) {
34
+ // Normalize format (back-compat: asYaml overrides if provided and format not set)
35
+ let format = opts.format || (opts.asYaml ? "yaml" : "json");
36
+ if (format === "yaml" && opts.asYaml && opts.outFile && /\.json$/i.test(opts.outFile)) {
37
+ // user asked for yaml but provided .json path → we'll still switch extension
38
+ }
39
+ if (!opts.compiledCatalog && !opts.wsdl && !opts.catalogFile) {
40
+ throw new Error("Provide one of: compiledCatalog, wsdl, or catalogFile");
41
+ }
42
+ if ((opts.wsdl && opts.catalogFile) || (opts.compiledCatalog && (opts.wsdl || opts.catalogFile))) {
43
+ // Not strictly an error, but disallow ambiguous multi-source inputs to keep deterministic
44
+ // Users should supply only ONE source of truth.
45
+ throw new Error("Provide only one source: compiledCatalog OR wsdl OR catalogFile");
46
+ }
47
+ let compiled;
48
+ if (opts.compiledCatalog) {
49
+ compiled = opts.compiledCatalog;
50
+ }
51
+ else if (opts.catalogFile) {
52
+ const raw = fs.readFileSync(opts.catalogFile, "utf8");
53
+ compiled = JSON.parse(raw);
54
+ }
55
+ else {
56
+ const wsdlCatalog = await loadWsdl(String(opts.wsdl));
57
+ compiled = compileCatalog(wsdlCatalog, {
58
+ // minimal compiler options (no generation side effects needed here)
59
+ wsdl: String(opts.wsdl),
60
+ out: "",
61
+ imports: "js",
62
+ catalog: false,
63
+ primitive: { int64As: "string", bigIntegerAs: "string", decimalAs: "string", dateAs: "string" },
64
+ choice: "all-optional",
65
+ failOnUnresolved: false,
66
+ attributesKey: "$attributes",
67
+ nillableAsOptional: false,
68
+ clientName: undefined,
69
+ });
70
+ }
71
+ const title = opts.title || (compiled.serviceName ? `${compiled.serviceName} SOAP API` : "Generated SOAP API");
72
+ const infoVersion = opts.version || "0.0.0";
73
+ // Load external config files (optional)
74
+ const tagsMap = opts.tagsFile ? safeJson(opts.tagsFile) : undefined;
75
+ const opsOverrides = opts.opsFile ? safeJson(opts.opsFile) : undefined;
76
+ const securityCfg = loadSecurityConfig(opts.securityConfigFile);
77
+ const securityBuilt = buildSecurity(securityCfg);
78
+ // Build components.schemas
79
+ const schemas = buildSchemas(compiled, {
80
+ closedSchemas: opts.closedSchemas,
81
+ pruneUnusedSchemas: opts.pruneUnusedSchemas,
82
+ });
83
+ // Build paths
84
+ const tagStyle = opts.tagStyle || "default";
85
+ const defaultTag = (() => {
86
+ if (tagStyle === "service")
87
+ return compiled.serviceName || "SOAP";
88
+ if (tagStyle === "first")
89
+ return "General"; // fallback; per-op derivation below
90
+ return compiled.serviceName || "SOAP";
91
+ })();
92
+ const paths = buildPaths(compiled, {
93
+ basePath: opts.basePath || "/",
94
+ pathStyle: opts.pathStyle || "kebab",
95
+ defaultMethod: opts.defaultMethod || "post",
96
+ tagsMap,
97
+ overrides: opsOverrides,
98
+ defaultTag,
99
+ opSecurity: securityBuilt.opSecurity,
100
+ opHeaderParameters: securityBuilt.opHeaderParameters,
101
+ });
102
+ // --- Standard Envelope (always enabled since 0.7.1) ---
103
+ const serviceName = compiled.serviceName || "Service";
104
+ const envelopeNamespace = opts.envelopeNamespace || "ResponseEnvelope";
105
+ const errorNamespace = opts.errorNamespace || "ErrorObject";
106
+ function leadingToken(ns) {
107
+ return ns.match(/^[A-Z][a-z0-9]*/)?.[0] || ns;
108
+ }
109
+ function joinWithNamespace(base, ns) {
110
+ const token = leadingToken(ns);
111
+ return base.endsWith(token) ? `${base}_${ns}` : `${base}${ns}`;
112
+ }
113
+ const baseEnvelopeName = opts.envelopeNamespace
114
+ ? envelopeNamespace
115
+ : joinWithNamespace(serviceName, envelopeNamespace);
116
+ const errorSchemaName = opts.errorNamespace
117
+ ? errorNamespace
118
+ : joinWithNamespace(serviceName, errorNamespace);
119
+ const errorSchema = {
120
+ type: "object",
121
+ properties: {
122
+ code: { type: "string" },
123
+ message: { type: "string" },
124
+ details: { anyOf: [{ type: "object", additionalProperties: true }, { type: "null" }] },
125
+ },
126
+ required: ["code", "message"],
127
+ additionalProperties: false,
128
+ description: "Standard error object for REST responses (not reused for underlying SOAP faults).",
129
+ };
130
+ const baseEnvelopeSchema = {
131
+ type: "object",
132
+ properties: {
133
+ status: { type: "string", description: "Machine-readable high-level status (e.g. SUCCESS, FAILURE, PENDING)." },
134
+ message: {
135
+ anyOf: [{ type: "string" }, { type: "null" }],
136
+ description: "Diagnostic/logging message (not for end-user display)."
137
+ },
138
+ data: { anyOf: [{}, { type: "null" }], description: "Primary payload; per-operation extension refines the shape." },
139
+ error: {
140
+ anyOf: [{ $ref: `#/components/schemas/${errorSchemaName}` }, { type: "null" }],
141
+ description: "Error details when status indicates failure; null otherwise."
142
+ },
143
+ },
144
+ required: ["status", "data", "error", "message"],
145
+ additionalProperties: true,
146
+ description: "Standard API response envelope base schema.",
147
+ };
148
+ const extensionSchemas = {};
149
+ for (const op of compiled.operations) {
150
+ const payloadType = op.outputElement?.local;
151
+ const payloadRef = payloadType && schemas[payloadType] ? { $ref: `#/components/schemas/${payloadType}` } : { type: "object" };
152
+ const baseForExt = payloadType || op.name;
153
+ const extName = joinWithNamespace(baseForExt, envelopeNamespace);
154
+ if (extensionSchemas[extName])
155
+ continue; // de-dupe
156
+ extensionSchemas[extName] = {
157
+ allOf: [
158
+ { $ref: `#/components/schemas/${baseEnvelopeName}` },
159
+ { type: "object", properties: { data: { anyOf: [payloadRef, { type: "null" }] } }, additionalProperties: true }
160
+ ],
161
+ description: `Envelope for ${payloadType || op.name} operation output wrapping its payload in 'data'.`
162
+ };
163
+ }
164
+ // Merge all schemas: add base + extensions + error then original schemas, then final alphabetical sort of all keys
165
+ const mergedSchemas = {
166
+ [baseEnvelopeName]: baseEnvelopeSchema,
167
+ ...extensionSchemas,
168
+ [errorSchemaName]: errorSchema,
169
+ ...schemas,
170
+ };
171
+ const sortedSchemaKeys = Object.keys(mergedSchemas).sort((a, b) => a.localeCompare(b));
172
+ const finalSchemas = {};
173
+ for (const k of sortedSchemaKeys)
174
+ finalSchemas[k] = mergedSchemas[k];
175
+ // Update paths response schemas to reference extension envelopes
176
+ for (const pathItem of Object.values(paths)) {
177
+ for (const methodObj of Object.values(pathItem)) {
178
+ const opId = methodObj.operationId;
179
+ if (!opId)
180
+ continue;
181
+ const op = compiled.operations.find(o => o.name === opId);
182
+ if (!op)
183
+ continue;
184
+ const payloadType = op.outputElement?.local;
185
+ const baseForExt = payloadType || op.name;
186
+ const extName = joinWithNamespace(baseForExt, envelopeNamespace);
187
+ if (methodObj.responses?.["200"]) {
188
+ methodObj.responses["200"].description = "Successful operation (standard envelope)";
189
+ methodObj.responses["200"].content = { "application/json": { schema: { $ref: `#/components/schemas/${extName}` } } };
190
+ }
191
+ if (methodObj.responses?.default) {
192
+ methodObj.responses.default.description = "Error response (standard envelope with populated error object)";
193
+ methodObj.responses.default.content = { "application/json": { schema: { $ref: `#/components/schemas/${baseEnvelopeName}` } } };
194
+ }
195
+ }
196
+ }
197
+ // --- End Standard Envelope ---
198
+ // Sort paths and methods alphabetically for diff-friendly output
199
+ const sortedPathKeys = Object.keys(paths).sort((a, b) => a.localeCompare(b));
200
+ const sortedPaths = {};
201
+ for (const p of sortedPathKeys) {
202
+ const ops = paths[p];
203
+ const methodKeys = Object.keys(ops).sort((a, b) => a.localeCompare(b));
204
+ const sortedOps = {};
205
+ for (const m of methodKeys)
206
+ sortedOps[m] = ops[m];
207
+ sortedPaths[p] = sortedOps;
208
+ }
209
+ // Sort securitySchemes & parameters if present
210
+ let sortedSecuritySchemes;
211
+ if (securityBuilt.securitySchemes) {
212
+ sortedSecuritySchemes = {};
213
+ for (const k of Object.keys(securityBuilt.securitySchemes).sort((a, b) => a.localeCompare(b))) {
214
+ sortedSecuritySchemes[k] = securityBuilt.securitySchemes[k];
215
+ }
216
+ }
217
+ let sortedParameters;
218
+ if (Object.keys(securityBuilt.headerParameters).length) {
219
+ sortedParameters = {};
220
+ for (const k of Object.keys(securityBuilt.headerParameters).sort((a, b) => a.localeCompare(b))) {
221
+ sortedParameters[k] = securityBuilt.headerParameters[k];
222
+ }
223
+ }
224
+ // Ensure operation tags arrays are deterministic (alphabetical unique) for diff-friendly output
225
+ for (const p of Object.keys(sortedPaths)) {
226
+ const pathItem = sortedPaths[p];
227
+ for (const m of Object.keys(pathItem)) {
228
+ const op = pathItem[m];
229
+ if (Array.isArray(op.tags)) {
230
+ const tags = Array.from(new Set(op.tags)).map(t => String(t));
231
+ tags.sort((a, b) => a.localeCompare(b));
232
+ op.tags = tags;
233
+ }
234
+ }
235
+ }
236
+ // Build components object then sort its top-level keys for stable ordering
237
+ const componentsRaw = {
238
+ schemas: finalSchemas,
239
+ ...(sortedSecuritySchemes ? { securitySchemes: sortedSecuritySchemes } : {}),
240
+ ...(sortedParameters ? { parameters: sortedParameters } : {}),
241
+ };
242
+ const componentKeyOrder = Object.keys(componentsRaw).sort((a, b) => a.localeCompare(b));
243
+ const components = {};
244
+ for (const k of componentKeyOrder)
245
+ components[k] = componentsRaw[k];
246
+ const doc = {
247
+ openapi: "3.1.0",
248
+ // NOTE: jsonSchemaDialect intentionally omitted unless a future flag requires non-default dialect.
249
+ info: { title, version: infoVersion },
250
+ paths: sortedPaths,
251
+ components,
252
+ };
253
+ if (opts.description)
254
+ doc.info.description = opts.description;
255
+ if (opts.servers && opts.servers.length) {
256
+ doc.servers = opts.servers.map(u => ({ url: u }));
257
+ }
258
+ else {
259
+ // Provide a deterministic default relative server for spec completeness & gateway friendliness.
260
+ doc.servers = [{ url: "/" }];
261
+ }
262
+ if (opts.skipValidate !== true) {
263
+ try {
264
+ const parser = await import("@apidevtools/swagger-parser");
265
+ await parser.default.validate(JSON.parse(JSON.stringify(doc)));
266
+ console.log("OpenAPI validation: OK");
267
+ }
268
+ catch (e) {
269
+ console.error("OpenAPI validation failed:", e instanceof Error ? e.message : e);
270
+ throw e;
271
+ }
272
+ }
273
+ else {
274
+ console.log("OpenAPI validation skipped by flag");
275
+ }
276
+ // Determine base path for writing
277
+ let base = opts.outFile;
278
+ if (base) {
279
+ const extMatch = base.match(/\.(json|ya?ml)$/i);
280
+ if (extMatch) {
281
+ base = base.slice(0, -extMatch[0].length); // strip extension
282
+ }
283
+ }
284
+ let jsonPath;
285
+ let yamlPath;
286
+ if (opts.outFile) {
287
+ const dir = path.dirname(opts.outFile);
288
+ if (!fs.existsSync(dir))
289
+ fs.mkdirSync(dir, { recursive: true });
290
+ if (format === "json") {
291
+ jsonPath = base ? `${base}.json` : opts.outFile;
292
+ fs.writeFileSync(jsonPath, JSON.stringify(doc, null, 2), "utf8");
293
+ }
294
+ else if (format === "yaml") {
295
+ yamlPath = base ? `${base}.yaml` : opts.outFile.replace(/\.(json)$/i, ".yaml");
296
+ fs.writeFileSync(yamlPath, yaml.dump(doc), "utf8");
297
+ }
298
+ else { // both
299
+ jsonPath = `${base}.json`;
300
+ yamlPath = `${base}.yaml`;
301
+ fs.writeFileSync(jsonPath, JSON.stringify(doc, null, 2), "utf8");
302
+ fs.writeFileSync(yamlPath, yaml.dump(doc), "utf8");
303
+ }
304
+ }
305
+ return { doc, jsonPath, yamlPath };
306
+ }
307
+ function safeJson(file) {
308
+ try {
309
+ return JSON.parse(fs.readFileSync(file, "utf8"));
310
+ }
311
+ catch (e) {
312
+ console.warn(`⚠️ Failed to parse JSON file '${file}': ${e instanceof Error ? e.message : String(e)}`);
313
+ return undefined;
314
+ }
315
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Authentication scheme types supported in the OpenAPI specification
3
+ *
4
+ * @property {"none"} string No authentication required
5
+ * @property {"basic"} string HTTP Basic authentication
6
+ * @property {"bearer"} string HTTP Bearer token authentication
7
+ * @property {"apiKey"} string API key authentication
8
+ * @property {"oauth2"} string OAuth 2.0 authentication
9
+ */
10
+ export type AuthScheme = "none" | "basic" | "bearer" | "apiKey" | "oauth2";
11
+ /**
12
+ * Security configuration schema for the OpenAPI generation
13
+ *
14
+ * @interface SecurityConfig
15
+ * @property {Object} [global] - Global security settings applied to all operations by default
16
+ * @property {AuthScheme} [global.scheme] - Default authentication scheme
17
+ * @property {Object} [global.apiKey] - API key configuration (when scheme is "apiKey")
18
+ * @property {Object} [global.bearer] - Bearer token configuration (when scheme is "bearer")
19
+ * @property {Object} [global.basic] - Basic auth configuration (when scheme is "basic")
20
+ * @property {Object} [global.oauth2] - OAuth2 configuration (when scheme is "oauth2")
21
+ * @property {Array<Object>} [global.headers] - Global security headers
22
+ * @property {Record<string, Object>} [overrides] - Operation-specific security overrides
23
+ */
24
+ export type SecurityConfig = {
25
+ global?: {
26
+ scheme?: AuthScheme;
27
+ apiKey?: {
28
+ in: "header" | "query" | "cookie";
29
+ name: string;
30
+ };
31
+ bearer?: {
32
+ bearerFormat?: string;
33
+ };
34
+ basic?: Record<string, never>;
35
+ oauth2?: {
36
+ flows: Record<string, any>;
37
+ };
38
+ headers?: Array<{
39
+ name: string;
40
+ required?: boolean;
41
+ schema?: any;
42
+ }>;
43
+ };
44
+ overrides?: Record<string, {
45
+ scheme?: AuthScheme;
46
+ headers?: Array<{
47
+ name: string;
48
+ required?: boolean;
49
+ schema?: any;
50
+ }>;
51
+ }>;
52
+ };
53
+ /**
54
+ * Built security components for OpenAPI generation
55
+ *
56
+ * @interface BuiltSecurity
57
+ * @property {Record<string, any>} [securitySchemes] - Security schemes defined for the API
58
+ * @property {Record<string, any>} headerParameters - Header parameters for security
59
+ * @property {Record<string, any[]>} opSecurity - Operation security requirements
60
+ * @property {Record<string, string[]>} opHeaderParameters - Operation-specific header parameters
61
+ */
62
+ export type BuiltSecurity = {
63
+ securitySchemes?: Record<string, any>;
64
+ headerParameters: Record<string, any>;
65
+ opSecurity: Record<string, any[] | undefined>;
66
+ opHeaderParameters: Record<string, string[]>;
67
+ };
68
+ /**
69
+ * Load security configuration from a JSON file
70
+ *
71
+ * @param {string} [filePath] - Path to the security configuration file
72
+ * @returns {SecurityConfig|undefined} Parsed security configuration object or undefined if loading failed
73
+ */
74
+ export declare function loadSecurityConfig(filePath?: string): SecurityConfig | undefined;
75
+ /**
76
+ * Build security schemes and parameters for OpenAPI generation
77
+ *
78
+ * @param {SecurityConfig} [cfg] - Security configuration object
79
+ * @returns {BuiltSecurity} Object containing built security schemes and parameters
80
+ */
81
+ export declare function buildSecurity(cfg?: SecurityConfig): BuiltSecurity;
82
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/openapi/security.ts"],"names":[],"mappings":"AAkBA;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,UAAU,CAAC;QACpB,MAAM,CAAC,EAAE;YAAE,EAAE,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QAC7D,MAAM,CAAC,EAAE;YAAE,YAAY,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;SAAE,CAAC;QACxC,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,GAAG,CAAA;SAAE,CAAC,CAAC;KACrE,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QACzB,MAAM,CAAC,EAAE,UAAU,CAAC;QACpB,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,GAAG,CAAA;SAAE,CAAC,CAAA;KACpE,CAAC,CAAC;CACJ,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;IAC9C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9C,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAShF;AAeD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,cAAc,GAAG,aAAa,CA+FjE"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * OpenAPI Security Configuration
3
+ *
4
+ * This module handles security scheme generation for the OpenAPI specification.
5
+ * It provides functionality to define authentication and authorization requirements
6
+ * for the API, including:
7
+ *
8
+ * - Basic authentication
9
+ * - Bearer token authentication
10
+ * - API key authentication
11
+ * - OAuth2 flows
12
+ * - Custom security headers
13
+ *
14
+ * The module supports both global security requirements and operation-specific
15
+ * security overrides through an external JSON configuration file.
16
+ */
17
+ import fs from "node:fs";
18
+ /**
19
+ * Load security configuration from a JSON file
20
+ *
21
+ * @param {string} [filePath] - Path to the security configuration file
22
+ * @returns {SecurityConfig|undefined} Parsed security configuration object or undefined if loading failed
23
+ */
24
+ export function loadSecurityConfig(filePath) {
25
+ if (!filePath)
26
+ return undefined;
27
+ try {
28
+ const raw = fs.readFileSync(filePath, "utf8");
29
+ return JSON.parse(raw);
30
+ }
31
+ catch (e) {
32
+ console.warn(`⚠️ Failed to read security config '${filePath}': ${e instanceof Error ? e.message : String(e)}`);
33
+ return undefined;
34
+ }
35
+ }
36
+ /**
37
+ * Generate a canonical parameter component name from a header name
38
+ *
39
+ * @param {string} headerName - Original header name
40
+ * @returns {string} Canonicalized component name
41
+ */
42
+ function makeParamComponentName(headerName) {
43
+ return headerName
44
+ .replace(/[^A-Za-z0-9]+/g, "_")
45
+ .replace(/^_+|_+$/g, "")
46
+ || "X_Header";
47
+ }
48
+ /**
49
+ * Build security schemes and parameters for OpenAPI generation
50
+ *
51
+ * @param {SecurityConfig} [cfg] - Security configuration object
52
+ * @returns {BuiltSecurity} Object containing built security schemes and parameters
53
+ */
54
+ export function buildSecurity(cfg) {
55
+ const securitySchemes = {};
56
+ const headerParameters = {};
57
+ const opSecurity = {};
58
+ const opHeaderParameters = {};
59
+ if (!cfg || !cfg.global) {
60
+ return { securitySchemes: undefined, headerParameters, opSecurity, opHeaderParameters };
61
+ }
62
+ const global = cfg.global;
63
+ const schemeName = "defaultAuth";
64
+ const scheme = global.scheme || "none";
65
+ const hasGlobal = scheme !== "none";
66
+ if (scheme !== "none") {
67
+ switch (scheme) {
68
+ case "basic":
69
+ securitySchemes[schemeName] = { type: "http", scheme: "basic" };
70
+ break;
71
+ case "bearer":
72
+ securitySchemes[schemeName] = { type: "http", scheme: "bearer", ...(global.bearer || {}) };
73
+ break;
74
+ case "apiKey":
75
+ securitySchemes[schemeName] = { type: "apiKey", ...(global.apiKey || { in: "header", name: "X-API-Key" }) };
76
+ break;
77
+ case "oauth2":
78
+ securitySchemes[schemeName] = { type: "oauth2", ...(global.oauth2 || { flows: {} }) };
79
+ break;
80
+ }
81
+ }
82
+ // Global headers
83
+ for (const h of global.headers || []) {
84
+ const compName = makeParamComponentName(h.name);
85
+ headerParameters[compName] = {
86
+ name: h.name,
87
+ in: "header",
88
+ required: !!h.required,
89
+ schema: h.schema || { type: "string" },
90
+ };
91
+ }
92
+ // Default op security (inherit global scheme)
93
+ // (If scheme is none we omit entirely)
94
+ // Overrides can set scheme to none to remove security
95
+ for (const [opName, override] of Object.entries(cfg.overrides || {})) {
96
+ const oScheme = override.scheme ?? scheme;
97
+ if (oScheme === "none") {
98
+ opSecurity[opName] = undefined;
99
+ }
100
+ else if (oScheme === scheme) {
101
+ opSecurity[opName] = hasGlobal ? [{ [schemeName]: [] }] : undefined;
102
+ }
103
+ else {
104
+ // Different scheme per operation -> create ad-hoc component name
105
+ const altName = `${schemeName}_${oScheme}`;
106
+ if (!securitySchemes[altName]) {
107
+ switch (oScheme) {
108
+ case "basic":
109
+ securitySchemes[altName] = { type: "http", scheme: "basic" };
110
+ break;
111
+ case "bearer":
112
+ securitySchemes[altName] = { type: "http", scheme: "bearer" };
113
+ break;
114
+ case "apiKey":
115
+ securitySchemes[altName] = { type: "apiKey", ...(global.apiKey || { in: "header", name: "X-API-Key" }) };
116
+ break;
117
+ case "oauth2":
118
+ securitySchemes[altName] = { type: "oauth2", ...(global.oauth2 || { flows: {} }) };
119
+ break;
120
+ }
121
+ }
122
+ opSecurity[opName] = [{ [altName]: [] }];
123
+ }
124
+ // Per-op headers merge global + override specific headers
125
+ const headers = [...(global.headers || []), ...(override.headers || [])];
126
+ opHeaderParameters[opName] = headers.map(h => makeParamComponentName(h.name));
127
+ for (const h of override.headers || []) {
128
+ const compName = makeParamComponentName(h.name);
129
+ if (!headerParameters[compName]) {
130
+ headerParameters[compName] = {
131
+ name: h.name,
132
+ in: "header",
133
+ required: !!h.required,
134
+ schema: h.schema || { type: "string" },
135
+ };
136
+ }
137
+ }
138
+ }
139
+ return {
140
+ securitySchemes: Object.keys(securitySchemes).length ? securitySchemes : undefined,
141
+ headerParameters,
142
+ opSecurity,
143
+ opHeaderParameters
144
+ };
145
+ }
@@ -0,0 +1,37 @@
1
+ import { type GenerateOpenAPIOptions } from "./openapi/generateOpenAPI.js";
2
+ import type { CompilerOptions } from "./config.js";
3
+ /**
4
+ * Configuration options for the generation pipeline
5
+ *
6
+ * @interface PipelineOptions
7
+ * @property {string} wsdl - Path or URL to the WSDL file to process
8
+ * @property {string} outDir - Output directory for generated TypeScript artifacts
9
+ * @property {Partial<CompilerOptions>} [compiler] - Compiler options for type generation
10
+ * @property {object} [openapi] - OpenAPI generation configuration (optional)
11
+ * @property {string} [openapi.outFile] - Optional output path for OpenAPI specification
12
+ */
13
+ export interface PipelineOptions {
14
+ wsdl: string;
15
+ outDir: string;
16
+ compiler?: Partial<CompilerOptions>;
17
+ openapi?: Omit<GenerateOpenAPIOptions, "wsdl" | "catalogFile" | "compiledCatalog"> & {
18
+ outFile?: string;
19
+ };
20
+ }
21
+ /**
22
+ * Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI
23
+ *
24
+ * This function orchestrates the entire process:
25
+ * 1. Loads and parses the WSDL from file or URL
26
+ * 2. Compiles the WSDL into an internal catalog representation
27
+ * 3. Emits TypeScript client code, types, and utilities
28
+ * 4. Optionally emits a JSON catalog for introspection
29
+ * 5. Optionally generates an OpenAPI 3.1 specification
30
+ *
31
+ * @param {PipelineOptions} opts - Configuration options for the pipeline
32
+ * @returns {Promise<{compiled: any}>} - The compiled catalog for potential further processing
33
+ */
34
+ export declare function runGenerationPipeline(opts: PipelineOptions): Promise<{
35
+ compiled: import("./compiler/schemaCompiler.js").CompiledCatalog;
36
+ }>;
37
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAGjD;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3G;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe;;GAgDhE"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * TypeScript WSDL Client Generation Pipeline
3
+ *
4
+ * This file implements the high-level generation pipeline that orchestrates the entire
5
+ * process of converting a WSDL file into TypeScript client code and optionally an OpenAPI
6
+ * specification. It serves as an integration layer between the various stages of the generation
7
+ * process.
8
+ */
9
+ import path from "node:path";
10
+ import fs from "node:fs";
11
+ import { loadWsdl } from "./loader/wsdlLoader.js";
12
+ import { compileCatalog } from "./compiler/schemaCompiler.js";
13
+ import { emitClient } from "./emit/clientEmitter.js";
14
+ import { emitTypes } from "./emit/typesEmitter.js";
15
+ import { emitUtils } from "./emit/utilsEmitter.js";
16
+ import { emitCatalog } from "./emit/catalogEmitter.js";
17
+ import { generateOpenAPI } from "./openapi/generateOpenAPI.js";
18
+ import { TYPESCRIPT_WSDL_CLIENT_DEFAULT_COMPLIER_OPTIONS } from "./config.js";
19
+ /**
20
+ * Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI
21
+ *
22
+ * This function orchestrates the entire process:
23
+ * 1. Loads and parses the WSDL from file or URL
24
+ * 2. Compiles the WSDL into an internal catalog representation
25
+ * 3. Emits TypeScript client code, types, and utilities
26
+ * 4. Optionally emits a JSON catalog for introspection
27
+ * 5. Optionally generates an OpenAPI 3.1 specification
28
+ *
29
+ * @param {PipelineOptions} opts - Configuration options for the pipeline
30
+ * @returns {Promise<{compiled: any}>} - The compiled catalog for potential further processing
31
+ */
32
+ export async function runGenerationPipeline(opts) {
33
+ // Merge provided compiler options with defaults, ensuring required fields are set
34
+ const finalCompiler = {
35
+ ...TYPESCRIPT_WSDL_CLIENT_DEFAULT_COMPLIER_OPTIONS,
36
+ catalog: opts.compiler?.catalog ?? true, // default to emitting catalog in pipeline mode
37
+ ...(opts.compiler || {}),
38
+ wsdl: opts.wsdl,
39
+ out: opts.outDir,
40
+ };
41
+ // Step 1: Load and parse the WSDL document
42
+ const wsdlCatalog = await loadWsdl(opts.wsdl);
43
+ // Step 2: Compile the WSDL into a structured catalog
44
+ const compiled = compileCatalog(wsdlCatalog, finalCompiler);
45
+ // Step 3: Ensure the output directory exists
46
+ fs.mkdirSync(opts.outDir, { recursive: true });
47
+ // Step 4: Emit TypeScript artifacts
48
+ emitClient(path.join(opts.outDir, "client.ts"), compiled);
49
+ emitTypes(path.join(opts.outDir, "types.ts"), compiled);
50
+ emitUtils(path.join(opts.outDir, "utils.ts"), compiled);
51
+ // Step 5: Optionally emit the JSON catalog for introspection
52
+ if (finalCompiler.catalog) {
53
+ emitCatalog(path.join(opts.outDir, "catalog.json"), compiled);
54
+ }
55
+ // Step 6: Optionally generate OpenAPI specification
56
+ if (opts.openapi) {
57
+ // Determine output path for OpenAPI specification
58
+ let resolvedOut = opts.openapi.outFile;
59
+ if (!resolvedOut) {
60
+ const yamlPreferred = !!opts.openapi.asYaml;
61
+ resolvedOut = path.join(opts.outDir, yamlPreferred ? "openapi.yaml" : "openapi.json");
62
+ }
63
+ // Generate the OpenAPI specification using the compiled catalog
64
+ await generateOpenAPI({
65
+ ...opts.openapi,
66
+ compiledCatalog: compiled,
67
+ outFile: resolvedOut,
68
+ });
69
+ }
70
+ // Return the compiled catalog for potential further processing
71
+ return { compiled };
72
+ }