@techspokes/typescript-wsdl-client 0.2.2 → 0.2.5
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 +69 -193
- package/dist/compiler/schemaCompiler.d.ts +15 -0
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +26 -21
- package/dist/emit/clientEmitter.d.ts.map +1 -1
- package/dist/emit/clientEmitter.js +19 -4
- package/dist/emit/typesEmitter.d.ts.map +1 -1
- package/dist/emit/typesEmitter.js +74 -26
- package/dist/util/xml.d.ts.map +1 -1
- package/dist/util/xml.js +29 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,285 +11,160 @@
|
|
|
11
11
|
[](https://github.com/techspokes)
|
|
12
12
|
[](https://github.com/sponsors/TechSpokes)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
Reads WSDL/XSD (with imports) and emits a small, typed client you can compile into your app.
|
|
14
|
+
## Introduction
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
* `complexType` (`sequence` / `all` / `choice`)
|
|
19
|
-
* `simpleContent` / `complexContent` (`extension` + `restriction`)
|
|
20
|
-
* Inline **anonymous** types (auto-named, stable)
|
|
21
|
-
* Global `element @ref` resolution
|
|
22
|
-
* Duplicate/local-name **merge** across schemas/namespaces
|
|
23
|
-
* Deterministic metadata (`ATTR_SPEC`, `PROP_META`) for clean JSON ⇄ SOAP mapping
|
|
24
|
-
* ESM **and** CommonJS friendly output
|
|
16
|
+
**TypeScript WSDL Client** is a generator that converts WSDL/XSD files into a fully-typed SOAP client for TypeScript. It simplifies SOAP integration by generating deterministic, type-safe code that works seamlessly with modern TypeScript and Node.js environments.
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
Key features:
|
|
19
|
+
- **Typed SOAP client**: Generates TypeScript interfaces and runtime code.
|
|
20
|
+
- **Deterministic metadata**: Ensures clean JSON ⇄ SOAP mapping.
|
|
21
|
+
- **ESM and CommonJS support**: Compatible with modern and legacy module systems.
|
|
22
|
+
- **Customizable mappings**: Control how XML primitives (e.g., `xs:decimal`, `xs:date`) are mapped.
|
|
23
|
+
|
|
24
|
+
Vendor: **[TechSpokes](https://www.techspokes.com)**
|
|
25
|
+
Maintainer: **Serge Liatko** ([@sergeliatko](https://github.com/sergeliatko))
|
|
28
26
|
|
|
29
27
|
---
|
|
30
28
|
|
|
31
|
-
##
|
|
29
|
+
## Installation
|
|
32
30
|
|
|
33
|
-
Install the generator
|
|
31
|
+
Install the generator as a development dependency:
|
|
34
32
|
|
|
35
33
|
```bash
|
|
36
34
|
npm i -D @techspokes/typescript-wsdl-client
|
|
37
|
-
#
|
|
35
|
+
# Your app will use node-soap at runtime:
|
|
38
36
|
npm i soap
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
### Generate a SOAP Client
|
|
44
|
+
|
|
45
|
+
Run the following command to generate a client from your WSDL file:
|
|
42
46
|
|
|
43
47
|
```bash
|
|
44
48
|
npx wsdl-tsc --wsdl ./spec/wsdl/MyService.wsdl --out ./src/generated/my --imports js --ops-ts
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
Use the
|
|
51
|
+
### Use the Generated Client
|
|
48
52
|
|
|
49
53
|
```ts
|
|
50
|
-
// ESM / NodeNext app
|
|
51
54
|
import { createSoapClient } from "./generated/my/runtime.js";
|
|
52
|
-
// class name defaults to <ServiceName>SoapClient (from WSDL), e.g., MyServiceSoapClient
|
|
53
55
|
import { MyServiceSoapClient } from "./generated/my/client.js";
|
|
54
|
-
|
|
55
|
-
// optional: pass security straight into createSoapClient (node-soap security instance)
|
|
56
56
|
import soap from "soap";
|
|
57
|
-
const security = new soap.WSSecurity("user", "pass");
|
|
58
57
|
|
|
58
|
+
const security = new soap.WSSecurity("user", "pass");
|
|
59
59
|
const soapClient = await createSoapClient({
|
|
60
60
|
wsdlUrl: "https://example.com/MyService?wsdl",
|
|
61
|
-
security,
|
|
61
|
+
security,
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
const client = new MyServiceSoapClient(soapClient, "$attributes");
|
|
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.
|
|
69
|
-
const rs = await client.MyOperation({
|
|
64
|
+
const client = new MyServiceSoapClient(soapClient, "$attributes");
|
|
65
|
+
const response = await client.MyOperation({
|
|
70
66
|
MyOperationRQ: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
MyElement: {
|
|
68
|
+
MyAttribute: "value",
|
|
69
|
+
ChildElementA: "valueA",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
75
72
|
});
|
|
76
73
|
|
|
77
|
-
console.log(
|
|
74
|
+
console.log(response);
|
|
78
75
|
```
|
|
79
76
|
|
|
80
|
-
> The generator always emits TypeScript sources (`*.ts`). You compile them with your app.
|
|
81
|
-
|
|
82
77
|
---
|
|
83
78
|
|
|
84
|
-
##
|
|
79
|
+
## Features
|
|
85
80
|
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
|
|
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.
|
|
81
|
+
- **Attributes and Child Elements**: Supports both attributes and nested elements in SOAP requests.
|
|
82
|
+
- **Literal Text Values**: Handles mixed content (attributes + text).
|
|
83
|
+
- **Security Integration**: Works with `node-soap` security instances (e.g., `WSSecurity`, `BasicAuthSecurity`).
|
|
84
|
+
- **WS-Policy Hints**: Provides inline hints for security configuration based on WSDL policies.
|
|
92
85
|
|
|
93
86
|
---
|
|
94
87
|
|
|
95
|
-
## CLI
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
wsdl-tsc --wsdl <path-or-url> --out <dir> [options]
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Local development
|
|
88
|
+
## CLI Usage
|
|
102
89
|
|
|
103
|
-
|
|
90
|
+
The CLI is the primary way to generate SOAP clients.
|
|
104
91
|
|
|
105
92
|
```bash
|
|
106
|
-
# Directly via tsx (requires tsx in devDependencies)
|
|
107
|
-
npx tsx src/cli.ts --wsdl <path-or-url> --out <dir> [options]
|
|
108
|
-
|
|
109
|
-
# Via npm script
|
|
110
|
-
git clone ... then:
|
|
111
|
-
npm install
|
|
112
|
-
git checkout <branch>
|
|
113
|
-
npm run dev -- --wsdl <path-or-url> --out <dir> [options]
|
|
114
|
-
|
|
115
|
-
# Using npm link to symlink your working copy
|
|
116
|
-
npm link
|
|
117
93
|
wsdl-tsc --wsdl <path-or-url> --out <dir> [options]
|
|
118
94
|
```
|
|
119
95
|
|
|
120
|
-
|
|
96
|
+
### Required Flags
|
|
97
|
+
- `--wsdl`: Path or URL to the WSDL file.
|
|
98
|
+
- `--out`: Output directory for the generated files.
|
|
121
99
|
|
|
122
|
-
|
|
123
|
-
* `--out` — output directory (created if missing)
|
|
124
|
-
|
|
125
|
-
**Options**
|
|
100
|
+
### Options
|
|
126
101
|
|
|
127
102
|
| Flag | Type | Choices | Default | Description |
|
|
128
103
|
|---------------------|-----------|--------------------------------|--------------|------------------------------------------------------------------|
|
|
129
104
|
| `--imports` | string | js, ts, bare | js | Intra-generated import specifiers: '.js', '.ts', or bare |
|
|
130
105
|
| `--ops-ts` | boolean | true, false | true | Emit `operations.ts` instead of JSON |
|
|
131
106
|
| `--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) |
|
|
133
107
|
| `--int64-as` | string | string, number, bigint | string | How to map xs:long/xs:unsignedLong |
|
|
134
108
|
| `--bigint-as` | string | string, number | string | How to map xs:integer family (positive/nonNegative/etc.) |
|
|
135
109
|
| `--decimal-as` | string | string, number | string | How to map xs:decimal (money/precision) |
|
|
136
110
|
| `--date-as` | string | string, Date | string | How to map date/time/duration types |
|
|
137
111
|
|
|
138
|
-
|
|
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
|
-
|
|
145
|
-
**Primitive mapping (safe defaults)**
|
|
146
|
-
|
|
147
|
-
Defaults are **string-first** to avoid precision & timezone surprises:
|
|
148
|
-
|
|
149
|
-
* `xs:decimal` → `string` (money/precision safe)
|
|
150
|
-
* 64-bit integers → `string` (you can opt into `bigint` or `number`)
|
|
151
|
-
* dates/times → `string` (transport-friendly, no implicit tz conversion)
|
|
112
|
+
---
|
|
152
113
|
|
|
153
|
-
|
|
114
|
+
## Generated Files
|
|
154
115
|
|
|
155
|
-
|
|
116
|
+
The generator produces the following files in the output directory:
|
|
156
117
|
|
|
157
118
|
```
|
|
158
119
|
<out>/
|
|
159
|
-
types.ts # interfaces
|
|
160
|
-
client.ts #
|
|
161
|
-
runtime.ts #
|
|
162
|
-
meta.ts #
|
|
163
|
-
operations.ts #
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
Example: if `User` has `@Id` and `@CreatedAt`, you’ll see:
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
interface User {
|
|
170
|
-
Id?: string;
|
|
171
|
-
CreatedAt?: string; // or Date if you chose --date-as Date
|
|
172
|
-
}
|
|
120
|
+
types.ts # TypeScript interfaces and type aliases
|
|
121
|
+
client.ts # Thin wrapper for SOAP operations
|
|
122
|
+
runtime.ts # SOAP runtime utilities
|
|
123
|
+
meta.ts # Metadata for JSON ⇄ SOAP mapping
|
|
124
|
+
operations.ts # Operation metadata (optional, based on --ops-ts)
|
|
173
125
|
```
|
|
174
126
|
|
|
175
127
|
---
|
|
176
128
|
|
|
177
|
-
##
|
|
178
|
-
|
|
179
|
-
### ESM / NodeNext (recommended)
|
|
129
|
+
## Advanced Usage
|
|
180
130
|
|
|
181
|
-
|
|
131
|
+
### Programmatic API
|
|
182
132
|
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
"compilerOptions": {
|
|
186
|
-
"target": "ES2022",
|
|
187
|
-
"module": "NodeNext",
|
|
188
|
-
"moduleResolution": "NodeNext",
|
|
189
|
-
"strict": true,
|
|
190
|
-
"esModuleInterop": true,
|
|
191
|
-
"skipLibCheck": true
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Generate with `--imports js` and import `./client.js`, `./runtime.js`.
|
|
197
|
-
|
|
198
|
-
### CommonJS
|
|
199
|
-
|
|
200
|
-
Keep your app `module: "CommonJS"` and generate with `--imports bare` (imports like `./runtime`).
|
|
201
|
-
TypeScript will compile to `require("./runtime")` cleanly.
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
## Recommended workflow
|
|
206
|
-
|
|
207
|
-
* **Vendor** your WSDL(s) in `spec/wsdl/` for reproducible builds.
|
|
208
|
-
* Generate into `src/generated/<service>/` and **commit the generated files** (deterministic CI/Docker).
|
|
209
|
-
* Build your app (the generated code compiles with it).
|
|
210
|
-
|
|
211
|
-
**Example scripts (app `package.json`):**
|
|
212
|
-
|
|
213
|
-
```json
|
|
214
|
-
{
|
|
215
|
-
"scripts": {
|
|
216
|
-
"codegen:my": "wsdl-tsc --wsdl ./spec/wsdl/MyService.wsdl --out ./src/generated/my --imports js --ops-ts",
|
|
217
|
-
"prebuild": "npm run codegen:my",
|
|
218
|
-
"build": "tsc -p tsconfig.json"
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Programmatic API (optional)
|
|
133
|
+
You can use the generator programmatically for custom workflows:
|
|
226
134
|
|
|
227
135
|
```ts
|
|
228
|
-
import { compileCatalog
|
|
229
|
-
import { loadWsdlCatalog } from "@techspokes/typescript-wsdl-client/internal-or-your-loader"; // if you expose it
|
|
136
|
+
import { compileCatalog } from "@techspokes/typescript-wsdl-client";
|
|
230
137
|
|
|
231
138
|
const catalog = await loadWsdlCatalog("./spec/wsdl/MyService.wsdl");
|
|
232
139
|
const compiled = compileCatalog(catalog, {
|
|
233
|
-
primitive: { decimalAs: "string", dateAs: "string" }
|
|
140
|
+
primitive: { decimalAs: "string", dateAs: "string" },
|
|
234
141
|
});
|
|
235
|
-
// …write your own emitters or use the built-ins in the CLI.
|
|
236
|
-
```
|
|
237
142
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
## Primitive mapping rationale
|
|
241
|
-
|
|
242
|
-
Defaults are **string-first** to avoid precision & timezone surprises:
|
|
243
|
-
|
|
244
|
-
* `xs:decimal` → `string` (money/precision safe)
|
|
245
|
-
* 64-bit integers → `string` (you can opt into `bigint` or `number`)
|
|
246
|
-
* dates/times → `string` (transport-friendly, no implicit tz conversion)
|
|
247
|
-
|
|
248
|
-
Change per your needs with the CLI flags above.
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## Minimal test you can run
|
|
253
|
-
|
|
254
|
-
```bash
|
|
255
|
-
# generate from a local WSDL
|
|
256
|
-
npx wsdl-tsc --wsdl ./spec/wsdl/MyService.wsdl --out ./tmp --imports js --ops-ts
|
|
257
|
-
|
|
258
|
-
# quick typecheck of generated output (NodeNext)
|
|
259
|
-
npx tsc --noEmit --module NodeNext --moduleResolution NodeNext --target ES2022 ./tmp/*.ts
|
|
143
|
+
// Use the compiled output as needed.
|
|
260
144
|
```
|
|
261
145
|
|
|
262
146
|
---
|
|
263
147
|
|
|
264
148
|
## Troubleshooting
|
|
265
149
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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`.
|
|
150
|
+
- **Missing `runtime.ts`**: Ensure the output directory is writable and you're using the latest version.
|
|
151
|
+
- **Module system issues**: Use `--imports js` for ESM/NodeNext or `--imports bare` for CommonJS.
|
|
152
|
+
- **Security warnings**: Configure `node-soap` security (e.g., `WSSecurity`) as needed.
|
|
271
153
|
|
|
272
154
|
---
|
|
273
155
|
|
|
274
|
-
##
|
|
275
|
-
|
|
276
|
-
Issues and PRs welcome. Please include a **minimal WSDL/XSD** fixture that reproduces the case.
|
|
277
|
-
Node 20+ supported.
|
|
156
|
+
## Roadmap
|
|
278
157
|
|
|
279
|
-
|
|
280
|
-
- See CODE_OF_CONDUCT.md for community expectations.
|
|
281
|
-
- Security reports: see SECURITY.md.
|
|
158
|
+
Please see the [ROADMAP.md](ROADMAP.md) for planned features and improvements.
|
|
282
159
|
|
|
283
160
|
---
|
|
284
161
|
|
|
285
|
-
##
|
|
162
|
+
## Contributing
|
|
286
163
|
|
|
287
|
-
|
|
288
|
-
-
|
|
289
|
-
-
|
|
290
|
-
-
|
|
291
|
-
- Roadmap: ROADMAP.md
|
|
292
|
-
- Changelog: CHANGELOG.md
|
|
164
|
+
We welcome contributions! Please see the following resources:
|
|
165
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md): Development workflow and guidelines.
|
|
166
|
+
- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md): Community expectations.
|
|
167
|
+
- [SECURITY.md](SECURITY.md): Reporting vulnerabilities.
|
|
293
168
|
|
|
294
169
|
---
|
|
295
170
|
|
|
@@ -306,9 +181,10 @@ MIT © TechSpokes.
|
|
|
306
181
|
- Your Name Here!
|
|
307
182
|
|
|
308
183
|
**Gold Sponsors**
|
|
309
|
-
- [Your Name or Company](https://your-link-here.com)
|
|
184
|
+
- [Your Name or Company (with a link) Here!](https://your-link-here.com)
|
|
310
185
|
|
|
311
186
|
**Platinum Sponsors**
|
|
312
|
-
- [Your Name or Company](https://your-link-here.com)
|
|
187
|
+
- [Your Name or Company (with a link) Here!](https://your-link-here.com)
|
|
188
|
+
- **AND** 30-min one-to-one video meeting on AI, business automations, vacation rentals industry, development, tools, or a subject of your choice.
|
|
313
189
|
|
|
314
190
|
Want to see your name or company here? [Become a sponsor!](https://github.com/sponsors/TechSpokes)
|
|
@@ -22,6 +22,21 @@ export type CompiledType = {
|
|
|
22
22
|
declaredType: string;
|
|
23
23
|
}>;
|
|
24
24
|
jsdoc?: string;
|
|
25
|
+
base?: string;
|
|
26
|
+
localAttrs?: Array<{
|
|
27
|
+
name: string;
|
|
28
|
+
tsType: string;
|
|
29
|
+
use?: "required" | "optional";
|
|
30
|
+
declaredType: string;
|
|
31
|
+
}>;
|
|
32
|
+
localElems?: Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
tsType: string;
|
|
35
|
+
min: number;
|
|
36
|
+
max: number | "unbounded";
|
|
37
|
+
nillable?: boolean;
|
|
38
|
+
declaredType: string;
|
|
39
|
+
}>;
|
|
25
40
|
};
|
|
26
41
|
export type CompiledAlias = {
|
|
27
42
|
name: string;
|
|
@@ -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;
|
|
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;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,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;IAEH,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,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;CACJ,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,CA6gBxF"}
|
|
@@ -248,23 +248,18 @@ export function compileCatalog(cat, _opts) {
|
|
|
248
248
|
// resolves type refs or @ref, applies min/max occurrence and nillable flags
|
|
249
249
|
const collectParticles = (ownerTypeName, node) => {
|
|
250
250
|
const out = [];
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
];
|
|
256
|
-
for (const grp of groups) {
|
|
257
|
-
for (const e of getChildrenWithLocalName(grp, "element")) {
|
|
251
|
+
// process a compositor or element container recursively
|
|
252
|
+
const recurse = (groupNode) => {
|
|
253
|
+
// handle direct element children
|
|
254
|
+
for (const e of getChildrenWithLocalName(groupNode, "element")) {
|
|
258
255
|
const nameOrRef = e["@_name"] || e["@_ref"];
|
|
259
|
-
if (!nameOrRef)
|
|
256
|
+
if (!nameOrRef)
|
|
260
257
|
continue;
|
|
261
|
-
}
|
|
262
258
|
const propName = e["@_name"];
|
|
263
259
|
const min = e["@_minOccurs"] ? Number(e["@_minOccurs"]) : 1;
|
|
264
260
|
const maxAttr = e["@_maxOccurs"];
|
|
265
261
|
const max = maxAttr === "unbounded" ? "unbounded" : maxAttr ? Number(maxAttr) : 1;
|
|
266
262
|
const nillable = e["@_nillable"] === "true";
|
|
267
|
-
// inline complexType: create a nested interface with a generated name
|
|
268
263
|
const inlineComplex = getFirstWithLocalName(e, "complexType");
|
|
269
264
|
const inlineSimple = getFirstWithLocalName(e, "simpleType");
|
|
270
265
|
if (inlineComplex) {
|
|
@@ -273,19 +268,24 @@ export function compileCatalog(cat, _opts) {
|
|
|
273
268
|
out.push({ name: propName || nameOrRef, tsType: rec.name, min, max, nillable, declaredType: `{${schemaNS}}${rec.name}` });
|
|
274
269
|
}
|
|
275
270
|
else if (inlineSimple) {
|
|
276
|
-
// inline simpleType (e.g., list or enumeration)
|
|
277
271
|
const r = compileSimpleTypeNode(inlineSimple, schemaNS, prefixes);
|
|
278
272
|
out.push({ name: propName || nameOrRef, tsType: r.tsType, min, max, nillable, declaredType: r.declared });
|
|
279
273
|
}
|
|
280
274
|
else {
|
|
281
|
-
// named type or ref: resolve via QName
|
|
282
275
|
const t = e["@_type"] || e["@_ref"];
|
|
283
276
|
const q = t ? resolveQName(t, schemaNS, prefixes) : { ns: XS, local: "string" };
|
|
284
277
|
const r = resolveTypeRef(q, schemaNS, prefixes);
|
|
285
278
|
out.push({ name: propName || nameOrRef, tsType: r.tsType, min, max, nillable, declaredType: r.declared });
|
|
286
279
|
}
|
|
287
280
|
}
|
|
288
|
-
|
|
281
|
+
// recurse into nested compositor groups
|
|
282
|
+
for (const comp of ["sequence", "all", "choice"]) {
|
|
283
|
+
for (const sub of getChildrenWithLocalName(groupNode, comp)) {
|
|
284
|
+
recurse(sub);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
recurse(node);
|
|
289
289
|
return out;
|
|
290
290
|
};
|
|
291
291
|
// Result accumulators
|
|
@@ -298,21 +298,26 @@ export function compileCatalog(cat, _opts) {
|
|
|
298
298
|
const res = getFirstWithLocalName(complexContent, "restriction");
|
|
299
299
|
const node = ext || res;
|
|
300
300
|
if (node) {
|
|
301
|
+
// handle complexContent extension: record base and separate local additions
|
|
302
|
+
let baseName;
|
|
301
303
|
const baseAttr = node["@_base"];
|
|
302
304
|
if (baseAttr) {
|
|
303
305
|
const baseQ = resolveQName(baseAttr, schemaNS, prefixes);
|
|
304
306
|
const baseRec = findComplexRec(baseQ);
|
|
305
307
|
if (baseRec) {
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
const baseType = getOrCompileComplex(baseRec.node["@_name"], baseRec.node, baseRec.tns, baseRec.prefixes);
|
|
309
|
+
baseName = baseType.name;
|
|
310
|
+
// inherit base members
|
|
311
|
+
attrs.push(...baseType.attrs);
|
|
312
|
+
elems.push(...baseType.elems);
|
|
310
313
|
}
|
|
311
314
|
}
|
|
312
|
-
// local additions/overrides
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
// collect local additions/overrides
|
|
316
|
+
const locals = collectAttributes(node);
|
|
317
|
+
const localElems = collectParticles(outName, node);
|
|
318
|
+
attrs.push(...locals);
|
|
319
|
+
elems.push(...localElems);
|
|
320
|
+
const result = { name: outName, ns: schemaNS, attrs, elems, base: baseName, localAttrs: locals, localElems };
|
|
316
321
|
compiledMap.set(key, result);
|
|
317
322
|
inProgress.delete(key);
|
|
318
323
|
return result;
|
|
@@ -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,
|
|
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,QAmEpH"}
|
|
@@ -18,6 +18,15 @@ export function emitClient(outFile, compiled, opts) {
|
|
|
18
18
|
return noExt ? pascal(noExt) : "";
|
|
19
19
|
})();
|
|
20
20
|
const className = overrideName || ((svcName || fileBase) ? `${svcName || fileBase}SoapClient` : "GeneratedSoapClient");
|
|
21
|
+
// Helpers for emitting safe method names
|
|
22
|
+
const isValidIdent = (name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
23
|
+
const reserved = new Set([
|
|
24
|
+
"break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete",
|
|
25
|
+
"do", "else", "enum", "export", "extends", "false", "finally", "for", "function", "if",
|
|
26
|
+
"import", "in", "instanceof", "new", "null", "return", "super", "switch", "this", "throw",
|
|
27
|
+
"true", "try", "typeof", "var", "void", "while", "with", "as", "implements", "interface",
|
|
28
|
+
"let", "package", "private", "protected", "public", "static", "yield", "constructor"
|
|
29
|
+
]);
|
|
21
30
|
lines.push(`export class ${className} {`);
|
|
22
31
|
lines.push(` constructor(private source: string | any, private attributesKey: string = "${opts.attributesKey || "$attributes"}") {}`);
|
|
23
32
|
lines.push(` async _client() {`);
|
|
@@ -33,17 +42,23 @@ export function emitClient(outFile, compiled, opts) {
|
|
|
33
42
|
const inMetaKey = inTypeName ?? m;
|
|
34
43
|
const outMetaKey = outTypeName ?? m;
|
|
35
44
|
const secHints = Array.isArray(op.security) && op.security.length ? op.security : [];
|
|
45
|
+
const methodHeader = (isValidIdent(m) && !reserved.has(m))
|
|
46
|
+
? ` async ${m}(args: ${inTs}): Promise<${outTs}> {`
|
|
47
|
+
: ` async [${JSON.stringify(m)}](args: ${inTs}): Promise<${outTs}> {`;
|
|
48
|
+
const clientCall = (isValidIdent(m) && !reserved.has(m))
|
|
49
|
+
? `c.${m}`
|
|
50
|
+
: `c[${JSON.stringify(m)}]`;
|
|
36
51
|
lines.push(` /** SOAPAction: ${op.soapAction}${secHints.length ? `\n * Security (WSDL policy hint): ${secHints.join(", ")}` : ""} */`);
|
|
37
|
-
lines.push(
|
|
52
|
+
lines.push(methodHeader);
|
|
38
53
|
lines.push(` const c: any = await this._client();`);
|
|
39
54
|
if (secHints.length) {
|
|
40
55
|
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
56
|
}
|
|
42
57
|
lines.push(` const meta = { ATTR_SPEC, CHILD_TYPE, PROP_META } as const;`);
|
|
43
|
-
lines.push(` const soapArgs = toSoapArgs(args as any,
|
|
58
|
+
lines.push(` const soapArgs = toSoapArgs(args as any, ${JSON.stringify(inMetaKey)}, meta, this.attributesKey);`);
|
|
44
59
|
lines.push(` return new Promise((resolve, reject) => {`);
|
|
45
|
-
lines.push(`
|
|
46
|
-
lines.push(` if (err) reject(err); else resolve(fromSoapResult(result,
|
|
60
|
+
lines.push(` ${clientCall}(soapArgs, (err: any, result: any) => {`);
|
|
61
|
+
lines.push(` if (err) reject(err); else resolve(fromSoapResult(result, ${JSON.stringify(outMetaKey)}, meta, this.attributesKey));`);
|
|
47
62
|
lines.push(` });`);
|
|
48
63
|
lines.push(` });`);
|
|
49
64
|
lines.push(` }`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typesEmitter.d.ts","sourceRoot":"","sources":["../../src/emit/typesEmitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"typesEmitter.d.ts","sourceRoot":"","sources":["../../src/emit/typesEmitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAEjF;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,QA2InE"}
|
|
@@ -11,37 +11,71 @@ export function emitTypes(outFile, compiled) {
|
|
|
11
11
|
const lines = [];
|
|
12
12
|
// Convenience lookups
|
|
13
13
|
const typeNames = new Set(compiled.types.map((t) => t.name));
|
|
14
|
+
const isValidIdent = (name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
15
|
+
const emitPropName = (name) => (isValidIdent(name) ? name : JSON.stringify(name));
|
|
14
16
|
//
|
|
15
17
|
// 1) Named simple types (aliases) first
|
|
16
18
|
//
|
|
19
|
+
// sort aliases by name to ensure consistent order
|
|
20
|
+
compiled.aliases.sort((a, b) => a.name.localeCompare(b.name));
|
|
17
21
|
for (const a of compiled.aliases) {
|
|
18
|
-
const ann = a.jsdoc ?
|
|
22
|
+
const ann = a.jsdoc ? `/** @xsd ${a.jsdoc} */\n` : "";
|
|
19
23
|
lines.push(`${ann}export type ${a.name} = ${a.tsType};`);
|
|
20
24
|
lines.push("");
|
|
21
25
|
}
|
|
22
26
|
//
|
|
23
27
|
// 2) Complex types as interfaces
|
|
24
28
|
//
|
|
29
|
+
// sort types by name to ensure consistent order
|
|
30
|
+
compiled.types.sort((a, b) => a.name.localeCompare(b.name));
|
|
25
31
|
for (const t of compiled.types) {
|
|
26
|
-
// Detect mis-mapped simpleContent extension
|
|
27
|
-
|
|
32
|
+
// Detect complexContent extension via compiled metadata or mis-mapped simpleContent extension
|
|
33
|
+
const complexBase = t.base;
|
|
34
|
+
// Detect mis-mapped simpleContent extension: single "$value" whose tsType is another named interface
|
|
28
35
|
const valueElems = (t.elems || []).filter((e) => e.name === "$value" &&
|
|
29
36
|
(e.max === 1 || e.max === undefined) &&
|
|
30
37
|
typeof e.tsType === "string" &&
|
|
31
38
|
typeNames.has(e.tsType));
|
|
32
|
-
const isSimpleContentExtension = (t.elems?.length || 0) === 1 && valueElems.length === 1;
|
|
33
|
-
const baseName = isSimpleContentExtension ? valueElems[0].tsType : undefined;
|
|
34
|
-
// Header
|
|
39
|
+
const isSimpleContentExtension = !complexBase && (t.elems?.length || 0) === 1 && valueElems.length === 1;
|
|
40
|
+
const baseName = complexBase ?? (isSimpleContentExtension ? valueElems[0].tsType : undefined);
|
|
41
|
+
// Header: extend base type if applicable
|
|
35
42
|
if (baseName) {
|
|
36
43
|
lines.push(`export interface ${t.name} extends ${baseName} {`);
|
|
37
44
|
}
|
|
38
45
|
else {
|
|
39
46
|
lines.push(`export interface ${t.name} {`);
|
|
40
47
|
}
|
|
48
|
+
// Prepare lists: for complexContent extension use only local additions
|
|
49
|
+
const attrsToEmit = complexBase ? (t.localAttrs || []) : (t.attrs || []);
|
|
50
|
+
// Elements list similar
|
|
51
|
+
let elementsToEmit = complexBase ? (t.localElems || []) : (t.elems || []);
|
|
52
|
+
// SimpleContent extension special handling drops synthetic $value
|
|
53
|
+
if (isSimpleContentExtension && !complexBase) {
|
|
54
|
+
const idx = elementsToEmit.findIndex(e => e.name === "$value");
|
|
55
|
+
if (idx >= 0)
|
|
56
|
+
elementsToEmit.splice(idx, 1);
|
|
57
|
+
}
|
|
41
58
|
//
|
|
42
59
|
// Attributes — with JSDoc on every attribute
|
|
43
60
|
//
|
|
44
|
-
|
|
61
|
+
if (0 < attrsToEmit.length) {
|
|
62
|
+
// add attributes header comment
|
|
63
|
+
lines.push("");
|
|
64
|
+
lines.push(" /**");
|
|
65
|
+
lines.push((1 === t.attrs.length) ? " * Attribute." : " * Attributes.");
|
|
66
|
+
lines.push(" */");
|
|
67
|
+
// Sort the elements with the following logic: required first (sorted a-z), then optional (sorted a-z)
|
|
68
|
+
attrsToEmit.sort((a, b) => {
|
|
69
|
+
// Required attributes come before optional attributes
|
|
70
|
+
if (a.use === "required" && b.use !== "required")
|
|
71
|
+
return -1; // `a` is required, b is optional
|
|
72
|
+
if (a.use !== "required" && b.use === "required")
|
|
73
|
+
return 1; // `a` is optional, b is required
|
|
74
|
+
// Within the same group (required or optional), sort alphabetically
|
|
75
|
+
return a.name.localeCompare(b.name);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
for (const a of attrsToEmit) {
|
|
45
79
|
const opt = a.use === "required" ? "" : "?";
|
|
46
80
|
const annObj = {
|
|
47
81
|
kind: "attribute",
|
|
@@ -49,40 +83,54 @@ export function emitTypes(outFile, compiled) {
|
|
|
49
83
|
use: a.use || "optional",
|
|
50
84
|
};
|
|
51
85
|
const ann = ` /** @xsd ${JSON.stringify(annObj)} */`;
|
|
86
|
+
lines.push("");
|
|
52
87
|
lines.push(ann);
|
|
53
|
-
lines.push(` ${a.name}${opt}: ${a.tsType};`);
|
|
88
|
+
lines.push(` ${emitPropName(a.name)}${opt}: ${a.tsType};`);
|
|
54
89
|
}
|
|
55
90
|
//
|
|
56
91
|
// Elements — with JSDoc on every element
|
|
57
92
|
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
93
|
+
// elementsToEmit already prepared above
|
|
94
|
+
if (0 < elementsToEmit.length) {
|
|
95
|
+
// add elements header comment
|
|
96
|
+
lines.push("");
|
|
97
|
+
lines.push(" /**");
|
|
98
|
+
lines.push((1 === elementsToEmit.length) ? " * Child element." : " * Children elements.");
|
|
99
|
+
lines.push(" */");
|
|
100
|
+
// Sort the elements with the following logic: required first (sorted a-z), then optional (sorted a-z), and finally "$value" if present.
|
|
101
|
+
elementsToEmit.sort((a, b) => {
|
|
102
|
+
// Special case: $value is always last
|
|
103
|
+
if (a.name === "$value")
|
|
104
|
+
return 1;
|
|
105
|
+
if (b.name === "$value")
|
|
106
|
+
return -1;
|
|
107
|
+
// Required elements come before optional elements
|
|
108
|
+
if (a.min !== 0 && b.min === 0)
|
|
109
|
+
return -1; // `a` is required, b is optional
|
|
110
|
+
if (a.min === 0 && b.min !== 0)
|
|
111
|
+
return 1; // `a` is optional, b is required
|
|
112
|
+
// Within the same group (required or optional), sort alphabetically
|
|
113
|
+
return a.name.localeCompare(b.name);
|
|
114
|
+
});
|
|
64
115
|
}
|
|
65
|
-
// Ensure "$value" is last if present
|
|
66
|
-
elementsToEmit.sort((a, b) => {
|
|
67
|
-
if (a.name === "$value" && b.name !== "$value")
|
|
68
|
-
return 1;
|
|
69
|
-
if (a.name !== "$value" && b.name === "$value")
|
|
70
|
-
return -1;
|
|
71
|
-
return 0;
|
|
72
|
-
});
|
|
73
116
|
for (const e of elementsToEmit) {
|
|
74
|
-
const isArray = e.max === "unbounded" ||
|
|
75
|
-
(typeof e.max === "number" && e.max > 1);
|
|
117
|
+
const isArray = e.max === "unbounded" || (e.max > 1);
|
|
76
118
|
const arr = isArray ? "[]" : "";
|
|
77
119
|
const opt = (e.min ?? 0) === 0 ? "?" : "";
|
|
78
120
|
const annObj = {
|
|
79
|
-
kind
|
|
121
|
+
// if a.name === "$value", the kind should be "scalar value"
|
|
122
|
+
kind: e.name === "$value" ? "scalar value" : "element",
|
|
80
123
|
type: e.declaredType,
|
|
81
124
|
occurs: { min: e.min, max: e.max, nillable: e.nillable ?? false },
|
|
82
125
|
};
|
|
126
|
+
// if the a.name === "$value" and we have more child elements, add an empty line before the annotation
|
|
127
|
+
if ((e.name === "$value") && (1 < elementsToEmit.length)) {
|
|
128
|
+
lines.push("");
|
|
129
|
+
}
|
|
83
130
|
const ann = ` /** @xsd ${JSON.stringify(annObj)} */`;
|
|
131
|
+
lines.push("");
|
|
84
132
|
lines.push(ann);
|
|
85
|
-
lines.push(` ${e.name}${opt}: ${e.tsType}${arr};`);
|
|
133
|
+
lines.push(` ${emitPropName(e.name)}${opt}: ${e.tsType}${arr};`);
|
|
86
134
|
}
|
|
87
135
|
lines.push("}");
|
|
88
136
|
lines.push("");
|
package/dist/util/xml.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xml.d.ts","sourceRoot":"","sources":["../../src/util/xml.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,EAAE,CAGpE;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,CASxE;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAK/E;AAED,6DAA6D;AAC7D,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"xml.d.ts","sourceRoot":"","sources":["../../src/util/xml.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,EAAE,CAGpE;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,CASxE;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAK/E;AAED,6DAA6D;AAC7D,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BxC;AAED,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"}
|
package/dist/util/xml.js
CHANGED
|
@@ -25,7 +25,35 @@ export function getFirstWithLocalName(node, local) {
|
|
|
25
25
|
}
|
|
26
26
|
/** Simple PascalCase helper used across emitters/parsers. */
|
|
27
27
|
export function pascal(s) {
|
|
28
|
-
|
|
28
|
+
const raw = String(s ?? "");
|
|
29
|
+
// Split on underscores to preserve them literally
|
|
30
|
+
const segments = raw.split("_");
|
|
31
|
+
const cased = segments.map(seg => {
|
|
32
|
+
// Uppercase letters after common separators (start, space, dash, dot, colon, slash)
|
|
33
|
+
const up = seg.replace(/(^|[-\s.:\/])([A-Za-z0-9_$])/g, (_m, _sep, c) => String(c).toUpperCase());
|
|
34
|
+
// Remove disallowed identifier characters but preserve A-Z, a-z, 0-9, _ and $
|
|
35
|
+
return up.replace(/[^A-Za-z0-9_$]/g, "");
|
|
36
|
+
});
|
|
37
|
+
let out = cased.join("_");
|
|
38
|
+
if (!out)
|
|
39
|
+
out = "_"; // fallback
|
|
40
|
+
if (/^[0-9]/.test(out))
|
|
41
|
+
out = `_${out}`; // ensure valid identifier start
|
|
42
|
+
// guard against TypeScript reserved keywords when the identifier equals them exactly
|
|
43
|
+
const reserved = [
|
|
44
|
+
"break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete",
|
|
45
|
+
"do", "else", "enum", "export", "extends", "false", "finally", "for", "function", "if",
|
|
46
|
+
"import", "in", "instanceof", "new", "null", "return", "super", "switch", "this", "throw",
|
|
47
|
+
"true", "try", "typeof", "var", "void", "while", "with", "as", "implements", "interface",
|
|
48
|
+
"let", "package", "private", "protected", "public", "static", "yield", "any", "boolean",
|
|
49
|
+
"constructor", "declare", "get", "module", "require", "number", "set", "string", "symbol",
|
|
50
|
+
"type", "from", "of"
|
|
51
|
+
];
|
|
52
|
+
const lower = out.toLowerCase();
|
|
53
|
+
if (reserved.includes(lower)) {
|
|
54
|
+
out = `_${out}`;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
29
57
|
}
|
|
30
58
|
export function resolveQName(qname, defaultNS, prefixes) {
|
|
31
59
|
if (!qname)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techspokes/typescript-wsdl-client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
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",
|