# Output Anatomy 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). ## Generation Flow The pipeline follows this sequence: WSDL source is parsed and compiled into a catalog, then each emitter reads the catalog and produces its output. ```text WSDL/XSD --> compile --> catalog.json --> emit client/ --> emit openapi.json --> emit gateway/ --> emit app/ ``` Each stage can run independently. The `pipeline` command runs all stages in sequence. ## Client Output Generated into the directory specified by `--client-dir`. | File | Purpose | |------|---------| | types.ts | All TypeScript interfaces derived from WSDL/XSD types | | client.ts | SOAP client class with one typed method per operation | | operations.ts | Pure interface matching client methods, for mocking without importing `soap` | | utils.ts | Runtime helpers including array unwrapping and type metadata | | catalog.json | Compiled WSDL data in JSON format, used by other generation stages | ### types.ts 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. ### client.ts Exports a client class that wraps the `soap` package. Each WSDL operation becomes an async method with typed input and output. The class handles SOAP envelope construction and response parsing internally. ### operations.ts Exports a TypeScript interface with the same method signatures as the client class but no implementation. Use this for dependency injection and testing. Your test code can implement this interface with mock data without importing the client or `soap`. ### utils.ts Contains runtime metadata and helper functions. Includes `unwrapArrayWrappers()` for bridging between SOAP array wrapper objects and flattened OpenAPI array schemas. ### Stream Operations When `--stream-config` opts an operation into streaming (see [ADR-002](decisions/002-streamable-responses.md)), the client emits additional surfaces alongside the buffered ones. Buffered operations in the same client are unaffected. `operations.ts` gains a `StreamOperationResponse` type that stream methods return instead of the buffered `{ response, headers, responseRaw, requestRaw }` shape: ```typescript export type StreamOperationResponse> = { records: AsyncIterable; headers: HeadersType; requestRaw?: string; }; ``` `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. 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. ## OpenAPI Output Generated at the path specified by `--openapi-file`. | File | Purpose | |------|---------| | openapi.json (or .yaml) | Complete OpenAPI 3.1 specification | 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. CLI OpenAPI generation always validates the generated spec using `@apidevtools/swagger-parser`. ### Stream Schema Extension Stream operations do not use the standard success envelope for `200` responses. NDJSON streams declare the configured stream media type (default `application/x-ndjson`) with `schema: { "type": "string" }` and an `x-wsdl-tsc-stream` extension that carries the record schema reference: ```json { "200": { "description": "Successful streamed SOAP operation response", "content": { "application/x-ndjson": { "schema": { "type": "string" }, "x-wsdl-tsc-stream": { "format": "ndjson", "itemSchema": { "$ref": "#/components/schemas/UnitDescriptiveContentType" } } } } } } ``` JSON array streams default to `application/json` and declare the record schema as the array item schema while keeping the same extension: ```json { "200": { "description": "Successful streamed SOAP operation response", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UnitDescriptiveContentType" } }, "x-wsdl-tsc-stream": { "format": "json-array", "itemSchema": { "$ref": "#/components/schemas/UnitDescriptiveContentType" } } } } } } ``` 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. ## Gateway Output Generated into the directory specified by `--gateway-dir`. | File | Purpose | |------|---------| | plugin.ts | Fastify plugin entry point; registers routes and the SOAP client decorator | | routes.ts | Route registration file that imports all individual route handlers | | routes/*.ts | One file per WSDL operation with request validation, SOAP call, and response transform | | schemas.ts | Schema registration file for JSON Schema validation | | schemas/models/*.json | JSON Schema files for each type | | schemas/operations/*.json | JSON Schema files for each operation request/response | | runtime.ts | Runtime helpers for array unwrapping and type bridging | | _typecheck.ts | Compile-time verification that types and schemas stay consistent | ### Route handler structure Each route file in `routes/` follows the same pattern: validate the JSON request body against the operation schema, call the corresponding SOAP operation via the typed client, transform the SOAP response to JSON, and return it with the appropriate response schema. ### Stream Routes 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(records: AsyncIterable): Readable` and `toJsonArray(records: AsyncIterable): Readable` helpers. See the [Gateway Guide](gateway-guide.md#streaming-handlers) for the full handler example and terminal-error policy. ### Generated Test Surface When `--test-dir` is combined with `--stream-config`, the generated happy-path tests for stream operations assert on the configured content type. NDJSON tests parse each line as a separate JSON record, and JSON array tests parse the response body once as an array. Mock clients use async-generator overrides that yield records to drive those tests; see the [Testing Guide](testing.md) for the pattern. ### Plugin registration The generated plugin exports a Fastify plugin function. Register it with your Fastify app and provide a client instance (real or mock) and a route prefix. ```typescript import Fastify from "fastify"; import { myServiceGateway } from "./generated/gateway/plugin.js"; import { createMyServiceClient } from "./generated/client/client.js"; const app = Fastify(); const client = await createMyServiceClient("https://soap-endpoint.example.com"); await app.register(myServiceGateway, { client, prefix: "/v1/my-service" }); ``` ## App Output Generated when `--init-app` is passed. Creates a runnable Fastify application in a sibling `app/` directory. | File | Purpose | |------|---------| | index.ts | Application entry point with server startup | | .env.example | Environment variable template for WSDL URL, port, host | | package.json | Dependencies and npm scripts | | tsconfig.json | TypeScript configuration for the app | 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. ## What to Commit All generated files are deterministic. Committing them to version control is safe and recommended. After WSDL changes, regenerate with the same command and review the diff to see exactly what changed. ## What to Customize - App files (`index.ts`, `.env`): these are your entry points; customize freely after first generation - Gateway plugin, routes, and schemas: do not edit manually; they are overwritten on regeneration - Add custom middleware, auth, logging, and error handling in your app code, not in generated gateway files