@techspokes/typescript-wsdl-client 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -23,7 +23,7 @@ Reads WSDL/XSD (with imports) and emits a small, typed client you can compile in
23
23
  * Deterministic metadata (`ATTR_SPEC`, `PROP_META`) for clean JSON ⇄ SOAP mapping
24
24
  * ESM **and** CommonJS friendly output
25
25
 
26
- Vendor: **[TechSpokes](https://www.techspokes.com)** · Contact: **[contact page](https://www.techspokes.com/contact/)"**
26
+ Vendor: **[TechSpokes](https://www.techspokes.com)** · Contact: **[contact page](https://www.techspokes.com/contact/)**
27
27
  Maintainer: **Serge Liatko** ([@sergeliatko](https://github.com/sergeliatko)) · GitHub org: [@techspokes](https://github.com/techspokes)
28
28
 
29
29
  ---
@@ -33,7 +33,7 @@ Maintainer: **Serge Liatko** ([@sergeliatko](https://github.com/sergeliatko)) ·
33
33
  Install the generator (dev-time) in your project:
34
34
 
35
35
  ```bash
36
- npm i -D typescript-wsdl-client
36
+ npm i -D @techspokes/typescript-wsdl-client
37
37
  # your app will use node-soap at runtime:
38
38
  npm i soap
39
39
  ```
@@ -49,17 +49,28 @@ Use the generated client:
49
49
  ```ts
50
50
  // ESM / NodeNext app
51
51
  import { createSoapClient } from "./generated/my/runtime.js";
52
- import { GeneratedSoapClient } from "./generated/my/client.js";
52
+ // class name defaults to <ServiceName>SoapClient (from WSDL), e.g., MyServiceSoapClient
53
+ import { MyServiceSoapClient } from "./generated/my/client.js";
53
54
 
54
- const soap = await createSoapClient({ wsdlUrl: "https://example.com/MyService?wsdl" });
55
- const client = new GeneratedSoapClient(soap, "$attributes"); // attributes key optional
55
+ // optional: pass security straight into createSoapClient (node-soap security instance)
56
+ import soap from "soap";
57
+ const security = new soap.WSSecurity("user", "pass");
56
58
 
59
+ const soapClient = await createSoapClient({
60
+ wsdlUrl: "https://example.com/MyService?wsdl",
61
+ security, // or configure after creation: (await createSoapClient(...)).setSecurity(security)
62
+ });
63
+
64
+ const client = new MyServiceSoapClient(soapClient, "$attributes"); // attributes key optional
65
+
66
+ // Example: To set literal text content for an XML element, use the reserved `$value` key.
67
+ // Other keys in the object represent XML attributes (e.g. `MyAttribute`) and child elements (e.g. `MyElement`).
68
+ // This lets you build mixed-content XML: `$value` for text, attribute keys for XML attributes, and element keys for nested elements.
57
69
  const rs = await client.MyOperation({
58
70
  MyOperationRQ: {
59
71
  MyAttribute: "passed as attribute",
60
72
  MyElement: "passed as child element",
61
- // ...other fields
62
- // attributes appear as top-level props; child elements as nested objects
73
+ $value: "passed text content here",
63
74
  }
64
75
  });
65
76
 
@@ -70,6 +81,17 @@ console.log(rs);
70
81
 
71
82
  ---
72
83
 
84
+ ## Security and WS-Policy hints
85
+
86
+ - node-soap leaves security up to you. Create a security instance (e.g., `new soap.BasicAuthSecurity(...)`, `new soap.WSSecurity(...)`, `new soap.ClientSSLSecurity(...)`) and set it on the client via `client.setSecurity(...)`.
87
+ - The runtime factory `createSoapClient({ wsdlUrl, endpoint?, wsdlOptions?, security? })` accepts an optional `security` and will call `client.setSecurity(security)` for you.
88
+ - The generated client includes minimal WS-Policy hints if your WSDL embeds inline `wsp:Policy` under `wsdl:binding` or `wsdl:binding/wsdl:operation`.
89
+ - Hints are surfaced in method JSDoc as “Security (WSDL policy hint): …” and in `operations.json`/`operations.ts` as a `security: string[]` field (e.g., `usernameToken`, `https`, `x509`, `messageSecurity`).
90
+ - These hints are best-effort and not authoritative (no `wsp:PolicyReference` dereferencing yet). They’re intended to nudge you to configure the right security.
91
+ - At runtime, if an operation has hints and your client has no `security` configured, a console warning is emitted.
92
+
93
+ ---
94
+
73
95
  ## CLI
74
96
 
75
97
  ```
@@ -107,11 +129,19 @@ wsdl-tsc --wsdl <path-or-url> --out <dir> [options]
107
129
  | `--imports` | string | js, ts, bare | js | Intra-generated import specifiers: '.js', '.ts', or bare |
108
130
  | `--ops-ts` | boolean | true, false | true | Emit `operations.ts` instead of JSON |
109
131
  | `--attributes-key` | string | any | $attributes | Key used by runtime marshaller for XML attributes |
132
+ | `--client-name` | string | any | — | Override the exported client class name (exact) |
110
133
  | `--int64-as` | string | string, number, bigint | string | How to map xs:long/xs:unsignedLong |
111
134
  | `--bigint-as` | string | string, number | string | How to map xs:integer family (positive/nonNegative/etc.) |
112
135
  | `--decimal-as` | string | string, number | string | How to map xs:decimal (money/precision) |
113
136
  | `--date-as` | string | string, Date | string | How to map date/time/duration types |
114
137
 
138
+ ### Client naming
139
+
140
+ - By default, the generated client class is named after the WSDL service: `<ServiceName>SoapClient`.
141
+ - If the service name can’t be determined, we fall back to the WSDL filename: `<WsdlFileBaseName>SoapClient`.
142
+ - If neither is available, we fall back to `GeneratedSoapClient`.
143
+ - You can override the name entirely with `--client-name MyCustomClient` (the exact export name will be used).
144
+
115
145
  **Primitive mapping (safe defaults)**
116
146
 
117
147
  Defaults are **string-first** to avoid precision & timezone surprises:
@@ -195,8 +225,8 @@ TypeScript will compile to `require("./runtime")` cleanly.
195
225
  ## Programmatic API (optional)
196
226
 
197
227
  ```ts
198
- import { compileCatalog, xsdToTsPrimitive, type CompilerOptions } from "typescript-wsdl-client";
199
- import { loadWsdlCatalog } from "typescript-wsdl-client/internal-or-your-loader"; // if you expose it
228
+ import { compileCatalog, xsdToTsPrimitive, type CompilerOptions } from "@techspokes/typescript-wsdl-client";
229
+ import { loadWsdlCatalog } from "@techspokes/typescript-wsdl-client/internal-or-your-loader"; // if you expose it
200
230
 
201
231
  const catalog = await loadWsdlCatalog("./spec/wsdl/MyService.wsdl");
202
232
  const compiled = compileCatalog(catalog, {
@@ -237,6 +267,7 @@ npx tsc --noEmit --module NodeNext --moduleResolution NodeNext --target ES2022 .
237
267
  * **“Cannot find './runtime.js'”** — Your app must be `module: "NodeNext"` when using `--imports js`.
238
268
  Use `--imports bare` for CommonJS apps.
239
269
  * **“node-soap not found”** — Install it in your **app**: `npm i soap`.
270
+ * **“Security required?”** — If your generated client warns or JSDoc shows a security hint, configure node-soap security: `client.setSecurity(new soap.WSSecurity(...))` or pass `security` to `createSoapClient`.
240
271
 
241
272
  ---
242
273
 
package/dist/cli.js CHANGED
@@ -37,6 +37,10 @@ const argv = await yargs(hideBin(process.argv))
37
37
  type: "string",
38
38
  default: "$attributes",
39
39
  desc: "Key used by runtime marshaller for XML attributes",
40
+ })
41
+ .option("client-name", {
42
+ type: "string",
43
+ desc: "Override the generated client class name (exact export name)",
40
44
  })
41
45
  // Primitive mapping knobs (safe defaults)
42
46
  .option("int64-as", {
@@ -94,5 +98,6 @@ emitClient(path.join(outDir, "client.ts"), compiled, {
94
98
  // @ts-ignore runtime-only options for emitter
95
99
  importExt,
96
100
  attributesKey: String(argv["attributes-key"]),
101
+ clientName: argv["client-name"],
97
102
  });
98
103
  console.log(`✅ Generated TypeScript client in ${outDir}`);
@@ -43,8 +43,11 @@ export type CompiledCatalog = {
43
43
  soapAction: string;
44
44
  inputElement?: QName;
45
45
  outputElement?: QName;
46
+ security?: string[];
46
47
  }>;
47
48
  wsdlTargetNS: string;
49
+ serviceName?: string;
50
+ wsdlUri: string;
48
51
  };
49
52
  /**
50
53
  * Compile a WSDL catalog into an internal representation (CompiledCatalog).
@@ -1 +1 @@
1
- {"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAU3D,MAAM,MAAM,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,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;KACtB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;QAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;KAC/C,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,KAAK,CAAC;QACrB,aAAa,CAAC,EAAE,KAAK,CAAC;KACvB,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAsBF;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,eAAe,GAAG,eAAe,CA+exF"}
1
+ {"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAU3D,MAAM,MAAM,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,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;KACtB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;QAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;KAC/C,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,KAAK,CAAC;QACrB,aAAa,CAAC,EAAE,KAAK,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA6DF;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,eAAe,GAAG,eAAe,CA2gBxF"}
@@ -13,6 +13,56 @@ function makeInlineTypeName(parentTypeName, propName, _max) {
13
13
  }
14
14
  return `${base}Anon`;
15
15
  }
16
+ // --- Minimal WS-Policy scanning (inline policies only; PolicyReference not resolved) ---
17
+ function collectSecurityFromPolicyNodes(policyNodes) {
18
+ const found = new Set();
19
+ const targets = new Set([
20
+ "UsernameToken",
21
+ "TransportBinding",
22
+ "HttpsToken",
23
+ "TransportToken",
24
+ "AsymmetricBinding",
25
+ "SymmetricBinding",
26
+ "SignedSupportingTokens",
27
+ "X509Token",
28
+ ]);
29
+ const visit = (n) => {
30
+ if (n == null || typeof n !== "object")
31
+ return;
32
+ for (const [k, v] of Object.entries(n)) {
33
+ if (k.startsWith("@_"))
34
+ continue; // attribute
35
+ // localName match (prefix-agnostic) by checking tail after ':' or whole key if no ':'
36
+ const ln = k.includes(":") ? k.split(":").pop() : k;
37
+ if (targets.has(ln)) {
38
+ switch (ln) {
39
+ case "UsernameToken":
40
+ found.add("usernameToken");
41
+ break;
42
+ case "HttpsToken":
43
+ case "TransportBinding":
44
+ case "TransportToken":
45
+ found.add("https");
46
+ break;
47
+ case "X509Token":
48
+ found.add("x509");
49
+ break;
50
+ case "AsymmetricBinding":
51
+ case "SymmetricBinding":
52
+ case "SignedSupportingTokens":
53
+ found.add("messageSecurity");
54
+ break;
55
+ }
56
+ }
57
+ // recurse
58
+ if (v && typeof v === "object")
59
+ visit(v);
60
+ }
61
+ };
62
+ for (const p of policyNodes)
63
+ visit(p);
64
+ return Array.from(found);
65
+ }
16
66
  /**
17
67
  * Compile a WSDL catalog into an internal representation (CompiledCatalog).
18
68
  * Steps:
@@ -469,11 +519,38 @@ export function compileCatalog(cat, _opts) {
469
519
  return { name, soapAction: bOps.get(name) || "", inputElement, outputElement };
470
520
  })
471
521
  .filter((x) => x != null));
522
+ // --- WS-Policy: scan for security requirements (inline policies only) ---
523
+ const bindingPolicies = getChildrenWithLocalName(soapBinding || {}, "Policy");
524
+ const bindingSec = collectSecurityFromPolicyNodes(bindingPolicies);
525
+ for (const op of ops) {
526
+ const bo = opsNodes.find(o => o?.["@_name"] === op.name) || {};
527
+ const opPolicies = getChildrenWithLocalName(bo, "Policy");
528
+ const opSec = collectSecurityFromPolicyNodes(opPolicies);
529
+ const secSet = new Set([...bindingSec, ...opSec]);
530
+ op.security = Array.from(secSet);
531
+ }
532
+ // --- Service discovery (for client naming) ---
533
+ let serviceName;
534
+ const soapBindingName = soapBinding?.["@_name"];
535
+ const serviceDefs = normalizeArray(defs?.["wsdl:service"] || defs?.["service"]);
536
+ const serviceUsingBinding = serviceDefs.find(s => {
537
+ const ports = getChildrenWithLocalName(s, "port");
538
+ return ports.some(p => {
539
+ const bq = p?.["@_binding"];
540
+ if (!bq || !soapBindingName)
541
+ return false;
542
+ const q = resolveQName(bq, tns, cat.prefixMap);
543
+ return q.local === soapBindingName; // match by local name
544
+ });
545
+ });
546
+ serviceName = serviceUsingBinding?.["@_name"] || serviceDefs[0]?.["@_name"];
472
547
  return {
473
548
  types: typesList,
474
549
  aliases: aliasList,
475
550
  meta: { attrSpec, childType, propMeta },
476
551
  operations: ops,
477
552
  wsdlTargetNS: defs?.["@_targetNamespace"] || "",
553
+ serviceName,
554
+ wsdlUri: cat.wsdlUri,
478
555
  };
479
556
  }
package/dist/config.d.ts CHANGED
@@ -23,6 +23,11 @@ export type CompilerOptions = {
23
23
  * Attribute bag key for the runtime mapper (node-soap).
24
24
  */
25
25
  attributesKey?: string;
26
+ /**
27
+ * Optional override for the generated client class name.
28
+ * If provided, the emitter will export this exact class name.
29
+ */
30
+ clientName?: string;
26
31
  /**
27
32
  * Controls low-level mapping of XSD primitives to TypeScript types. Safe defaults are provided.
28
33
  */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC;IAClC;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC3B;;OAEG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B;;OAEG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,eAM5B,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC;IAClC;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC3B;;OAEG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B;;OAEG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,eAM5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"clientEmitter.d.ts","sourceRoot":"","sources":["../../src/emit/clientEmitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,QAmCpH"}
1
+ {"version":3,"file":"clientEmitter.d.ts","sourceRoot":"","sources":["../../src/emit/clientEmitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,QAkDpH"}
@@ -7,7 +7,18 @@ export function emitClient(outFile, compiled, opts) {
7
7
  lines.push(`import { ATTR_SPEC, CHILD_TYPE, PROP_META } from "./meta${ext}";`);
8
8
  lines.push(`import type * as T from "./types${ext}";`);
9
9
  lines.push("");
10
- lines.push(`export class GeneratedSoapClient {`);
10
+ // Derive class name: prefer explicit override, else WSDL service name, else base filename, else default
11
+ const overrideName = (opts.clientName || "").trim();
12
+ const svcName = compiled.serviceName && pascal(compiled.serviceName);
13
+ const fileBase = (() => {
14
+ const uri = compiled.wsdlUri || "";
15
+ // extract last path segment and strip extension for both URL and file path
16
+ const seg = uri.split(/[\\/]/).pop() || "";
17
+ const noExt = seg.replace(/\.[^.]+$/, "");
18
+ return noExt ? pascal(noExt) : "";
19
+ })();
20
+ const className = overrideName || ((svcName || fileBase) ? `${svcName || fileBase}SoapClient` : "GeneratedSoapClient");
21
+ lines.push(`export class ${className} {`);
11
22
  lines.push(` constructor(private source: string | any, private attributesKey: string = "${opts.attributesKey || "$attributes"}") {}`);
12
23
  lines.push(` async _client() {`);
13
24
  lines.push(` if (typeof this.source === 'string') return createSoapClient({ wsdlUrl: this.source });`);
@@ -21,9 +32,13 @@ export function emitClient(outFile, compiled, opts) {
21
32
  const outTs = outTypeName ? `T.${outTypeName}` : `any`;
22
33
  const inMetaKey = inTypeName ?? m;
23
34
  const outMetaKey = outTypeName ?? m;
24
- lines.push(` /** SOAPAction: ${op.soapAction} */`);
35
+ const secHints = Array.isArray(op.security) && op.security.length ? op.security : [];
36
+ lines.push(` /** SOAPAction: ${op.soapAction}${secHints.length ? `\n * Security (WSDL policy hint): ${secHints.join(", ")}` : ""} */`);
25
37
  lines.push(` async ${m}(args: ${inTs}): Promise<${outTs}> {`);
26
38
  lines.push(` const c: any = await this._client();`);
39
+ if (secHints.length) {
40
+ lines.push(` if (!c || !c.security) { console.warn("[wsdl-client] Operation '${m}' may require security: ${secHints.join(", ")}. Configure client.setSecurity(...) or pass { security } to createSoapClient()."); }`);
41
+ }
27
42
  lines.push(` const meta = { ATTR_SPEC, CHILD_TYPE, PROP_META } as const;`);
28
43
  lines.push(` const soapArgs = toSoapArgs(args as any, "${inMetaKey}", meta, this.attributesKey);`);
29
44
  lines.push(` return new Promise((resolve, reject) => {`);
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeEmitter.d.ts","sourceRoot":"","sources":["../../src/emit/runtimeEmitter.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,QAuF1C"}
1
+ {"version":3,"file":"runtimeEmitter.d.ts","sourceRoot":"","sources":["../../src/emit/runtimeEmitter.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,QAoF1C"}
@@ -9,6 +9,7 @@ export type CreateSoapClientOptions = {
9
9
  endpoint?: string;
10
10
  wsdlOptions?: Record<string, any>;
11
11
  requestOptions?: Record<string, any>;
12
+ security?: any; // pass an instance of a node-soap security class, e.g., new soap.WSSecurity("user","pass")
12
13
  }
13
14
  export type AttrSpec = Record<string, readonly string[]>;
14
15
  export type ChildType = Record<string, Readonly<Record<string, string>>>;
@@ -17,12 +18,8 @@ export type PropMeta = Record<string, Readonly<Record<string, any>>>;
17
18
  export async function createSoapClient(opts: CreateSoapClientOptions): Promise<any> {
18
19
  const client = await soap.createClientAsync(opts.wsdlUrl, opts.wsdlOptions || {});
19
20
  if (opts.endpoint) client.setEndpoint(opts.endpoint);
20
- if (opts.requestOptions) {
21
- const { wsdlUrl, endpoint, wsdlOptions, ...req } = opts as any;
22
- if (client.setSecurity && req) {
23
- // security is caller's responsibility (if needed)
24
- }
25
- }
21
+ if (opts.security) client.setSecurity(opts.security);
22
+ // security and any request-specific configuration are the caller's responsibility
26
23
  return client;
27
24
  }
28
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techspokes/typescript-wsdl-client",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "TypeScript WSDL → SOAP client generator with full xs:attribute support, complex types, sequences, inheritance, and namespace-collision merging.",
5
5
  "keywords": [
6
6
  "wsdl",