@techspokes/typescript-wsdl-client 1.1.8 → 1.3.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 (85) hide show
  1. package/README.md +22 -3
  2. package/dist/checkedPlanning.js +27 -4
  3. package/dist/cli.js +17 -1
  4. package/dist/client/checkedDirectRuntime.d.ts +11 -1
  5. package/dist/client/checkedDirectRuntime.js +19 -2
  6. package/dist/client/directClientRuntime.d.ts +18 -1
  7. package/dist/client/directClientRuntime.js +15 -3
  8. package/dist/client/directClientVersion.d.ts +2 -0
  9. package/dist/client/directClientVersion.js +2 -0
  10. package/dist/client/generateClient.js +72 -16
  11. package/dist/client/generateDirectClient.js +14 -9
  12. package/dist/client/generateOperations.js +30 -11
  13. package/dist/client/generateUtils.js +5 -1
  14. package/dist/compiler/canonicalGraph.d.ts +4 -15
  15. package/dist/compiler/canonicalGraph.js +1 -8
  16. package/dist/compiler/componentRecordProjection.d.ts +3 -0
  17. package/dist/compiler/componentRecordProjection.js +10 -0
  18. package/dist/compiler/declarationIdentity.d.ts +22 -0
  19. package/dist/compiler/declarationIdentity.js +9 -0
  20. package/dist/compiler/directCodec.d.ts +22 -2
  21. package/dist/compiler/directCodec.js +40 -7
  22. package/dist/compiler/directOperation.d.ts +28 -5
  23. package/dist/compiler/directOperation.js +84 -14
  24. package/dist/compiler/directSoap.d.ts +40 -10
  25. package/dist/compiler/directSoap.js +151 -18
  26. package/dist/compiler/directSoapSource.js +3 -2
  27. package/dist/compiler/directSourceCapsule.js +69 -4
  28. package/dist/compiler/directStructure.js +6 -2
  29. package/dist/compiler/directStructurePlan.d.ts +10 -1
  30. package/dist/compiler/directStructurePlan.js +27 -7
  31. package/dist/compiler/directValues.d.ts +15 -3
  32. package/dist/compiler/directValues.js +20 -4
  33. package/dist/compiler/legacyDeclarations.d.ts +26 -0
  34. package/dist/compiler/legacyDeclarations.js +144 -0
  35. package/dist/compiler/projectionScalar.d.ts +18 -1
  36. package/dist/compiler/projectionScalar.js +47 -13
  37. package/dist/compiler/rpcStringValues.d.ts +30 -0
  38. package/dist/compiler/rpcStringValues.js +196 -0
  39. package/dist/compiler/rpcTypedValues.d.ts +48 -0
  40. package/dist/compiler/rpcTypedValues.js +190 -0
  41. package/dist/compiler/schemaCompiler.js +44 -109
  42. package/dist/compiler/semanticCatalog.d.ts +2 -2
  43. package/dist/compiler/soapHeaders.d.ts +10 -4
  44. package/dist/compiler/soapHeaders.js +28 -9
  45. package/dist/gateway/generateCheckedGateway.js +26 -6
  46. package/dist/gateway/generateComposedGateway.d.ts +16 -0
  47. package/dist/gateway/generateComposedGateway.js +65 -0
  48. package/dist/gateway/generateGateway.js +1 -0
  49. package/dist/gateway/generators.js +19 -4
  50. package/dist/gateway/helpers.d.ts +1 -0
  51. package/dist/gateway/helpers.js +15 -0
  52. package/dist/generation/componentShape.js +25 -1
  53. package/dist/index.d.ts +5 -1
  54. package/dist/index.js +3 -1
  55. package/dist/loader/orderedSyntax.d.ts +4 -0
  56. package/dist/loader/orderedSyntax.js +18 -4
  57. package/dist/multiServicePipeline.d.ts +41 -0
  58. package/dist/multiServicePipeline.js +287 -0
  59. package/dist/openapi/composeOpenAPI.d.ts +29 -0
  60. package/dist/openapi/composeOpenAPI.js +322 -0
  61. package/dist/pipeline.d.ts +1 -1
  62. package/dist/pipeline.js +2 -0
  63. package/docs/agent-skill.md +10 -1
  64. package/docs/api-reference.md +127 -9
  65. package/docs/checked-gateway.md +10 -0
  66. package/docs/checked-generation.md +11 -5
  67. package/docs/cli-reference.md +25 -5
  68. package/docs/concepts.md +4 -5
  69. package/docs/configuration.md +10 -3
  70. package/docs/corpus.md +1 -1
  71. package/docs/gateway-guide.md +104 -27
  72. package/docs/generated-code.md +12 -6
  73. package/docs/migration-playbook.md +4 -0
  74. package/docs/migration.md +134 -6
  75. package/docs/output-anatomy.md +20 -17
  76. package/docs/production.md +7 -5
  77. package/docs/start-here.md +12 -2
  78. package/docs/supported-patterns.md +23 -0
  79. package/docs/testing.md +2 -0
  80. package/docs/troubleshooting.md +3 -1
  81. package/llms.txt +9 -1
  82. package/package.json +2 -2
  83. package/src/runtime/clientStreamMethods.tpl.txt +156 -148
  84. package/src/runtime/operationsStreamHelper.tpl.txt +6 -6
  85. package/src/runtime/streamXml.ts +48 -20
@@ -1,16 +1,18 @@
1
1
  # Try opt-in checked generation
2
2
 
3
- Version 1.1.8 offers an opt-in WSDL-to-checked-gateway workflow for early testing and feedback. Choose `pipeline --model checked-components-v1` to generate a typed SOAP client, OpenAPI, Fastify gateway, runnable app and scaffold tests for supported document/literal SOAP 1.1 contracts. Omitting `--model` keeps the existing legacy behavior.
3
+ Opt-in checked generation supports qualified SOAP 1.1 and SOAP 1.2 document/literal contracts and the bounded SOAP 1.1 RPC/encoded string profile. Choose `pipeline --model checked-components-v1`; omitting `--model` keeps legacy generation. SOAP 1.2 execution is available starting with 1.3.0; see the [binding and artifact contract](checked-gateway.md#component-service-generation). See the root [README](../README.md).
4
4
 
5
5
  This is a bounded preview, not general WSDL/XSD or production-provider qualification. It adds source-backed request, response, fault and artifact checks for the supported fragment. Generation exposes legacy and components only; the shipped v1.1.5 direct runtime remains a compatibility binding for existing clients. See the [checked gateway reference](checked-gateway.md).
6
6
 
7
7
  ## Install and try the bundled contract
8
8
 
9
+ The examples target 1.3.0. Before that version is published, install a locally packed 1.3.0 tarball instead of requesting an unpublished registry version. After publication, use the explicit registry version below. Earlier releases retain their own documented contracts.
10
+
9
11
  Use Node.js 24 or newer. Run these commands in a new trial directory:
10
12
 
11
13
  ```powershell
12
14
  npm init -y
13
- npm install --save-dev @techspokes/typescript-wsdl-client@1.1.8
15
+ npm install --save-dev @techspokes/typescript-wsdl-client@1.3.0
14
16
  npm install soap
15
17
  npx wsdl-tsc pipeline --model checked-components-v1 --wsdl-source node_modules/@techspokes/typescript-wsdl-client/docs/examples/checked-forecast.wsdl --client-dir .generated/forecast/client --openapi-file .generated/forecast/openapi.json --gateway-dir .generated/forecast/gateway --gateway-version-prefix v1 --gateway-service-name forecast --init-app --app-dir .generated/forecast --test-dir .generated/forecast/test --checked-report .generated/forecast-plan.json
16
18
  ```
@@ -69,7 +71,7 @@ Read the planning report before editing anything. Unsupported source, mapping, b
69
71
 
70
72
  Checked planning refusals show the construct or source location, the reason and the next action, then exit nonzero without generating artifacts or invoking SOAP. Save the unchanged structured report with `--checked-report`; set the `DEBUG` environment variable to `wsdl-tsc` when you need the raw error, stack and report details.
71
73
 
72
- For a locationless import, `--checked-draft` can write `service-resolution.json` containing a `REQUIRED_SCHEMA_LOCATION` placeholder. Replace that value with the actual permitted schema location, then rerun with `--checked-config service-resolution.json`. This supplies missing source information; it does not waive native checks or reconstruct a type graph by hand.
74
+ For a locationless import whose referenced components are unavailable, `--checked-draft` can write `service-resolution.json` containing a `REQUIRED_SCHEMA_LOCATION` placeholder. Replace that value with the actual permitted schema location, then rerun with `--checked-config service-resolution.json`. Unused locationless imports need no fabricated schema; native qualification and reference resolution remain mandatory.
73
75
 
74
76
  Multiple service/port/binding candidates require an explicit choice. Partial configuration and operation subsets are supported; see [selection and recovery details](checked-gateway.md#planning-and-missing-schema-recovery). Reports and drafts are preserved by default; use `--force-checked-files` deliberately to replace them on a later run.
75
77
 
@@ -133,7 +135,9 @@ The older `assessSchemaSource` API and `assess` command retain the narrow `check
133
135
 
134
136
  The qualified component fragment includes imported schemas, nested/repeated and inherited records, attributes, simple content, ordered values and supported scalar restrictions. [Keyed mixed/namespace/open values](checked-gateway.md#mixed-namespaced-and-open-values) additionally preserve source-permitted wildcard data with native strict/lax/skip checks. Escapia qualification retains 15 Main operations and two Content operations against owned services. Content uses explicit companion configuration with scoped validation and buffered delivery; it is not automatic companion discovery or checked streaming.
135
137
 
136
- General XSD, SOAP 1.2/RPC-encoded, broader authentication/security, arbitrary wildcard conversion and checked incremental streaming remain outside this preview. OpenAPI describes the JSON shape but does not express every native scalar/order/wildcard rule. Required declarations and strict wildcard failures cannot become successful unchecked replies.
138
+ The [bounded SOAP 1.1 RPC/encoded profile](supported-patterns.md#checked-soap-11-rpc-string-profile) supports typed scalar parts, native restrictions, one flat required Boolean record and one opaque string result. [NOAA qualification](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/docs/noaa-ndfd-qualification.md) covers all 12 operations against an owned server; ten live operations succeeded and two GML operations returned non-SOAP failures. There is no parsed DWML/GML or geographic correctness claim.
139
+
140
+ General XSD, general RPC encoding, broader authentication/security, arbitrary wildcard conversion and checked incremental streaming remain outside this preview. SOAP 1.2 document/literal uses the separately versioned binding described in the [checked gateway reference](checked-gateway.md#component-service-generation); live-provider acceptance is separate from owned-server protocol evidence. OpenAPI describes the JSON shape but does not express every native scalar/order/wildcard rule. Required declarations and strict wildcard failures cannot become successful unchecked replies.
137
141
 
138
142
  [#409](https://github.com/TechSpokes/typescript-wsdl-client/issues/409) supplies the bounded mixed/namespace/open mapping and its schema/serialization slice of [#410](https://github.com/TechSpokes/typescript-wsdl-client/issues/410). Representative unrelated contract qualification remains under [#397](https://github.com/TechSpokes/typescript-wsdl-client/issues/397); broader HTTP translation, source, scalar, group/naming, security and streaming work retains its existing owners. Legacy defaults and full-profile activation remain unchanged. See the [roadmap](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/ROADMAP.md) for direction and [development checkpoint](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/research/docs/content-model-development-checkpoint.md) for detailed evidence.
139
143
 
@@ -158,4 +162,6 @@ With generated defaults, omitted restrictions retain the generated choices; expl
158
162
 
159
163
  Consumer-owned server/config/test files are preserved unless their existing `--force-init` or `--force-test` option is selected. Review customizations before deliberately refreshing older host scaffolds. Existing v1.1.5-issued direct clients continue using `checkedDirectRuntimeV1` unchanged; new generation uses components.
160
164
 
161
- Legacy users need no model change. Refresh existing consumer dependency lockfiles separately to pick up transitive security fixes.
165
+ Legacy users need no model change. Follow the [1.1.x-to-1.2.0 migration guide](migration.md#from-11x-to-120) for affected generated names, stream values, buffered conversion fixes and rollback. The preview reached npm in 1.1.8; the earlier 1.1.6/1.1.7 candidates were unpublished.
166
+
167
+ Checked hosts import `@techspokes/typescript-wsdl-client` at runtime. Retain it in production dependencies as emitted by the checked app manifest; do not omit it from a deployment that installs only production dependencies. Refresh separately maintained consumer package/lockfiles deliberately, and preserve adopted app/test customizations when regenerating.
@@ -1,5 +1,7 @@
1
1
  # CLI Reference
2
2
 
3
+ Commands and configuration for generation and assessment. See the root [README](../README.md).
4
+
3
5
  Generation offers legacy, the default, and opt-in `pipeline --model checked-components-v1`. Start with [Try Opt-in Checked Generation](checked-generation.md); the [checked gateway reference](checked-gateway.md) covers exact configuration and v1.1.5 runtime compatibility. Checked generation emits source-backed contracts rather than a legacy catalog and refuses unsupported compiler/streaming controls.
4
6
 
5
7
  The `wsdl-tsc` tool generates TypeScript SOAP clients, OpenAPI specifications, and Fastify gateways from WSDL files.
@@ -44,6 +46,18 @@ Checked modes require all three explicit outputs: `--client-dir`, `--openapi-fil
44
46
 
45
47
  Run the complete generation pipeline in one pass: WSDL parsing, TypeScript client, OpenAPI spec, Fastify gateway, and optional app. This is the recommended command for most use cases.
46
48
 
49
+ ### Multi-service configuration
50
+
51
+ ```bash
52
+ npx wsdl-tsc pipeline --services-config ./services.json
53
+ ```
54
+
55
+ `--services-config` selects native legacy composition and is exclusive with the single-source pipeline flags below. The JSON file contains `services: [{key, generation}]`, aggregate `openapi: {outFile}` and aggregate `gateway: {outDir}` settings. Each `generation` uses the programmatic `PipelineOptions` field names, including explicit `wsdl`, `catalogOut`, `clientOutDir`, `openapi.outFile`, `openapi.basePath`, `gateway.outDir`, `gateway.serviceSlug` and `gateway.versionSlug`.
56
+
57
+ Paths resolve relative to the configuration file. Service keys, normalized gateway service slugs, route prefixes and generated ownership paths must remain distinct; source WSDLs must stay outside generated outputs. The aggregate OpenAPI file must be JSON, and its generated discovery route defaults to `/openapi.json`; set aggregate `gateway.openapiPath` to `false` to disable it.
58
+
59
+ Keep compiler options, `streamConfigFile` or parsed `streamConfig`, and OpenAPI/gateway options inside the owning service entry. Runtime credentials and client sources belong to the generated `createClients` options, separate from generation configuration. Aggregate app/test scaffolding and checked composition are not supported in this mode; see the [complete configuration and runtime example](gateway-guide.md#multi-service-setup).
60
+
47
61
  ### Usage
48
62
 
49
63
  ```bash
@@ -56,11 +70,11 @@ npx wsdl-tsc pipeline \
56
70
  [options]
57
71
  ```
58
72
 
59
- ### Required Flags
73
+ ### Required Single-Source Flags
60
74
 
61
75
  | Flag | Description |
62
76
  |------|-------------|
63
- | `--wsdl-source` | Path or URL to WSDL file |
77
+ | `--wsdl-source` | Required WSDL path or URL; `--catalog-file` selects the catalog output |
64
78
 
65
79
  At least one of `--client-dir`, `--openapi-file`, or `--gateway-dir` must be provided.
66
80
 
@@ -162,7 +176,9 @@ Legacy tests need consumer test/runtime dependencies and use their emitted Vites
162
176
 
163
177
  | Flag | Default | Description |
164
178
  |------|---------|-------------|
165
- | `--stream-config` | (none) | Path to a JSON stream-configuration file (ADR-002). Marks selected operations as streaming: client emits `AsyncIterable<RecordType>`, gateway serves NDJSON or JSON array output, OpenAPI advertises the record schema via `x-wsdl-tsc-stream`. Buffered output is unchanged when the flag is omitted. |
179
+ | `--stream-config` | (none) | Path to a JSON stream-configuration file; selects operations for mapped record streaming |
180
+
181
+ Configured client methods return records through `AsyncIterable<RecordType>`; gateway routes serve NDJSON or JSON-array items, and OpenAPI carries `x-wsdl-tsc-stream` record metadata. Unselected operations retain the buffered API, while generated mapper/metadata output can still change across versions. See [1.2.0 migration](migration.md#from-11x-to-120) before regenerating.
166
182
 
167
183
  `--stream-config` is accepted on the `compile`, `client`, and `pipeline` commands. It is not accepted on `openapi`, `gateway`, or `app` because those consume a pre-compiled `catalog.json` that already carries the normalized stream metadata.
168
184
 
@@ -281,7 +297,7 @@ npx wsdl-tsc client --catalog-file <path> --client-dir <path> [options]
281
297
 
282
298
  | Flag | Description |
283
299
  |------|-------------|
284
- | `--wsdl-source` | Path or URL to WSDL file |
300
+ | `--wsdl-source` or `--catalog-file` | WSDL source or precompiled catalog input |
285
301
  | `--client-dir` | Output directory for generated files |
286
302
 
287
303
  Provide either `--wsdl-source` (compile from WSDL) or `--catalog-file` (use pre-compiled catalog). When using `--wsdl-source`, the catalog is auto-generated in the client directory unless overridden with `--catalog-file`.
@@ -360,7 +376,7 @@ These flags are mutually exclusive.
360
376
 
361
377
  ### Response Envelope
362
378
 
363
- All responses are wrapped in a standard envelope (always-on since v0.7.1).
379
+ Buffered legacy success responses use the standard envelope. Legacy streams emit NDJSON or a JSON array directly; checked component routes use checked values/faults. Errors after stream commitment truncate output.
364
380
 
365
381
  Base structure:
366
382
 
@@ -467,6 +483,8 @@ Format: `urn:services:{serviceSlug}:{versionSlug}:schemas:{models|operations}:{s
467
483
 
468
484
  ### Contract Assumptions
469
485
 
486
+ These assumptions apply to buffered legacy gateway inputs. Stream response bodies and checked component schemas follow their separate contracts.
487
+
470
488
  - All request/response bodies must use `$ref` to `components.schemas`
471
489
  - Every operation must have a default response with `application/json`
472
490
  - All referenced schemas must exist in `components.schemas`
@@ -587,6 +605,8 @@ npx wsdl-tsc compile --wsdl-source <file|url> --catalog-file <path> [options]
587
605
 
588
606
  ### Compiler Flags
589
607
 
608
+ The shared [client/compiler flag table](#client-flags) documents import extensions, naming, scalar policies, unresolved references and optionality. `compile --help` lists the complete current surface, including stream configuration.
609
+
590
610
  | Flag | Default | Description |
591
611
  |------|---------|-------------|
592
612
  | `--client-choice-mode` | `all-optional` | Controls `xs:choice` modeling |
package/docs/concepts.md CHANGED
@@ -312,8 +312,7 @@ OpenAPI uses operation docs for both `description` and default `summary` values.
312
312
 
313
313
  ## Response Envelope
314
314
 
315
- All gateway responses follow a uniform envelope structure. This has been
316
- always-on since v0.7.1.
315
+ Buffered legacy gateway responses use this envelope. Stream success bodies deliver records directly, and checked components use their separate value/fault contract.
317
316
 
318
317
  ### Success Response
319
318
 
@@ -367,7 +366,7 @@ details:
367
366
 
368
367
  ### Streaming Bypass
369
368
 
370
- Operations opted into streaming with `--stream-config` bypass the success envelope on the `200` response path. The OpenAPI response content is declared as the configured stream media type, and the gateway writes raw NDJSON lines or one streamed JSON array straight to the response body. Error responses (400, 502, and the rest) still use the normal envelope so clients always see structured failures before the first record. See [ADR-002](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/docs/decisions/002-streamable-responses.md) for the full rationale.
369
+ Legacy stream operations bypass the success envelope. OpenAPI declares the configured media type, and the gateway writes NDJSON lines or one JSON array. Errors handled before response commitment can use the normal error envelope; after commitment, failures truncate output. See [streaming handlers](gateway-guide.md#streaming-handlers).
371
370
 
372
371
  ### Envelope Naming
373
372
 
@@ -497,13 +496,13 @@ The generator produces two response execution models. Buffered is the default an
497
496
 
498
497
  ### Execution Model Contrast
499
498
 
500
- Buffered operations call `node-soap`, wait for the full response to materialize, and return `{ response, headers, responseRaw, requestRaw }`. Streaming operations bypass `node-soap`, POST a hand-built SOAP envelope via `fetch`, and return `StreamOperationResponse<RecordType>` with `records: AsyncIterable<RecordType>`. The SAX parser in `runtime/streamXml.ts` walks the configured `recordPath` and yields each record as its closing tag arrives.
499
+ Buffered operations call `node-soap`, wait for the full response to materialize, and return `{ response, headers, responseRaw, requestRaw }`. Streaming operations use node-soap's WSDL serializer and public HTTP `requestStream` transport, returning `StreamOperationResponse<RecordType>` with `records: AsyncIterable<RecordType>`. The SAX parser walks `recordPath`; each completed record passes through `fromSoapResult` to produce the generated DTO, including flat attributes and typed `$value` content.
501
500
 
502
501
  The catalog is the source of truth for stream metadata. Each opted-in operation carries an `OperationStreamMetadata` entry, and downstream emitters (client, OpenAPI, gateway, tests) all read from the catalog. OpenAPI carries a derived view via the `x-wsdl-tsc-stream` extension.
503
502
 
504
503
  ### Terminal-Error Policy
505
504
 
506
- Errors before the first record use the normal gateway error envelope because the response headers and status have not been committed yet. Errors mid-stream truncate the response; NDJSON consumers detect this as an incomplete HTTP response, and JSON array consumers see an incomplete or invalid JSON document. Both cases must be treated as failed streams. This behavior is documented for operators in the [Production Guide](production.md#terminal-error-policy).
505
+ Failures before response commitment can use the normal gateway error handler. After commitment, failures truncate output; an incomplete HTTP response or JSON document is a failed stream. See the [Production Guide](production.md#terminal-error-policy).
507
506
 
508
507
  ## Companion Catalogs and Shape Resolution
509
508
 
@@ -102,8 +102,7 @@ Per-operation overrides for method, summary, description, and deprecation.
102
102
  ## Stream Configuration
103
103
 
104
104
  The `--stream-config <file>` flag (available on `compile`, `client`, `pipeline`)
105
- opts selected WSDL operations into streaming. Buffered output is unchanged for
106
- operations not listed in the file.
105
+ opts selected WSDL operations into streaming. Unlisted operations retain their buffered API selection; 1.2.0 can still change generated mapper and metadata output.
107
106
 
108
107
  ```json
109
108
  {
@@ -140,7 +139,15 @@ operations not listed in the file.
140
139
  See [ADR-002](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/docs/decisions/002-streamable-responses.md) for rationale and the
141
140
  terminal-error policy.
142
141
 
143
- ## Example Files
142
+ ## Multi-service configuration
143
+
144
+ Use `pipeline --services-config services.json` exclusively, without single-source CLI flags. Each `services[].generation` uses programmatic `PipelineOptions` names, not CLI flag names. The canonical schema/example and typed runtime registry are in [Multi-Service Setup](gateway-guide.md#multi-service-setup).
145
+
146
+ Paths in the services file resolve relative to that file; companion paths inside `streamConfigFile` resolve relative to the stream file. Programmatic `streamConfig` requires normalized metadata from `parseStreamConfig`, whereas raw JSON uses `recordType` and `shapeCatalog`. Do not interchange those shapes.
147
+
148
+ Use unique lowercase service keys, normalized service slugs, decorators and static base paths, with explicit disjoint output ownership. Aggregate output is JSON only; `gateway.openapiPath` selects discovery or disables it with `false`. Bind runtime WSDL sources and security separately for every client. Checked services and aggregate app/test scaffolding are refused.
149
+
150
+ ## Example configuration files
144
151
 
145
152
  Example configuration files are available in the `examples/config/` directory:
146
153
 
package/docs/corpus.md CHANGED
@@ -8,7 +8,7 @@ This table is generated from `test/conformance/readiness-corpus.json`; its sourc
8
8
 
9
9
  | Input | Provenance tier | Mode | Expected result | Evidence stages | Executable evidence |
10
10
  |---|---|---|---|---|---|
11
- | [noaa-ndfd](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/fixtures/wsdl/real-services/noaa-ndfd/ndfd-xml.wsdl) | real | checked | refused | original source decoding and 12-operation inventory, locationless-import planning refusal, zero SOAP dispatch | [noaa-ndfd-qualification.test.ts](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/integration/noaa-ndfd-qualification.test.ts) |
11
+ | [noaa-ndfd](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/fixtures/wsdl/real-services/noaa-ndfd/ndfd-xml.wsdl) | real | checked | partial | original source decoding and 12-operation inventory, all 12 operations native planning and generation, installed build and every generated HTTP route, owned typed SOAP 1.1 RPC with native restrictions and 73-field Boolean record, exact Latin1 responses and offline provider replay; zero provider calls in tests | [noaa-ndfd-qualification.test.ts](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/integration/noaa-ndfd-qualification.test.ts), [checked-gateway-installed.test.ts](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/integration/checked-gateway-installed.test.ts) |
12
12
  | [weather](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/examples/minimal/weather.wsdl) | synthetic | legacy | supported | generate, typecheck, OpenAPI, gateway mock, installed package | [gateway-routes.test.ts](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/integration/gateway-routes.test.ts); `npm run package:smoke` |
13
13
  | [checked-forecast](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/conformance/fixtures/assessment/forecast.wsdl) | synthetic | checked | supported | generate, install, typecheck, HTTP/SOAP success and faults | [checked-gateway-installed.test.ts](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/integration/checked-gateway-installed.test.ts) |
14
14
  | [application-groups](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/conformance/fixtures/assessment/application-groups.wsdl) | synthetic | checked | supported | generate, native SOAP, OpenAPI, installed HTTP/SOAP variant | [checked-application-groups.test.ts](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/integration/checked-application-groups.test.ts), [checked-gateway-installed.test.ts](https://github.com/TechSpokes/typescript-wsdl-client/blob/main/test/integration/checked-gateway-installed.test.ts) |
@@ -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: