@techspokes/typescript-wsdl-client 0.16.1 → 0.17.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 (71) hide show
  1. package/README.md +5 -0
  2. package/dist/app/generateApp.d.ts.map +1 -1
  3. package/dist/app/generateApp.js +1 -0
  4. package/dist/cli.js +46 -2
  5. package/dist/client/generateClient.d.ts.map +1 -1
  6. package/dist/client/generateClient.js +62 -6
  7. package/dist/client/generateOperations.d.ts.map +1 -1
  8. package/dist/client/generateOperations.js +27 -4
  9. package/dist/compiler/schemaCompiler.d.ts +40 -11
  10. package/dist/compiler/schemaCompiler.d.ts.map +1 -1
  11. package/dist/compiler/schemaCompiler.js +81 -6
  12. package/dist/compiler/shapeResolver.d.ts +18 -0
  13. package/dist/compiler/shapeResolver.d.ts.map +1 -0
  14. package/dist/compiler/shapeResolver.js +280 -0
  15. package/dist/gateway/generateGateway.d.ts.map +1 -1
  16. package/dist/gateway/generateGateway.js +2 -1
  17. package/dist/gateway/generators.d.ts +13 -1
  18. package/dist/gateway/generators.d.ts.map +1 -1
  19. package/dist/gateway/generators.js +98 -13
  20. package/dist/gateway/helpers.d.ts +16 -0
  21. package/dist/gateway/helpers.d.ts.map +1 -1
  22. package/dist/gateway/helpers.js +1 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +16 -1
  26. package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
  27. package/dist/openapi/generateOpenAPI.js +28 -0
  28. package/dist/pipeline.d.ts +13 -0
  29. package/dist/pipeline.d.ts.map +1 -1
  30. package/dist/pipeline.js +17 -1
  31. package/dist/runtime/ndjson.d.ts +24 -0
  32. package/dist/runtime/ndjson.d.ts.map +1 -0
  33. package/dist/runtime/ndjson.js +30 -0
  34. package/dist/runtime/streamXml.d.ts +45 -0
  35. package/dist/runtime/streamXml.d.ts.map +1 -0
  36. package/dist/runtime/streamXml.js +212 -0
  37. package/dist/test/generators.d.ts.map +1 -1
  38. package/dist/test/generators.js +50 -0
  39. package/dist/test/mockData.d.ts +6 -0
  40. package/dist/test/mockData.d.ts.map +1 -1
  41. package/dist/test/mockData.js +6 -2
  42. package/dist/util/cli.d.ts.map +1 -1
  43. package/dist/util/cli.js +3 -1
  44. package/dist/util/runtimeSource.d.ts +2 -0
  45. package/dist/util/runtimeSource.d.ts.map +1 -0
  46. package/dist/util/runtimeSource.js +38 -0
  47. package/dist/util/streamConfig.d.ts +59 -0
  48. package/dist/util/streamConfig.d.ts.map +1 -0
  49. package/dist/util/streamConfig.js +230 -0
  50. package/docs/README.md +1 -0
  51. package/docs/api-reference.md +146 -0
  52. package/docs/architecture.md +27 -5
  53. package/docs/cli-reference.md +30 -0
  54. package/docs/concepts.md +51 -0
  55. package/docs/configuration.md +40 -0
  56. package/docs/decisions/002-streamable-responses.md +308 -0
  57. package/docs/gateway-guide.md +37 -0
  58. package/docs/generated-code.md +21 -0
  59. package/docs/migration-playbook.md +33 -0
  60. package/docs/migration.md +31 -6
  61. package/docs/output-anatomy.md +49 -0
  62. package/docs/production.md +32 -0
  63. package/docs/start-here.md +33 -0
  64. package/docs/supported-patterns.md +2 -0
  65. package/docs/testing.md +14 -0
  66. package/docs/troubleshooting.md +18 -0
  67. package/package.json +5 -2
  68. package/src/runtime/clientStreamMethods.tpl.txt +183 -0
  69. package/src/runtime/ndjson.ts +32 -0
  70. package/src/runtime/operationsStreamHelper.tpl.txt +13 -0
  71. package/src/runtime/streamXml.ts +293 -0
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Companion-catalog shape resolver (phase-2 of ADR-002).
3
+ *
4
+ * For stream operations whose record types live in a different WSDL (e.g.
5
+ * Escapia's main EVRN service provides the concrete UnitDescriptiveContentType
6
+ * while the content-service WSDL only exposes an xs:any-wrapped envelope),
7
+ * load the companion catalog and copy the reachable record-type graph into
8
+ * the current catalog.
9
+ *
10
+ * Invariants:
11
+ * - A name collision against a type already in the current catalog is only
12
+ * allowed when the two types are *structurally identical* — otherwise we
13
+ * fail the build rather than silently rename public API types.
14
+ * - Existing buffered generation stays byte-for-byte identical when no
15
+ * streamConfig.shapeCatalogs entries are declared.
16
+ */
17
+ import fs from "node:fs";
18
+ import path from "node:path";
19
+ import { compileCatalog } from "./schemaCompiler.js";
20
+ import { loadWsdl } from "../loader/wsdlLoader.js";
21
+ import { resolveCompilerOptions } from "../config.js";
22
+ import { WsdlCompilationError } from "../util/errors.js";
23
+ /**
24
+ * Apply a parsed StreamConfig to a compiled catalog:
25
+ * 1. Verify every opted-in operation's record type is resolvable.
26
+ * 2. For each operation that names a shapeCatalog, load the companion
27
+ * catalog (once, cached) and copy the reachable record-type graph.
28
+ *
29
+ * Mutates `compiled` in place. Safe to call with a StreamConfig that has
30
+ * zero shapeCatalogs — it will still validate record-type presence against
31
+ * the current catalog.
32
+ */
33
+ export async function applyShapeCatalogs(compiled, streamConfig, options = {}) {
34
+ const baseDir = options.baseDir ?? process.cwd();
35
+ // Load each declared shape catalog once, on-demand. Shape catalogs declared
36
+ // but never referenced are harmless — we only load the ones an operation
37
+ // actually uses.
38
+ const referencedCatalogs = new Set();
39
+ for (const meta of Object.values(streamConfig.operations)) {
40
+ if (meta.shapeCatalogName)
41
+ referencedCatalogs.add(meta.shapeCatalogName);
42
+ }
43
+ const companionByName = new Map();
44
+ for (const name of referencedCatalogs) {
45
+ const ref = streamConfig.shapeCatalogs[name];
46
+ if (!ref) {
47
+ throw new WsdlCompilationError(`Stream config references shape catalog "${name}" that is not declared under "shapeCatalogs".`, {
48
+ suggestion: `Add a "shapeCatalogs.${name}" entry pointing to a wsdlSource or catalogFile, or remove the reference.`,
49
+ });
50
+ }
51
+ companionByName.set(name, await loadCompanionCatalog(name, ref, baseDir, compiled));
52
+ }
53
+ for (const [opName, meta] of Object.entries(streamConfig.operations)) {
54
+ if (meta.shapeCatalogName) {
55
+ const companion = companionByName.get(meta.shapeCatalogName);
56
+ if (!companion) {
57
+ // Guarded by the loop above; this path is defensive.
58
+ throw new WsdlCompilationError(`Stream config for "${opName}" references shape catalog "${meta.shapeCatalogName}" which failed to load.`);
59
+ }
60
+ copyReachableGraph(compiled, companion, meta.recordTypeName, {
61
+ opName,
62
+ shapeCatalog: meta.shapeCatalogName,
63
+ });
64
+ }
65
+ else {
66
+ // No companion: the record type must already live in the main catalog.
67
+ if (!hasNamedType(compiled, meta.recordTypeName)) {
68
+ throw new WsdlCompilationError(`Stream config for operation "${opName}" references record type "${meta.recordTypeName}" which is not present in the compiled catalog.`, {
69
+ element: meta.recordTypeName,
70
+ suggestion: `Either declare a "shapeCatalogs.<name>" entry and set shapeCatalog on the operation, or correct the recordType to a type defined in the WSDL.`,
71
+ });
72
+ }
73
+ }
74
+ }
75
+ }
76
+ async function loadCompanionCatalog(name, ref, baseDir, compiled) {
77
+ if (ref.catalogFile) {
78
+ const abs = path.resolve(baseDir, ref.catalogFile);
79
+ let text;
80
+ try {
81
+ text = fs.readFileSync(abs, "utf-8");
82
+ }
83
+ catch (err) {
84
+ throw new WsdlCompilationError(`Failed to read companion catalog "${name}" at ${abs}: ${err.message}`, { suggestion: `Check that shapeCatalogs.${name}.catalogFile points to an existing catalog.json.` });
85
+ }
86
+ try {
87
+ return JSON.parse(text);
88
+ }
89
+ catch (err) {
90
+ throw new WsdlCompilationError(`Companion catalog "${name}" at ${abs} is not valid JSON: ${err.message}`);
91
+ }
92
+ }
93
+ if (ref.wsdlSource) {
94
+ // Relative paths resolve against baseDir; URLs pass through untouched.
95
+ const src = isLikelyUrl(ref.wsdlSource) ? ref.wsdlSource : path.resolve(baseDir, ref.wsdlSource);
96
+ const wsdlCatalog = await loadWsdl(src);
97
+ // Reuse the current catalog's compiler options so primitives / attrs keys
98
+ // / choice handling stay consistent with the main generation.
99
+ const companionOptions = resolveCompilerOptions({ ...compiled.options, catalog: false }, { wsdl: src, out: baseDir });
100
+ return compileCatalog(wsdlCatalog, companionOptions);
101
+ }
102
+ throw new WsdlCompilationError(`Shape catalog "${name}" declares neither wsdlSource nor catalogFile.`);
103
+ }
104
+ function isLikelyUrl(s) {
105
+ return /^[a-z][a-z0-9+.-]*:\/\//i.test(s);
106
+ }
107
+ function hasNamedType(cat, name) {
108
+ return cat.types.some((t) => t.name === name) || cat.aliases.some((a) => a.name === name);
109
+ }
110
+ /**
111
+ * Walk the reachable type graph from `rootTypeName` within the companion
112
+ * catalog and copy each reachable entry into `dst`. Types with the same name
113
+ * already present in `dst` are checked for structural equality; any divergence
114
+ * is fatal.
115
+ */
116
+ function copyReachableGraph(dst, src, rootTypeName, context) {
117
+ const srcTypes = new Map(src.types.map((t) => [t.name, t]));
118
+ const srcAliases = new Map(src.aliases.map((a) => [a.name, a]));
119
+ if (!srcTypes.has(rootTypeName) && !srcAliases.has(rootTypeName)) {
120
+ throw new WsdlCompilationError(`Stream config for operation "${context.opName}" references record type "${rootTypeName}" but it is not present in shape catalog "${context.shapeCatalog}".`, {
121
+ element: rootTypeName,
122
+ suggestion: `Check the companion catalog or correct the recordType.`,
123
+ });
124
+ }
125
+ const known = new Set([...srcTypes.keys(), ...srcAliases.keys()]);
126
+ const queue = [rootTypeName];
127
+ const visited = new Set();
128
+ while (queue.length > 0) {
129
+ const typeName = queue.shift();
130
+ if (visited.has(typeName))
131
+ continue;
132
+ visited.add(typeName);
133
+ const srcType = srcTypes.get(typeName);
134
+ if (srcType) {
135
+ mergeType(dst, src, srcType, context);
136
+ // Enqueue referenced names from elems, attrs, base.
137
+ for (const e of srcType.elems ?? []) {
138
+ for (const ref of extractReferencedNames(e.tsType, known))
139
+ queue.push(ref);
140
+ }
141
+ for (const a of srcType.attrs ?? []) {
142
+ for (const ref of extractReferencedNames(a.tsType, known))
143
+ queue.push(ref);
144
+ }
145
+ if (srcType.base && known.has(srcType.base))
146
+ queue.push(srcType.base);
147
+ continue;
148
+ }
149
+ const srcAlias = srcAliases.get(typeName);
150
+ if (srcAlias) {
151
+ mergeAlias(dst, src, srcAlias, context);
152
+ for (const ref of extractReferencedNames(srcAlias.tsType, known))
153
+ queue.push(ref);
154
+ }
155
+ // Not in companion; silently skip — either a primitive or a type that
156
+ // the main catalog is expected to own.
157
+ }
158
+ }
159
+ function mergeType(dst, src, srcType, context) {
160
+ const existingIdx = dst.types.findIndex((t) => t.name === srcType.name);
161
+ if (existingIdx < 0) {
162
+ dst.types.push({ ...srcType });
163
+ copyTypeMeta(dst, src, srcType.name);
164
+ return;
165
+ }
166
+ const existing = dst.types[existingIdx];
167
+ if (!structurallyEqualType(existing, srcType)) {
168
+ throw new WsdlCompilationError(`Companion catalog "${context.shapeCatalog}" declares type "${srcType.name}" that conflicts structurally with the current catalog.`, {
169
+ element: srcType.name,
170
+ suggestion: `Rename one of the conflicting types in the source WSDL, or resolve the conflict before streaming. wsdl-tsc refuses to silently rename public API types.`,
171
+ });
172
+ }
173
+ // Structurally identical — keep the existing entry (and its meta) as-is.
174
+ }
175
+ function mergeAlias(dst, src, srcAlias, context) {
176
+ const existingIdx = dst.aliases.findIndex((a) => a.name === srcAlias.name);
177
+ if (existingIdx < 0) {
178
+ dst.aliases.push({ ...srcAlias });
179
+ // Aliases that point at a complex type get meta synonyms in compileCatalog;
180
+ // the main catalog already owns that path for its own types, so we only
181
+ // need to add meta if the companion aliases a complex type whose type we
182
+ // also just copied.
183
+ copyAliasSynonymMeta(dst, src, srcAlias.name);
184
+ return;
185
+ }
186
+ const existing = dst.aliases[existingIdx];
187
+ if (!structurallyEqualAlias(existing, srcAlias)) {
188
+ throw new WsdlCompilationError(`Companion catalog "${context.shapeCatalog}" declares alias "${srcAlias.name}" that conflicts structurally with the current catalog.`, {
189
+ element: srcAlias.name,
190
+ suggestion: `Resolve the conflicting type name before streaming. wsdl-tsc refuses to silently rename public API types.`,
191
+ });
192
+ }
193
+ }
194
+ function copyTypeMeta(dst, src, typeName) {
195
+ if (typeName in src.meta.attrSpec)
196
+ dst.meta.attrSpec[typeName] = [...src.meta.attrSpec[typeName]];
197
+ if (typeName in src.meta.attrType)
198
+ dst.meta.attrType[typeName] = { ...src.meta.attrType[typeName] };
199
+ if (typeName in src.meta.childType)
200
+ dst.meta.childType[typeName] = { ...src.meta.childType[typeName] };
201
+ if (typeName in src.meta.propMeta)
202
+ dst.meta.propMeta[typeName] = { ...src.meta.propMeta[typeName] };
203
+ }
204
+ function copyAliasSynonymMeta(dst, src, aliasName) {
205
+ // Phase-2 MVP: only copy meta for aliases whose name already keys into the
206
+ // source's meta maps. Aliases that are just simple-type renames don't need
207
+ // child/attr meta at all.
208
+ copyTypeMeta(dst, src, aliasName);
209
+ }
210
+ const TS_BUILTINS = new Set([
211
+ "string", "number", "boolean", "bigint", "null", "undefined", "void", "any", "unknown", "never",
212
+ "Date", "Array", "Record", "Map", "Set", "Promise", "Buffer", "Object", "Function",
213
+ ]);
214
+ /**
215
+ * Extract PascalCase identifiers from a TypeScript type expression that are
216
+ * known to the companion catalog. Deliberately cheap — matches identifiers
217
+ * starting with an uppercase letter, skips TS built-ins and anything not
218
+ * present in the companion's known-names set.
219
+ */
220
+ function extractReferencedNames(tsType, known) {
221
+ if (!tsType)
222
+ return [];
223
+ const out = [];
224
+ // Identifiers starting with an uppercase letter; bare word boundaries.
225
+ for (const m of tsType.matchAll(/\b[A-Z][A-Za-z0-9_]*\b/g)) {
226
+ const name = m[0];
227
+ if (TS_BUILTINS.has(name))
228
+ continue;
229
+ if (known.has(name))
230
+ out.push(name);
231
+ }
232
+ return out;
233
+ }
234
+ function structurallyEqualType(a, b) {
235
+ return canonicalizeType(a) === canonicalizeType(b);
236
+ }
237
+ function structurallyEqualAlias(a, b) {
238
+ return canonicalizeAlias(a) === canonicalizeAlias(b);
239
+ }
240
+ function canonicalizeType(t) {
241
+ // Drop fields that legitimately differ between catalogs (ns, docs) and
242
+ // stabilize ordering. Stream metadata is on operations, not on types.
243
+ return JSON.stringify({
244
+ name: t.name,
245
+ attrs: (t.attrs ?? []).map(canonicalizeAttr),
246
+ elems: (t.elems ?? []).map(canonicalizeElem),
247
+ base: t.base ?? null,
248
+ wildcards: (t.wildcards ?? []).map((w) => ({
249
+ min: w.min,
250
+ max: w.max,
251
+ namespace: w.namespace ?? null,
252
+ processContents: w.processContents ?? null,
253
+ })),
254
+ });
255
+ }
256
+ function canonicalizeAlias(a) {
257
+ return JSON.stringify({
258
+ name: a.name,
259
+ tsType: a.tsType,
260
+ declared: a.declared,
261
+ });
262
+ }
263
+ function canonicalizeAttr(a) {
264
+ return {
265
+ name: a.name,
266
+ tsType: a.tsType,
267
+ use: a.use ?? "optional",
268
+ declaredType: a.declaredType,
269
+ };
270
+ }
271
+ function canonicalizeElem(e) {
272
+ return {
273
+ name: e.name,
274
+ tsType: e.tsType,
275
+ min: e.min,
276
+ max: e.max,
277
+ nillable: !!e.nillable,
278
+ declaredType: e.declaredType,
279
+ };
280
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAE/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6JjF"}
1
+ {"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAE/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8JjF"}
@@ -183,7 +183,8 @@ export async function generateGateway(opts) {
183
183
  }
184
184
  // Step 6: Emit runtime.ts (if enabled)
185
185
  if (emitRuntime) {
186
- emitRuntimeModule(outDir, versionSlug, serviceSlug, shouldUnwrap ? catalog : undefined);
186
+ const hasStreams = operations.some((o) => !!o.stream);
187
+ emitRuntimeModule(outDir, versionSlug, serviceSlug, shouldUnwrap ? catalog : undefined, { hasStreams });
187
188
  }
188
189
  // Step 7: Emit plugin.ts and type-check fixture (if enabled)
189
190
  if (emitPlugin) {
@@ -40,6 +40,16 @@ export interface OperationMetadata {
40
40
  description?: string;
41
41
  /** When true, response schema is omitted from route registration to avoid fast-json-stringify stack overflow on deeply nested $ref graphs */
42
42
  skipResponseSchema?: boolean;
43
+ /**
44
+ * Stream metadata populated from the OpenAPI `x-wsdl-tsc-stream` extension.
45
+ * When present, the route handler pipes `result.records` through the NDJSON
46
+ * helper instead of envelope-wrapping a single response object.
47
+ */
48
+ stream?: {
49
+ mediaType: string;
50
+ format: "ndjson" | "json-array";
51
+ recordTypeName?: string;
52
+ };
43
53
  }
44
54
  /**
45
55
  * Emits Fastify-compatible operation schema files
@@ -107,7 +117,9 @@ export declare function emitSchemasModule(outDir: string, modelsDir: string, ver
107
117
  * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode for generated TypeScript modules
108
118
  */
109
119
  export declare function emitRouteFiles(outDir: string, routesDir: string, versionSlug: string, serviceSlug: string, operations: OperationMetadata[], importsMode: "js" | "ts" | "bare"): void;
110
- export declare function emitRuntimeModule(outDir: string, versionSlug: string, serviceSlug: string, catalog?: any): void;
120
+ export declare function emitRuntimeModule(outDir: string, versionSlug: string, serviceSlug: string, catalog?: any, opts?: {
121
+ hasStreams?: boolean;
122
+ }): void;
111
123
  /**
112
124
  * Emits plugin.ts module as the primary Fastify plugin wrapper
113
125
  *
@@ -1 +1 @@
1
- {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"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"}
@@ -148,11 +148,19 @@ export function emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, sche
148
148
  // Build response schemas
149
149
  const responses = operation.responses || {};
150
150
  const responseObj = {};
151
+ // Detect streaming operation: OpenAPI emits a non-JSON content entry with
152
+ // an `x-wsdl-tsc-stream` extension on the 200 response.
153
+ const streamEntry = detectStreamResponse(responses["200"]);
151
154
  const explicitCodes = Object.keys(responses).filter(isNumeric);
152
155
  for (const code of explicitCodes) {
153
156
  const r = responses[code];
154
157
  if (!r || typeof r !== "object")
155
158
  continue;
159
+ // Stream ops intentionally skip JSON response-schema construction on the
160
+ // streamed status code; the body is an NDJSON stream, not a single
161
+ // JSON object, so fast-json-stringify cannot serialize it anyway.
162
+ if (streamEntry && code === "200")
163
+ continue;
156
164
  const rSchema = r.content?.["application/json"]?.schema;
157
165
  if (!rSchema) {
158
166
  throw new Error(`Response ${code} for operation '${operationId}' is missing application/json schema`);
@@ -222,12 +230,38 @@ export function emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, sche
222
230
  path: p,
223
231
  summary: normalizeDocCommentText(typeof operation.summary === "string" ? operation.summary : undefined),
224
232
  description: normalizeDocCommentText(typeof operation.description === "string" ? operation.description : undefined),
225
- skipResponseSchema,
233
+ // Stream ops always skip response schema because NDJSON is not a single
234
+ // JSON document and fast-json-stringify can't serialize it.
235
+ skipResponseSchema: skipResponseSchema || !!streamEntry,
236
+ ...(streamEntry ? { stream: streamEntry } : {}),
226
237
  });
227
238
  }
228
239
  }
229
240
  return operations;
230
241
  }
242
+ /**
243
+ * Look for an `x-wsdl-tsc-stream` extension on the 200 response's content map.
244
+ * Returns the normalized stream metadata when found, or undefined otherwise.
245
+ */
246
+ function detectStreamResponse(response200) {
247
+ const content = response200?.content;
248
+ if (!content || typeof content !== "object")
249
+ return undefined;
250
+ for (const [mediaType, entry] of Object.entries(content)) {
251
+ const ext = entry && typeof entry === "object" ? entry["x-wsdl-tsc-stream"] : undefined;
252
+ if (!ext || typeof ext !== "object")
253
+ continue;
254
+ const format = ext.format;
255
+ if (format !== "ndjson" && format !== "json-array")
256
+ continue;
257
+ const itemRef = ext.itemSchema?.$ref;
258
+ const recordTypeName = typeof itemRef === "string"
259
+ ? itemRef.split("/").pop()
260
+ : undefined;
261
+ return { mediaType, format, recordTypeName };
262
+ }
263
+ return undefined;
264
+ }
231
265
  /**
232
266
  * Emits schemas.ts module that registers all model schemas with Fastify
233
267
  *
@@ -361,7 +395,7 @@ import type { FastifyInstance } from "fastify";
361
395
  function detectArrayWrappers(catalog) {
362
396
  return detectArrayWrappersShared(catalog.types || []);
363
397
  }
364
- export function emitRuntimeModule(outDir, versionSlug, serviceSlug, catalog) {
398
+ export function emitRuntimeModule(outDir, versionSlug, serviceSlug, catalog, opts) {
365
399
  const vSlug = slugName(versionSlug);
366
400
  const sSlug = slugName(serviceSlug);
367
401
  // Build unwrap maps from catalog if provided
@@ -602,7 +636,43 @@ export function createGatewayErrorHandler_${vSlug}_${sSlug}() {
602
636
  };
603
637
  }
604
638
  `;
605
- fs.writeFileSync(path.join(outDir, "runtime.ts"), runtimeTs + unwrapSection, "utf8");
639
+ const streamSection = opts?.hasStreams ? buildStreamRuntimeSection() : "";
640
+ fs.writeFileSync(path.join(outDir, "runtime.ts"), runtimeTs + unwrapSection + streamSection, "utf8");
641
+ }
642
+ /**
643
+ * Returns the streaming helpers block for runtime.ts.
644
+ *
645
+ * The emitted `toNdjson` mirrors the reference implementation in
646
+ * src/runtime/ndjson.ts but is inlined here to avoid a cross-package import
647
+ * from the generated gateway (which would require wsdl-tsc to be a runtime
648
+ * dependency of the consumer's project).
649
+ */
650
+ function buildStreamRuntimeSection() {
651
+ return `
652
+ // -----------------------------------------------------------------------------
653
+ // Streaming helpers (emitted because at least one operation is stream-configured).
654
+ // -----------------------------------------------------------------------------
655
+
656
+ import { Readable } from "node:stream";
657
+
658
+ /**
659
+ * Wrap an async iterable of records in a Node Readable stream that emits
660
+ * NDJSON (one JSON document per line, LF-terminated). Downstream backpressure
661
+ * is honored by Readable.from's default iterator-pausing behavior. Source
662
+ * errors are forwarded to the returned stream's 'error' event — before-first-
663
+ * byte errors fire before any push(), so Fastify translates them into the
664
+ * standard JSON error envelope.
665
+ */
666
+ export function toNdjson<T>(records: AsyncIterable<T>): Readable {
667
+ return Readable.from(encodeNdjson(records), { objectMode: false, encoding: "utf-8" });
668
+ }
669
+
670
+ async function* encodeNdjson<T>(records: AsyncIterable<T>): AsyncIterable<string> {
671
+ for await (const record of records) {
672
+ yield JSON.stringify(record) + "\\n";
673
+ }
674
+ }
675
+ `;
606
676
  }
607
677
  /**
608
678
  * Emits plugin.ts module as the primary Fastify plugin wrapper
@@ -799,25 +869,44 @@ export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, servi
799
869
  ? `request.body as ${reqTypeName}`
800
870
  : "request.body";
801
871
  // Build the runtime import and return expression based on unwrap availability
802
- const runtimeImport = hasUnwrap
803
- ? `import { buildSuccessEnvelope, unwrapArrayWrappers } from "../runtime${suffix}";`
804
- : `import { buildSuccessEnvelope } from "../runtime${suffix}";`;
872
+ const runtimeImport = op.stream
873
+ ? `import { toNdjson } from "../runtime${suffix}";`
874
+ : hasUnwrap
875
+ ? `import { buildSuccessEnvelope, unwrapArrayWrappers } from "../runtime${suffix}";`
876
+ : `import { buildSuccessEnvelope } from "../runtime${suffix}";`;
805
877
  const returnExpr = hasUnwrap
806
878
  ? `return buildSuccessEnvelope(unwrapArrayWrappers(result.response, "${resTypeName}"));`
807
879
  : `return buildSuccessEnvelope(result.response);`;
808
880
  // Note: op.path comes from OpenAPI and already includes any base path
809
881
  const schemaBinding = op.skipResponseSchema
810
- ? `\n// Response schema omitted: $ref graph exceeds fast-json-stringify depth limit\nconst { response: _response, ...routeSchema } = schema as Record<string, unknown>;\n`
882
+ ? `\n// Response schema omitted: ${op.stream
883
+ ? "stream operations emit NDJSON, not a single JSON object"
884
+ : "$ref graph exceeds fast-json-stringify depth limit"}\nconst { response: _response, ...routeSchema } = schema as Record<string, unknown>;\n`
811
885
  : "";
812
886
  const schemaLine = op.skipResponseSchema
813
887
  ? " schema: routeSchema,"
814
888
  : " schema,";
815
889
  const operationDocLines = buildOperationDocLines(op);
890
+ const handlerBody = op.stream
891
+ ? ` handler: async (request, reply) => {
892
+ const client = fastify.${clientMeta.decoratorName};
893
+ const result = await client.${clientMethod}(${bodyArg});
894
+ reply.type(${JSON.stringify(op.stream.mediaType)});
895
+ return reply.send(toNdjson(result.records));
896
+ },`
897
+ : ` handler: async (request) => {
898
+ const client = fastify.${clientMeta.decoratorName};
899
+ const result = await client.${clientMethod}(${bodyArg});
900
+ ${returnExpr}
901
+ },`;
902
+ const responseTypeDoc = op.stream
903
+ ? `${op.stream.recordTypeName ?? "records"} (streamed as ${op.stream.format})`
904
+ : `${resTypeName} (wrapped in envelope)`;
816
905
  let routeTs = `/**
817
906
  * Route: ${op.method.toUpperCase()} ${op.path}
818
907
  * Operation: ${op.operationId || op.operationSlug}
819
908
  ${operationDocLines} * Request Type: ${reqTypeName}
820
- * Response Type: ${resTypeName} (wrapped in envelope)
909
+ * Response Type: ${responseTypeDoc}
821
910
  * Auto-generated - do not edit manually.
822
911
  */
823
912
  import type { FastifyInstance } from "fastify";
@@ -829,11 +918,7 @@ export async function ${fnName}(fastify: FastifyInstance) {
829
918
  method: "${op.method.toUpperCase()}",
830
919
  url: "${op.path}",
831
920
  ${schemaLine}
832
- handler: async (request) => {
833
- const client = fastify.${clientMeta.decoratorName};
834
- const result = await client.${clientMethod}(${bodyArg});
835
- ${returnExpr}
836
- },
921
+ ${handlerBody}
837
922
  });
838
923
  }
839
924
  `;
@@ -176,6 +176,16 @@ export interface ResolvedOperationMeta {
176
176
  responseTypeName?: string;
177
177
  summary?: string;
178
178
  description?: string;
179
+ /**
180
+ * Populated for operations that opt into streaming via stream-config. Drives
181
+ * mock-client emission (async-iterable records) and test-payload shape.
182
+ */
183
+ stream?: {
184
+ format: "ndjson" | "json-array";
185
+ mediaType: string;
186
+ recordTypeName: string;
187
+ recordPath: string[];
188
+ };
179
189
  }
180
190
  /**
181
191
  * Options for resolving client metadata
@@ -226,6 +236,12 @@ export declare function resolveOperationMeta(operationId: string, operationSlug:
226
236
  summary?: string;
227
237
  description?: string;
228
238
  doc?: string;
239
+ stream?: {
240
+ format: "ndjson" | "json-array";
241
+ mediaType: string;
242
+ recordTypeName: string;
243
+ recordPath: string[];
244
+ };
229
245
  }>): ResolvedOperationMeta;
230
246
  /**
231
247
  * Measures the $ref graph complexity of a schema component.
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"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"}
@@ -430,6 +430,7 @@ export function resolveOperationMeta(operationId, operationSlug, method, path, c
430
430
  responseTypeName,
431
431
  ...(summary ? { summary } : {}),
432
432
  ...(description ? { description } : {}),
433
+ ...(catalogOp?.stream ? { stream: catalogOp.stream } : {}),
433
434
  };
434
435
  }
435
436
  function normalizeOperationText(value) {
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
3
3
  export { generateGateway } from "./gateway/generateGateway.js";
4
4
  export { generateTests } from "./test/generateTests.js";
5
5
  export { runGenerationPipeline } from "./pipeline.js";
6
+ export { loadStreamConfigFile, parseStreamConfig, StreamConfigError, type OperationStreamMetadata, type ShapeCatalogRef, type StreamConfig, } from "./util/streamConfig.js";
7
+ export { applyShapeCatalogs, type ApplyShapeCatalogsOptions } from "./compiler/shapeResolver.js";
6
8
  /**
7
9
  * Compiles a WSDL file to TypeScript client code
8
10
  *
@@ -27,5 +29,9 @@ export declare function compileWsdlToProject(input: {
27
29
  wsdl: string;
28
30
  outDir: string;
29
31
  options?: CompilerOptions;
32
+ /** Path to a stream configuration JSON file (ADR-002). Takes precedence over `streamConfig` when both are set. */
33
+ streamConfigFile?: string;
34
+ /** In-memory stream configuration. Ignored when `streamConfigFile` is set. */
35
+ streamConfig?: import("./util/streamConfig.js").StreamConfig;
30
36
  }): Promise<void>;
31
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAWjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GACjE,OAAO,CAAC,IAAI,CAAC,CA4Df"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAWjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAC,kBAAkB,EAAE,KAAK,yBAAyB,EAAC,MAAM,6BAA6B,CAAC;AAG/F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,kHAAkH;IAClH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,OAAO,wBAAwB,EAAE,YAAY,CAAC;CAC9D,GACA,OAAO,CAAC,IAAI,CAAC,CA2Ef"}
package/dist/index.js CHANGED
@@ -26,6 +26,8 @@ export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
26
26
  export { generateGateway } from "./gateway/generateGateway.js";
27
27
  export { generateTests } from "./test/generateTests.js";
28
28
  export { runGenerationPipeline } from "./pipeline.js";
29
+ export { loadStreamConfigFile, parseStreamConfig, StreamConfigError, } from "./util/streamConfig.js";
30
+ export { applyShapeCatalogs } from "./compiler/shapeResolver.js";
29
31
  // noinspection JSUnusedGlobalSymbols
30
32
  /**
31
33
  * Compiles a WSDL file to TypeScript client code
@@ -53,6 +55,11 @@ export async function compileWsdlToProject(input) {
53
55
  wsdl: input.wsdl,
54
56
  out: input.outDir,
55
57
  });
58
+ // Resolve stream configuration.
59
+ const { loadStreamConfigFile } = await import("./util/streamConfig.js");
60
+ const streamConfig = input.streamConfigFile
61
+ ? loadStreamConfigFile(input.streamConfigFile)
62
+ : input.streamConfig;
56
63
  // Load & compile
57
64
  const wsdlCatalog = await loadWsdl(input.wsdl);
58
65
  info(`Loaded WSDL: ${wsdlCatalog.wsdlUri}`);
@@ -60,7 +67,15 @@ export async function compileWsdlToProject(input) {
60
67
  throw new Error(`No schemas found in WSDL: ${input.wsdl}`);
61
68
  }
62
69
  info(`Schemas discovered: ${wsdlCatalog.schemas.length}`);
63
- const compiled = compileCatalog(wsdlCatalog, finalOptions);
70
+ const compiled = compileCatalog(wsdlCatalog, finalOptions, streamConfig);
71
+ // Apply companion-catalog shape resolution when a stream config is present.
72
+ if (streamConfig) {
73
+ const { applyShapeCatalogs } = await import("./compiler/shapeResolver.js");
74
+ const shapeBaseDir = input.streamConfigFile
75
+ ? path.dirname(path.resolve(input.streamConfigFile))
76
+ : path.dirname(path.resolve(input.wsdl));
77
+ await applyShapeCatalogs(compiled, streamConfig, { baseDir: shapeBaseDir });
78
+ }
64
79
  info(`Compiled WSDL: ${wsdlCatalog.wsdlUri}`);
65
80
  // check if we have any data models and operations
66
81
  if (compiled.types.length === 0 && compiled.aliases.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"generateOpenAPI.d.ts","sourceRoot":"","sources":["../../src/openapi/generateOpenAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,EAAiB,KAAK,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAKnF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC;IAC3E,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CA0RD"}
1
+ {"version":3,"file":"generateOpenAPI.d.ts","sourceRoot":"","sources":["../../src/openapi/generateOpenAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,EAAiB,KAAK,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAKnF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC;IAC3E,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CAsTD"}