@techspokes/typescript-wsdl-client 1.1.8 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +17 -3
  2. package/dist/checkedPlanning.js +27 -4
  3. package/dist/cli.js +17 -1
  4. package/dist/client/generateClient.js +72 -16
  5. package/dist/client/generateOperations.js +30 -11
  6. package/dist/client/generateUtils.js +5 -1
  7. package/dist/compiler/canonicalGraph.d.ts +4 -15
  8. package/dist/compiler/canonicalGraph.js +1 -8
  9. package/dist/compiler/componentRecordProjection.d.ts +3 -0
  10. package/dist/compiler/componentRecordProjection.js +10 -0
  11. package/dist/compiler/declarationIdentity.d.ts +22 -0
  12. package/dist/compiler/declarationIdentity.js +9 -0
  13. package/dist/compiler/directCodec.d.ts +22 -2
  14. package/dist/compiler/directCodec.js +40 -7
  15. package/dist/compiler/directOperation.d.ts +26 -4
  16. package/dist/compiler/directOperation.js +59 -5
  17. package/dist/compiler/directSoap.js +24 -7
  18. package/dist/compiler/directSourceCapsule.js +59 -3
  19. package/dist/compiler/directStructure.js +6 -2
  20. package/dist/compiler/directStructurePlan.d.ts +10 -1
  21. package/dist/compiler/directStructurePlan.js +27 -7
  22. package/dist/compiler/directValues.d.ts +15 -3
  23. package/dist/compiler/directValues.js +20 -4
  24. package/dist/compiler/legacyDeclarations.d.ts +26 -0
  25. package/dist/compiler/legacyDeclarations.js +144 -0
  26. package/dist/compiler/projectionScalar.d.ts +18 -1
  27. package/dist/compiler/projectionScalar.js +47 -13
  28. package/dist/compiler/rpcStringValues.d.ts +30 -0
  29. package/dist/compiler/rpcStringValues.js +196 -0
  30. package/dist/compiler/rpcTypedValues.d.ts +48 -0
  31. package/dist/compiler/rpcTypedValues.js +190 -0
  32. package/dist/compiler/schemaCompiler.js +44 -109
  33. package/dist/compiler/semanticCatalog.d.ts +2 -2
  34. package/dist/gateway/generateComposedGateway.d.ts +16 -0
  35. package/dist/gateway/generateComposedGateway.js +65 -0
  36. package/dist/gateway/generateGateway.js +1 -0
  37. package/dist/gateway/generators.js +19 -4
  38. package/dist/gateway/helpers.d.ts +1 -0
  39. package/dist/gateway/helpers.js +15 -0
  40. package/dist/generation/componentShape.js +25 -1
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +2 -0
  43. package/dist/loader/orderedSyntax.d.ts +4 -0
  44. package/dist/loader/orderedSyntax.js +18 -4
  45. package/dist/multiServicePipeline.d.ts +41 -0
  46. package/dist/multiServicePipeline.js +287 -0
  47. package/dist/openapi/composeOpenAPI.d.ts +29 -0
  48. package/dist/openapi/composeOpenAPI.js +322 -0
  49. package/dist/pipeline.d.ts +1 -1
  50. package/dist/pipeline.js +2 -0
  51. package/docs/agent-skill.md +10 -1
  52. package/docs/api-reference.md +127 -9
  53. package/docs/checked-gateway.md +2 -0
  54. package/docs/checked-generation.md +11 -5
  55. package/docs/cli-reference.md +25 -5
  56. package/docs/concepts.md +4 -5
  57. package/docs/configuration.md +10 -3
  58. package/docs/corpus.md +1 -1
  59. package/docs/gateway-guide.md +104 -27
  60. package/docs/generated-code.md +12 -6
  61. package/docs/migration-playbook.md +4 -0
  62. package/docs/migration.md +124 -6
  63. package/docs/output-anatomy.md +20 -17
  64. package/docs/production.md +7 -5
  65. package/docs/start-here.md +12 -2
  66. package/docs/supported-patterns.md +17 -0
  67. package/docs/testing.md +2 -0
  68. package/docs/troubleshooting.md +3 -1
  69. package/llms.txt +9 -1
  70. package/package.json +2 -2
  71. package/src/runtime/clientStreamMethods.tpl.txt +156 -148
  72. package/src/runtime/operationsStreamHelper.tpl.txt +6 -6
  73. package/src/runtime/streamXml.ts +48 -20
@@ -201,9 +201,19 @@ export async function registerRoute_v1_weather_getweatherinformation(fastify: Fa
201
201
  schema: routeSchema,
202
202
  handler: async (request, reply) => {
203
203
  const client = fastify.weatherClient;
204
- const result = await client.GetWeatherInformation(request.body as GetWeatherInformation);
205
- reply.type("application/x-ndjson");
206
- return reply.send(toNdjson(result.records));
204
+ const controller = new AbortController();
205
+ const disconnected = () => { if (!reply.raw.writableEnded) controller.abort(); };
206
+ request.raw.once("aborted", disconnected);
207
+ reply.raw.once("close", disconnected);
208
+ if (request.raw.aborted || reply.raw.destroyed) controller.abort();
209
+ try {
210
+ const result = await client.GetWeatherInformation(request.body as GetWeatherInformation, {signal: controller.signal});
211
+ reply.type("application/x-ndjson");
212
+ return await reply.send(toNdjson(result.records));
213
+ } finally {
214
+ request.raw.removeListener("aborted", disconnected);
215
+ reply.raw.removeListener("close", disconnected);
216
+ }
207
217
  },
208
218
  });
209
219
  }
@@ -211,7 +221,25 @@ export async function registerRoute_v1_weather_getweatherinformation(fastify: Fa
211
221
 
212
222
  For `format: "json-array"`, the same handler imports `toJsonArray`, sets `reply.type("application/json")`, and sends `reply.send(toJsonArray(result.records))`.
213
223
 
214
- The client method returns `StreamOperationResponse<RecordType>` with a `records: AsyncIterable<RecordType>`. Errors raised before the first record use the normal error envelope. Errors raised mid-stream truncate the response; NDJSON clients see an incomplete HTTP response, and JSON array clients see an incomplete or invalid JSON document.
224
+ The client method returns `StreamOperationResponse<RecordType>` with a single-pass `records` iterable. Failures before response commitment can use the error envelope; errors after commitment truncate the response. Treat an incomplete HTTP response or JSON array as a failed stream, even if earlier records were valid.
225
+
226
+ Each completed record passes through the legacy client's `fromSoapResult` mapper before delivery. Public DTOs flatten XML attributes, convert boolean/numeric lexical values according to projected types, preserve configured string-first decimal/integer policies, map typed `$value` content, tokenize XML lists and retain repeated-element arrays. Empty complex types without child fields become `{}`; empty primitive strings remain strings. The low-level `streamXml.ts` parser retains lexical values and attribute bags, and checked mapping conventions are unchanged.
227
+
228
+ For buffered responses, node-soap's `null` empty-object recovery requires a declared non-nillable child of a known complex type without child fields. Nullable and unknown values keep their existing behavior; empty containers with declared optional children are outside this bounded recovery.
229
+
230
+ When array-wrapper flattening is enabled, the gateway unwraps each mapped record using the same wrapper convention as buffered routes. Both NDJSON and JSON-array delivery stay incremental. A consumer disconnect aborts the upstream request even before SOAP response headers arrive, and remains connected to cancellation throughout record delivery.
231
+
232
+ ### Streaming transport settings
233
+
234
+ Stream calls use `soap.HttpClient.requestStream` or the configured `options.httpClient.requestStream`, with the configured `options.request` adapter. They honor the current `soapClient().setEndpoint(...)`, static SOAP/HTTP headers, and the current security object's `addHeaders`, `addOptions`, `toXML` and `postProcess` hooks. A custom HTTP client must implement `requestStream`.
235
+
236
+ Requests pass through `toSoapArgs` and node-soap's WSDL document serializer, preserving unqualified local children and foreign namespace references. The initialized client's `envelopeKey` also governs SOAP headers and security `postProcess`. Stream calls require a compatible live document/literal binding matching the generated operation name and SOAP action; missing or ambiguous incompatible bindings are refused before I/O.
237
+
238
+ Only clients with stream-configured operations expose constructor `streamRequestOptions`, using node-soap's `IExOptions` HTTP options. Use it for stream-specific HTTP timeouts or an HTTPS agent with trusted CA/client certificates; WSDL-fetch settings remain separate. TLS verification stays enabled by default and uses Node's configured trust; no certificate bypass is added.
239
+
240
+ Generated stream operations also accept `{signal, timeoutMs}` as an optional second argument. `timeoutMs` covers the upstream HTTP request and record consumption, starting after SOAP metadata initialization. Exhaust the records or return their iterator to release the connection; a per-call signal already aborted rejects before metadata initialization. See the [generated stream client API](api-reference.md#generated-stream-client-contract).
241
+
242
+ Streaming supports SOAP 1.1 document/literal requests. SOAP 1.2/custom envelope namespaces, functional SOAP headers, SOAP body attributes, MTOM attachments and NTLM transport are explicitly refused. Other node-soap serializer/security combinations require separate qualification; date mappings remain type-level, and `bigint` values retain normal JSON serialization limitations.
215
243
 
216
244
  ## Error Handling
217
245
 
@@ -220,12 +248,14 @@ The centralized error handler (runtime.ts) automatically classifies errors:
220
248
  | Error Type | HTTP Status | Error Code |
221
249
  |--------------------|-------------|---------------------|
222
250
  | Validation errors | 400 | VALIDATION_ERROR |
223
- | SOAP faults | 502 | SOAP_FAULT |
251
+ | Buffered node-soap faults | 502 | SOAP_FAULT |
224
252
  | Connection refused | 503 | SERVICE_UNAVAILABLE |
225
253
  | Timeout | 504 | GATEWAY_TIMEOUT |
226
254
  | Other errors | 500 | INTERNAL_ERROR |
227
255
 
228
- All errors are wrapped in the standard envelope format. See [Concepts](concepts.md) for the envelope structure.
256
+ Legacy errors handled before response commitment use the standard envelope. After stream output begins, errors truncate the response. Checked component values and faults follow their separate [checked contract](checked-gateway.md).
257
+
258
+ Stream parser faults fail the iterator instead of producing an empty successful result. Before output begins they use the generic error classification; after output begins the response is truncated.
229
259
 
230
260
  ## Stub Handler Mode
231
261
 
@@ -252,39 +282,86 @@ Example: `urn:services:weather:v1:schemas:models:getcityweatherbyzipresponse`
252
282
 
253
283
  ## Multi-Service Setup
254
284
 
255
- When integrating multiple SOAP services, each service gets its own client, gateway, and OpenAPI spec. Register each in its own Fastify encapsulation scope to prevent decorator collisions:
285
+ Native legacy composition generates independent service clients and routers, one aggregate Fastify plugin, and one combined OpenAPI document. Save a configuration such as `services.json` beside your `sources/` directory:
286
+
287
+ ```json
288
+ {
289
+ "services": [
290
+ {
291
+ "key": "main",
292
+ "generation": {
293
+ "wsdl": "./sources/main.wsdl",
294
+ "catalogOut": "./.generated/main/client/catalog.json",
295
+ "clientOutDir": "./.generated/main/client",
296
+ "compiler": {"imports": "js"},
297
+ "openapi": {"outFile": "./.generated/main/openapi.json", "basePath": "/main"},
298
+ "gateway": {"outDir": "./.generated/main/gateway", "serviceSlug": "main", "versionSlug": "v1"}
299
+ }
300
+ },
301
+ {
302
+ "key": "content",
303
+ "generation": {
304
+ "wsdl": "./sources/content.wsdl",
305
+ "catalogOut": "./.generated/content/client/catalog.json",
306
+ "clientOutDir": "./.generated/content/client",
307
+ "compiler": {"imports": "js"},
308
+ "openapi": {"outFile": "./.generated/content/openapi.json", "basePath": "/content"},
309
+ "gateway": {"outDir": "./.generated/content/gateway", "serviceSlug": "content", "versionSlug": "v1"}
310
+ }
311
+ }
312
+ ],
313
+ "openapi": {"outFile": "./.generated/aggregate/openapi.json", "title": "Combined SOAP Gateway", "version": "1.0.0"},
314
+ "gateway": {"outDir": "./.generated/aggregate/gateway", "imports": "js"}
315
+ }
316
+ ```
317
+
318
+ ```bash
319
+ npx wsdl-tsc pipeline --services-config ./services.json
320
+ ```
321
+
322
+ Relative paths resolve against `services.json`; companion paths in `streamConfigFile` resolve against that stream file. Each `generation` uses `PipelineOptions` property names. Every service requires explicit outputs and its own static `basePath`; prefixes are baked into routes once. Keys, normalized service slugs, decorators and prefixes must be unique; output ownership must not overlap across services, and source metadata must remain outside outputs.
323
+
324
+ ### Runtime clients
325
+
326
+ The aggregate plugin exports a typed `createClients` helper. Its registry requires exactly the configured keys, and each value is that service client's constructor options:
256
327
 
257
328
  ```typescript
258
329
  import Fastify from "fastify";
259
- import weatherPlugin from "./.generated/weather/gateway/plugin.js";
260
- import inventoryPlugin from "./.generated/inventory/gateway/plugin.js";
261
- import { Weather } from "./.generated/weather/client/client.js";
262
- import { Inventory } from "./.generated/inventory/client/client.js";
263
-
264
- const app = Fastify({ logger: true });
330
+ import gateway, {createClients} from "./.generated/aggregate/gateway/plugin.js";
331
+ import type {IOptions, ISecurity} from "soap";
265
332
 
266
- // Each plugin gets its own scope to isolate decorators
267
- await app.register(async (scope) => {
268
- await scope.register(weatherPlugin, {
269
- client: new Weather({ source: "https://example.com/weather?wsdl" }),
270
- prefix: "/api/weather",
271
- });
272
- });
333
+ declare const mainEndpoint: string, contentEndpoint: string;
334
+ declare const mainOptions: IOptions, contentOptions: IOptions;
335
+ declare const mainSecurity: ISecurity, contentSecurity: ISecurity;
273
336
 
274
- await app.register(async (scope) => {
275
- await scope.register(inventoryPlugin, {
276
- client: new Inventory({ source: "https://example.com/inventory?wsdl" }),
277
- prefix: "/api/inventory",
278
- });
337
+ const clients = createClients({
338
+ main: {source: "./sources/main.wsdl", options: mainOptions, security: mainSecurity},
339
+ content: {source: "./sources/content.wsdl", options: contentOptions, security: contentSecurity},
279
340
  });
341
+ (await clients.main.soapClient()).setEndpoint(mainEndpoint);
342
+ (await clients.content.soapClient()).setEndpoint(contentEndpoint);
280
343
 
281
- await app.listen({ port: 3000 });
344
+ const app = Fastify({logger: true});
345
+ await app.register(gateway, {clients});
346
+ await app.listen({port: 3000});
282
347
  ```
283
348
 
284
- See [`examples/fastify-gateway`](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/examples/fastify-gateway/README.md) for a complete example.
349
+ Runtime `source` supplies WSDL metadata, while `setEndpoint` selects the SOAP destination. Keep source, credentials, trust, headers, stream settings and cancellation decisions scoped to their client; overlapping SOAP operation names still dispatch through their owning route. Alternatively, provide a `{clients}` registry of preconfigured instances or compatible operations mocks directly.
350
+
351
+ The aggregate serves the combined definition at `/openapi.json`; generation `gateway.openapiPath` can change or disable that route. Do not register the aggregate under a Fastify runtime prefix, including an inherited parent prefix; configure each service's generation-time `openapi.basePath` instead.
352
+
353
+ Components and operation identifiers are qualified by service key, including stream item schema references. Local references and security scheme names are rewritten, and service-local root security/server semantics are preserved at their applicable operations or paths. Composition rejects duplicate final paths, unresolved/relative references and unsupported schema resources or anchors. Compiler settings and optional stream configuration stay inside each `generation`; checked composition and aggregate app/test scaffolding remain deferred.
354
+
355
+ The aggregate specification is JSON only. Aggregate spec/plugin staging protects the previous pair on failure, but earlier service-generated dependencies can already have changed. This is not a whole-directory transaction; regenerate and verify matched artifacts after a failure.
356
+
357
+ Single-source plugins can still be mounted manually in separate Fastify scopes. See [`examples/fastify-gateway`](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/examples/fastify-gateway/README.md) for single-source application wiring and the [composition API](api-reference.md#runmultiservicepipeline) for its supported settings and limits.
358
+
359
+ The credential-free [native composition example](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/examples/native-composition/README.md) uses two committed generic sources, explicit `.generated/` outputs and a compileable typed registry.
285
360
 
286
361
  ## Contract Assumptions
287
362
 
363
+ The following assumptions describe buffered legacy gateway input. Stream responses use format-specific bodies without success envelopes; checked components use their own value/schema contract. Native composition retains those per-service legacy contracts.
364
+
288
365
  - All request/response bodies must use $ref to components.schemas
289
366
  - Every operation must have a default response with application/json content
290
367
  - All referenced schemas must exist in components.schemas
@@ -4,6 +4,10 @@ Guide to using the TypeScript SOAP clients and types generated by wsdl-tsc.
4
4
 
5
5
  See [README](../README.md) for quick start and [Concepts](concepts.md) for type modeling details.
6
6
 
7
+ ## Upgrading generated output
8
+
9
+ Installing a generator version does not itself rewrite these files, but a consumer build/install script may regenerate them. For 1.2.0, review the [migration guide](migration.md#from-11x-to-120) before replacing matched artifacts: affected type names, stream records and corrected scalar/simple-content results can require consumer changes. Preserve adopted app/test scaffolds and keep the generator/runtime versions appropriate to the selected profile.
10
+
7
11
  ## Installed package definitions
8
12
 
9
13
  The generator package ships TypeScript declarations for its public ESM API. Definition lookup resolves to those packaged `.d.ts` files; original implementation source and declaration maps are not shipped for public API source navigation. Installed package smoke checks this definition resolution using the consumer's TypeScript language service; editor-specific UI behavior is not asserted.
@@ -140,20 +144,20 @@ const forecast = await client.GetCityForecastByZIP({
140
144
  ZIP: "10001"
141
145
  });
142
146
 
143
- console.log(forecast.GetCityForecastByZIPResult.Success);
144
- console.log(forecast.GetCityForecastByZIPResult.ForecastResult);
147
+ console.log(forecast.response.GetCityForecastByZIPResult?.Success);
148
+ console.log(forecast.response.GetCityForecastByZIPResult?.ForecastResult);
145
149
  ```
146
150
 
147
151
  Operations without input still require an empty object:
148
152
 
149
153
  ```typescript
150
154
  const info = await client.GetWeatherInformation({});
151
- console.log(info.GetWeatherInformationResult.WeatherDescriptions);
155
+ console.log(info.response.GetWeatherInformationResult?.WeatherDescription);
152
156
  ```
153
157
 
154
158
  ## Calling Stream Operations
155
159
 
156
- Operations opted into streaming with `--stream-config` return `StreamOperationResponse<RecordType>` instead of the buffered shape. `records` is a single-pass `AsyncIterable<RecordType>` that pulls bytes from the upstream SOAP response on demand, so memory stays bounded regardless of payload size.
160
+ Operations opted into streaming with `--stream-config` accept `(args, {signal, timeoutMs}?)` and return `StreamOperationResponse<RecordType>`. The single-pass `records` iterable maps complete records into generated DTOs. Memory depends on record size and transport/parser buffering; there is no constant-memory or byte-cap guarantee.
157
161
 
158
162
  ```typescript
159
163
  const result = await client.UnitDescriptiveInfoStream({});
@@ -164,6 +168,8 @@ for await (const record of result.records) {
164
168
 
165
169
  Because `records` is single-pass, each response can be iterated once. Headers captured before the first record are available on `result.headers`, and the serialized SOAP envelope that was sent upstream is on `result.requestRaw` when populated.
166
170
 
171
+ Exhaust or return the iterator, for example by breaking a `for await` loop, to dispose of transport resources. A per-call deadline starts after metadata initialization; an already-aborted signal rejects before it. Stream-enabled constructors accept `streamRequestOptions` for HTTP settings such as a verified HTTPS agent, separately from WSDL loading. See [transport settings and refusals](gateway-guide.md#streaming-transport-settings).
172
+
167
173
  Streaming requires the `saxes` runtime dependency. The generated app scaffold pins `saxes ^6.0.0` automatically; consumers that integrate the generated client into their own project must install it explicitly.
168
174
 
169
175
  See [Stream Configuration](configuration.md#stream-configuration) for how to opt specific operations in, and [ADR-002](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/docs/decisions/002-streamable-responses.md) for the rationale and terminal-error policy.
@@ -214,11 +220,11 @@ Generated gateway test suites use one valid branch in union-mode mocks. They als
214
220
  All operations and types are fully typed:
215
221
 
216
222
  ```typescript
217
- const result: GetCityWeatherByZIPResponse = await client.GetCityWeatherByZIP({
223
+ const result = await client.GetCityWeatherByZIP({
218
224
  ZIP: "10001"
219
225
  });
220
226
 
221
- result.GetCityWeatherByZIPResult.Temperature;
227
+ result.response.GetCityWeatherByZIPResult?.Temperature;
222
228
  ```
223
229
 
224
230
  Autocomplete and type checking work across all generated interfaces.
@@ -4,6 +4,10 @@ A step-by-step guide for modernizing a SOAP API into a typed TypeScript client a
4
4
 
5
5
  For CLI flag details, see [CLI Reference](cli-reference.md). For gateway integration, see [Gateway Guide](gateway-guide.md).
6
6
 
7
+ See the root [README](../README.md) and [1.1.x-to-1.2.0 migration](migration.md#from-11x-to-120) before regenerating. Native legacy composition retains per-client ownership; checked/RPC generation has a separate buffered validation contract.
8
+
9
+ This playbook describes adopting a SOAP-to-REST integration. Updating an existing generated gateway is a separate package-upgrade task: keep its profile first, review matched regenerated artifacts and preserved scaffolds, and use the version migration guide's verification and rollback steps. Adopting checked generation or composition should be a deliberate follow-up rather than an incidental upgrade effect.
10
+
7
11
  ## Overview
8
12
 
9
13
  The typical migration follows this sequence:
package/docs/migration.md CHANGED
@@ -1,23 +1,141 @@
1
1
  # Migration Guide
2
2
 
3
- This guide covers upgrade steps between major versions of `@techspokes/typescript-wsdl-client`.
3
+ This guide covers package upgrades and generated-project compatibility for `@techspokes/typescript-wsdl-client`.
4
4
 
5
5
  Versions are listed newest first. Find your current version and follow the steps for each version you need to cross.
6
6
 
7
+ See the root [README](../README.md). Historical version sections below describe their original releases; 1.2.0 compatibility guidance supersedes earlier unchanged-output statements for this upgrade.
8
+
7
9
  ## General Upgrade Steps
8
10
 
9
11
  These steps apply to every version upgrade:
10
12
 
11
- 1. Update the package: `npm install --save-dev @techspokes/typescript-wsdl-client@latest`
12
- 2. Read the migration section for your version jump below
13
- 3. Regenerate all output by running your generation script or pipeline command
14
- 4. Run `tsc --noEmit` to verify the generated code compiles
15
- 5. Test your application
13
+ 1. Read the migration section for the installed and intended versions before updating dependencies.
14
+ 2. Preserve the working dependency lockfile, inputs, generation settings and application customizations.
15
+ 3. Install an explicit target version in an upgrade branch.
16
+ 4. Regenerate matched generator-owned artifacts using the existing generation script and options.
17
+ 5. Review the generated diff and run the consumer project's TypeScript build and tests.
18
+
19
+ ## From 1.1.x to 1.2.0
20
+
21
+ ### Published starting points
22
+
23
+ Both 1.1.5 and 1.1.8 were published to npm. Versions 1.1.6 and 1.1.7 were unpublished candidates whose preview work was carried into 1.1.8; their GitHub tags and release-note files do not make them installable npm versions. This publication history was checked against the [npm registry metadata](https://registry.npmjs.org/@techspokes%2Ftypescript-wsdl-client) on 2026-11-10.
24
+
25
+ Use these instructions for either published starting point. A project upgrading from 1.1.5 also crosses the checked-generation preview and legacy conversion corrections already shipped in 1.1.8. Historical release notes remain a record of their original candidates, not additional required installation steps.
26
+
27
+ ### Package installation versus regeneration
28
+
29
+ Publishing a package does not change a deployed gateway. Installing a newer generator does not itself rewrite existing generated files; a consumer's install/build scripts may run generation, and dependency resolution or package-runtime imports can change behavior without regeneration.
30
+
31
+ | Project action | Upgrade effect |
32
+ |---|---|
33
+ | Keep the installed package, lockfile and deployed build | No automatic switch to 1.2.0 |
34
+ | Update the package without running generation | Existing generated files remain; runtime dependencies still need verification |
35
+ | Run generation during a build or install | New output can appear in that next build |
36
+ | Regenerate using the existing legacy command | Legacy remains selected; compiler and mapper corrections take effect |
37
+ | Select checked generation or native composition | Deliberate adoption with separate contracts and configuration |
38
+
39
+ A dependency range of `^1.1.5` or `^1.1.8` permits 1.2.0 when npm resolves an update. An exact version pin or a `~1.1.x` range does not permit this minor upgrade. Commit the lockfile and use `npm ci` to reproduce its recorded dependency tree; updating or replacing that lockfile is a separate deliberate step. See [npm version ranges](https://docs.npmjs.com/about-semantic-versioning/) and [npm ci](https://docs.npmjs.com/cli/v11/commands/npm-ci/).
40
+
41
+ ### Changes that can affect existing projects
42
+
43
+ The 1.2.0 compiler correction separates declarations that previously collapsed into one generated type. Distinct inline `Entry` declarations under `Left` and `Right` now receive separate owner-qualified names. Non-colliding exported names and XML/JSON member names are preserved; residual Pascal-case or namespace collisions receive deterministic suffixes.
44
+
45
+ This minor release includes corrective compatibility changes: affected exported DTO names can break source imports after regeneration, and stream records now use generated DTOs rather than lexical attribute bags. The 1.2.0 version number is not a guarantee of a drop-in regeneration upgrade; review the following impacts before deploying the regenerated project.
46
+
47
+ | Existing use | What to review |
48
+ |---|---|
49
+ | Buffered legacy gateway | Keep its model and envelope; inspect regenerated schemas and DTO conversion |
50
+ | Colliding declaration names | Update affected imports, type annotations and stream `recordType` references |
51
+ | Legacy streaming | Update consumers expecting lexical strings or nested attribute bags |
52
+ | Boolean attributes or simple content | Review corrected scalar conversion and `$value` objects |
53
+ | Adopted app or test scaffolds | Merge intended updates; existing files are preserved by default |
54
+ | Published 1.1.5 checked direct clients | Retain the versioned binding and its original execution restrictions |
55
+
56
+ Regeneration can also change OpenAPI model names and generated SDK names that depend on them. Preserving an XML/JSON member name does not preserve a previously incorrect value shape or scalar conversion. Do not mix a newly generated client with an older catalog, metadata helper, gateway or OpenAPI document to suppress these changes.
57
+
58
+ ### Stream record migration
59
+
60
+ Configured stream methods still return `records: AsyncIterable<RecordType>`, but each completed XML record now passes through generated DTO mapping. Flattened attributes, typed `$value` content, configured scalar policies, XML lists and repeated fields follow the generated model. The gateway sends those mapped records as NDJSON or JSON-array items without a legacy success envelope.
61
+
62
+ For a record with Boolean `Enabled` and integer `Rank` attributes and an integer `Count` child, a previous parser-shaped record using the default output attribute key could look like this:
63
+
64
+ ```json
65
+ {"attributes":{"Enabled":"1","Rank":"7"},"Count":"42"}
66
+ ```
67
+
68
+ The corresponding mapped DTO fields are:
69
+
70
+ ```json
71
+ {"Enabled":true,"Rank":7,"Count":42}
72
+ ```
73
+
74
+ This is a field-level illustration, not a complete SOAP payload or a rule that all numbers become JavaScript numbers. Decimal and int64 policies remain configurable and string-first by default; JavaScript `bigint` still requires a consumer-owned JSON strategy. Update stream consumers and review their advertised OpenAPI schemas together.
75
+
76
+ Stream calls add optional `{signal, timeoutMs}` and supported node-soap serializer/transport hooks. Requests now use the WSDL serializer and supported client endpoint/security settings; verify those settings against the intended backend. SOAP 1.2, functional SOAP headers, SOAP body attributes, MTOM and NTLM remain refused stream combinations. See the [stream API](api-reference.md#generated-stream-client-contract) and [transport boundaries](gateway-guide.md#streaming-transport-settings).
77
+
78
+ ### Buffered conversion corrections since 1.1.5
79
+
80
+ The intervening published 1.1.8 includes corrections for Boolean XML attributes and simple-content responses. Valid Boolean spellings become actual Booleans; invalid spellings reject instead of producing misleading successful values. Text-only complex simple content retains its `$value` object, including when optional attributes are absent. Review application code that compensated for the previous incorrect results.
81
+
82
+ The ordinary buffered legacy call still exposes its generated `.response` value, and its gateway retains the legacy success envelope. Omitting stream configuration does not promise byte-identical generated output: shared mapper and metadata corrections also apply to buffered clients.
83
+
84
+ ### Upgrade an existing legacy gateway
85
+
86
+ Keep Node.js >= 24 and the existing ESM project setup. Keep the generator as a development dependency and `soap` as a runtime dependency; retain the gateway's other runtime dependencies. A separately maintained generated app has its own package and lockfile, so upgrading the generator in its parent project does not automatically refresh that app's dependencies.
87
+
88
+ After 1.2.0 is published, install the explicit target in the project that owns generation:
89
+
90
+ ```bash
91
+ npm install --save-dev --save-exact @techspokes/typescript-wsdl-client@1.2.0
92
+ ```
93
+
94
+ Before publication, use a locally packed candidate as described in [checked-generation installation guidance](checked-generation.md#install-and-try-the-bundled-contract), rather than requesting an unpublished registry version. Do not add a checked model flag just to upgrade a legacy project.
95
+
96
+ 1. Preserve a known-working revision with its dependency lockfiles and generation inputs.
97
+ 2. Run the existing generation script with the same profile, paths, naming and mapping options.
98
+ 3. Regenerate matched catalog, client, metadata helpers, OpenAPI and gateway artifacts from original WSDL/XSD sources.
99
+ 4. Review generated type/schema names, buffered values and any configured stream records.
100
+ 5. Merge app/configuration/package and test scaffold updates deliberately.
101
+ 6. Run the consumer project's TypeScript check with generated files included and its maintained tests.
102
+ 7. Verify representative request, response and fault behavior against an authorized test backend before deployment.
103
+
104
+ Existing app and generated-test scaffold files are skipped by default. `pipeline --force-init` and `--force-test` explicitly replace their respective scaffolds; standalone `app --force` has the app replacement role. Preserve custom server, authentication, environment, package and test changes before selecting any force option. Do not delete an entire app directory merely to refresh the replaceable client/gateway files inside it.
105
+
106
+ The compiled catalog format remains unchanged. Old catalogs remain readable as stored, but cannot recover declarations lost through an earlier merge; loading one does not fetch its WSDL or reconstruct missing types. Companion catalogs retain their existing conservative same-name structural conflict checks.
107
+
108
+ ### Optional adoption of new capabilities
109
+
110
+ | Capability | Adoption boundary |
111
+ |---|---|
112
+ | Native legacy multi-service gateway | Use `pipeline --services-config`; retain independent sources, clients and security |
113
+ | Checked document/literal or bounded RPC | Trial `pipeline --model checked-components-v1` in separate output paths |
114
+ | Existing manual gateway mounting | Continue registering single-source plugins in separate Fastify scopes |
115
+
116
+ Native composition emits one aggregate plugin and OpenAPI document for legacy services; it does not supply an aggregate app/test scaffold or compose checked routes. See the [multi-service guide](gateway-guide.md#multi-service-setup). Check downstream tooling when adopting service-qualified OpenAPI components and operation identifiers.
117
+
118
+ Checked generation is a bounded buffered preview, not an automatic replacement for a legacy gateway. Its JSON values, validation and fault contracts differ from legacy DTO/envelope behavior, and unsupported contracts refuse. Checked hosts import this package at runtime, so retain it as a production dependency according to the emitted app manifest; a generator-only development dependency is insufficient for that deployment. Keep runtime version, source snapshots and generated checked artifacts matched. Checked streaming remains outside this profile.
119
+
120
+ Existing published 1.1.5-issued direct clients retain `checkedDirectRuntimeV1` and its original owned-loopback restrictions; installed compatibility fixtures exercise that specific binding. A deployed host importing that retained binding also needs this package as a production dependency, even when its generator tooling lives elsewhere. The unchanged direct protocol allows those retained artifacts to use the newer compatible package binding; it does not grant component-runtime capabilities.
121
+
122
+ The unpublished `checked-direct-v1` generation choice is not a current generation mode. New checked generation uses components; moving an old direct client to components is deliberate regeneration and adoption, not a runtime capability upgrade. See [checked-generation upgrade guidance](checked-generation.md#upgrade-and-regenerate).
123
+
124
+ ### Consumer verification and rollback
125
+
126
+ Release verification exercises representative generated/installed consumers, compatibility fixtures and owned SOAP exchanges. It cannot certify every existing consumer project or provider operation. Generated mock/scaffold tests do not replace maintained application tests, TypeScript checks or independent SOAP/provider acceptance; follow [consumer testing guidance](testing.md#for-consumer-projects).
127
+
128
+ Verify affected type imports, OpenAPI/SDK changes, scalar and `$value` conversions, stream item shapes and cancellation/error handling. Retain endpoint, credentials, TLS trust and array-wrapper settings explicitly. The published preview does not provide a universal migration checker or a guarantee of complete WSDL/XSD support.
129
+
130
+ For rollback, restore the known-working application revision, generator/app package manifests and lockfiles, matched generated artifacts and source/configuration inputs together, then reinstall with `npm ci` and rebuild. Projects that do not commit generated output should regenerate using the original pinned generator and inputs. Restoring only the generator version leaves newly generated code in place and is not a complete rollback.
16
131
 
17
132
  ## Version Compatibility
18
133
 
134
+ The 1.2.0 scaffold row records the current legacy app template pins, not an automatic rewrite of an existing consumer's package. Follow the emitted manifest for new apps and review separately maintained runtime dependencies when upgrading older ones.
135
+
19
136
  | wsdl-tsc | Node.js | TypeScript | soap | Fastify | saxes |
20
137
  |----------|---------|------------|------|---------|-------|
138
+ | 1.2.0 legacy scaffold | >= 24.0 | ^6.0.3 | ^1.14.0 | ^5.12.8 | ^6.0.0 |
21
139
  | 1.0.x | >= 24.0 | >= 6.0 | >= 1.9 | >= 5.10 | >= 6.0 |
22
140
  | 0.35.x and later | >= 24.0 | >= 6.0 | >= 1.9 | >= 5.8 | >= 6.0 |
23
141
  | 0.17.x | >= 20.0 | >= 6.0 | >= 1.9 | >= 5.8 | >= 6.0 |
@@ -2,9 +2,11 @@
2
2
 
3
3
  This document describes each file produced by the generator, what it contains, and how to work with it. For CLI flags that control output, see [CLI Reference](cli-reference.md).
4
4
 
5
+ See the root [README](../README.md) for the documentation index. This catalog-based flow describes legacy output; [checked generation](checked-generation.md) emits source-backed artifacts with a separate value/validation contract.
6
+
5
7
  ## TypeScript File Preambles
6
8
 
7
- All complete emitted TypeScript files receive one [generated preamble](generated-code.md#generated-preambles-and-eslint), including optional helpers and `_typecheck.ts`. App and generated-test scaffolds use editable-source wording and retain their existing skip/force behavior. Data outputs and existing consumer source remain unchanged.
9
+ All complete emitted TypeScript files receive one [generated preamble](generated-code.md#generated-preambles-and-eslint), including optional helpers and `_typecheck.ts`. App and generated-test scaffolds use editable-source wording and retain their existing skip/force behavior. Preambles do not alter data files or existing consumer source; regeneration compatibility follows the [version migration guide](migration.md#from-11x-to-120).
8
10
 
9
11
  ## Generation Flow
10
12
 
@@ -25,15 +27,15 @@ Generated into the directory specified by `--client-dir`.
25
27
 
26
28
  | File | Purpose |
27
29
  |------|---------|
28
- | types.ts | All TypeScript interfaces derived from WSDL/XSD types |
30
+ | types.ts | TypeScript models projected from WSDL/XSD types |
29
31
  | client.ts | SOAP client class with one typed method per operation |
30
32
  | operations.ts | Pure interface matching client methods, for mocking without importing `soap` |
31
- | utils.ts | Runtime helpers including array unwrapping and type metadata |
33
+ | utils.ts | JSON/SOAP conversion metadata |
32
34
  | catalog.json | Compiled WSDL data in JSON format, used by other generation stages |
33
35
 
34
36
  ### types.ts
35
37
 
36
- Contains one interface per WSDL complex type. Attributes and elements are flattened into peer properties. Inheritance is expressed with TypeScript `extends`. All numeric and date-time types map to `string` by default to prevent precision loss.
38
+ Declares generated interfaces and aliases with flattened attribute and element properties. Inheritance can use TypeScript `extends`. Scalar types follow compiler policies: int64, arbitrary-size integers, decimal and date/time default to strings; ordinary numeric and Boolean types retain their projected types.
37
39
 
38
40
  ### client.ts
39
41
 
@@ -45,7 +47,7 @@ Exports a TypeScript interface with the same method signatures as the client cla
45
47
 
46
48
  ### utils.ts
47
49
 
48
- Contains runtime metadata and helper functions. Includes `unwrapArrayWrappers()` for bridging between SOAP array wrapper objects and flattened OpenAPI array schemas.
50
+ Provides metadata used by the client's `toSoapArgs` and `fromSoapResult` methods. Optional array-wrapper flattening belongs to gateway `runtime.ts` and applies per record for streams.
49
51
 
50
52
  ### Stream Operations
51
53
 
@@ -61,7 +63,7 @@ export type StreamOperationResponse<RecordType, HeadersType = Record<string, unk
61
63
  };
62
64
  ```
63
65
 
64
- `client.ts` gains a protected `callStream()` transport. Stream methods bypass `node-soap` (which buffers the full response before invoking its callback, as confirmed in phase-0 research) and instead POST a hand-built SOAP envelope via global `fetch`, piping the response body through a SAX-driven record parser. `node-soap` remains the transport for buffered operations.
66
+ `client.ts` gains a protected `callStream()` transport using node-soap's WSDL serializer and public HTTP `requestStream`. Completed parser records pass through the generated DTO mapper; iterator return, per-call abort/deadline and downstream disconnect release the upstream transport. See [stream transport settings](gateway-guide.md#streaming-transport-settings).
65
67
 
66
68
  Generated imports required at runtime: `saxes` for SAX parsing. The generated app scaffold pins `saxes ^6.0.0` automatically; manual consumers must install it explicitly.
67
69
 
@@ -73,7 +75,7 @@ Generated at the path specified by `--openapi-file`.
73
75
  |------|---------|
74
76
  | openapi.json (or .yaml) | Complete OpenAPI 3.1 specification |
75
77
 
76
- The spec includes one POST path per WSDL operation, request and response schemas in `components/schemas`, and descriptions derived from WSDL documentation annotations. Schemas are generated from the same catalog used for TypeScript types, so the two outputs stay aligned.
78
+ The spec defaults to one POST path per WSDL operation, with request/response schemas in `components/schemas` and source documentation descriptions. Configured defaults and operation overrides can select other HTTP methods. Schemas and TypeScript models use the same catalog.
77
79
 
78
80
  CLI OpenAPI generation always validates the generated spec using `@apidevtools/swagger-parser`.
79
81
 
@@ -120,7 +122,7 @@ JSON array streams default to `application/json` and declare the record schema a
120
122
  }
121
123
  ```
122
124
 
123
- OpenAPI 3.1 cannot fully describe an NDJSON sequence as a standard JSON Schema document, so the extension makes the item schema explicit for generated gateways, documentation tools, and future SDK generators. JSON array streams use a normal array schema and keep the extension for downstream tooling. Error responses (400, 502, and the rest) still use the normal envelope.
125
+ OpenAPI 3.1 cannot fully describe an NDJSON sequence as a standard JSON Schema document, so the extension makes the item schema explicit. JSON-array streams use an array schema and retain the extension. Errors handled before response commitment can use the legacy error envelope; later failures truncate output.
124
126
 
125
127
  ## Gateway Output
126
128
 
@@ -143,7 +145,7 @@ Each route file in `routes/` follows the same pattern: validate the JSON request
143
145
 
144
146
  ### Stream Routes
145
147
 
146
- Stream-configured operations generate a different route shape. The Fastify response serialization schema is omitted because the handler sends a `Readable` directly. NDJSON handlers set `reply.type("application/x-ndjson")` and return `reply.send(toNdjson(result.records))`; JSON array handlers set `reply.type("application/json")` and return `reply.send(toJsonArray(result.records))`. `runtime.ts` gains `toNdjson<T>(records: AsyncIterable<T>): Readable` and `toJsonArray<T>(records: AsyncIterable<T>): Readable` helpers. See the [Gateway Guide](gateway-guide.md#streaming-handlers) for the full handler example and terminal-error policy.
148
+ Stream routes omit Fastify response serialization and deliver a `Readable` using `toNdjson` or `toJsonArray`. Completed records are mapped and optionally unwrapped per record; the route connects downstream disconnects to a per-call abort signal before waiting for upstream headers and throughout delivery. See [Streaming Handlers](gateway-guide.md#streaming-handlers) for the cancellable flow and pre-commit versus mid-stream failure policy.
147
149
 
148
150
  ### Generated Test Surface
149
151
 
@@ -155,26 +157,27 @@ The generated plugin exports a Fastify plugin function. Register it with your Fa
155
157
 
156
158
  ```typescript
157
159
  import Fastify from "fastify";
158
- import { myServiceGateway } from "./.generated/gateway/plugin.js";
159
- import { createMyServiceClient } from "./.generated/client/client.js";
160
+ import gateway from "./.generated/gateway/plugin.js";
161
+ import { Weather } from "./.generated/client/client.js";
160
162
 
161
163
  const app = Fastify();
162
- const client = await createMyServiceClient("https://soap-endpoint.example.com");
163
- await app.register(myServiceGateway, { client, prefix: "/v1/my-service" });
164
+ const client = new Weather({source: "./weather.wsdl"});
165
+ await app.register(gateway, { client, prefix: "/v1/weather" });
164
166
  ```
165
167
 
166
168
  ## App Output
167
169
 
168
- Generated when `--init-app` is passed. Creates a runnable Fastify application in a sibling `app/` directory.
170
+ Generated with `pipeline --init-app` or the `app` command. `--app-dir` selects the output location; the default is a sibling `app/` directory.
169
171
 
170
172
  | File | Purpose |
171
173
  |------|---------|
172
- | index.ts | Application entry point with server startup |
174
+ | server.ts | Application entry point with server startup |
175
+ | config.ts | Environment-based configuration loader |
173
176
  | .env.example | Environment variable template for WSDL URL, port, host |
174
177
  | package.json | Dependencies and npm scripts |
175
178
  | tsconfig.json | TypeScript configuration for the app |
176
179
 
177
- App files have overwrite protection: they are written only if they do not already exist. This prevents regeneration from overwriting your customizations. Use `--force-app` to override this behavior.
180
+ App scaffolds are preserved by default. Use `pipeline --force-init` or standalone `app --force` only for deliberate replacement after reviewing adopted customizations.
178
181
 
179
182
  ## What to Commit
180
183
 
@@ -182,6 +185,6 @@ All generated files are deterministic. Committing them to version control is saf
182
185
 
183
186
  ## What to Customize
184
187
 
185
- - App files (`index.ts`, `.env`): these are your entry points; customize freely after first generation
188
+ - Adopted app files (`server.ts`, `config.ts`, `.env`) are consumer-owned customization points.
186
189
  - Gateway plugin, routes, and schemas: do not edit manually; they are overwritten on regeneration
187
190
  - Add custom middleware, auth, logging, and error handling in your app code, not in generated gateway files
@@ -71,11 +71,13 @@ Records flow through `Readable.from` in the generated `runtime.ts`. The iterator
71
71
 
72
72
  ### Connection Timeouts
73
73
 
74
- Stream responses can stay open for the full duration of the upstream SOAP call. The Fastify `keepAliveTimeout`, `requestTimeout`, and any reverse proxy (nginx, ALB, CloudFront) must allow the maximum expected upstream duration. Default Fastify timeouts are shorter than typical long-running SOAP batch responses.
74
+ Stream responses can stay open for the duration of the upstream SOAP call. Configure an upstream operation deadline and align reverse-proxy/load-balancer limits with the expected duration and idle gaps. Supported legacy stream calls accept `{signal, timeoutMs}`; their per-call deadline starts after SOAP metadata initialization.
75
+
76
+ Fastify's `requestTimeout` limits receipt of the incoming request, while HTTP keep-alive settings govern idle connection reuse; neither is a substitute for the SOAP operation deadline. Review the [Fastify server options](https://fastify.dev/docs/latest/Reference/Server/) separately from [stream transport settings](gateway-guide.md#streaming-transport-settings). Do not assume a universal Fastify default will bound an active upstream call.
75
77
 
76
78
  ### Memory Profile
77
79
 
78
- Memory stays bounded regardless of payload size because records are parsed and emitted one at a time. The SAX parser buffers only within the current record element. This is the primary reason to opt into streaming: buffered operations load the full response into memory before yielding.
80
+ Streaming avoids materializing the entire response, but memory depends on record size and transport/parser buffering. A large record still requires memory before DTO mapping; no constant-memory or total-byte-cap guarantee is provided.
79
81
 
80
82
  ### Observability
81
83
 
@@ -83,7 +85,7 @@ Log the time to first record, not just the time to response completion. First-re
83
85
 
84
86
  ### Terminal-Error Policy
85
87
 
86
- Errors raised before the first record use the normal gateway error envelope because the JSON array helper prefetches the first record before emitting `[`. Errors raised mid-stream truncate the response. NDJSON consumers detect this as an incomplete HTTP response, and JSON array consumers must treat an incomplete or invalid JSON document as a failed stream. Document this behavior for downstream API consumers so they distinguish truncation from a legitimate empty stream.
88
+ Errors handled before response commitment can use the gateway error envelope. The JSON-array helper prefetches before emitting `[`, but a first-record boundary alone is not a universal commitment guarantee. After commitment, failures truncate output; consumers must treat incomplete HTTP or JSON as a failed stream, even when earlier records were valid.
87
89
 
88
90
  ## Known Limitations
89
91
 
@@ -107,6 +109,6 @@ Single-child sequences with maxOccurs>1 become array schemas. Sequences with mul
107
109
 
108
110
  `ndjson` is the default stream format. `json-array` is supported for operations that need a single JSON document response; it streams records incrementally as a JSON array and does not buffer the full SOAP response.
109
111
 
110
- ### Stream Transport Bypasses node-soap
112
+ ### Stream transport settings
111
113
 
112
- Stream operations bypass `node-soap` entirely and POST a hand-built SOAP envelope via `fetch`. Any `node-soap` middleware, interceptors, or custom security handlers that your buffered operations rely on will not apply to stream operations. Authentication headers, proxies, and TLS options must be configured on the stream transport path separately.
114
+ Stream operations use node-soap's WSDL serializer and public HTTP `requestStream`, including supported static headers, endpoint and security hooks. Configure stream HTTP options separately from WSDL acquisition; see [transport support and refusals](gateway-guide.md#streaming-transport-settings). On supported Node versions, `node --use-system-ca` uses verified system trust; caller-specific CA/HTTPS agent settings remain client-owned. Keep certificate verification enabled.