@techspokes/typescript-wsdl-client 0.15.2 → 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 (79) hide show
  1. package/README.md +5 -0
  2. package/dist/app/generateApp.d.ts.map +1 -1
  3. package/dist/app/generateApp.js +4 -3
  4. package/dist/cli.js +46 -2
  5. package/dist/client/generateClient.d.ts.map +1 -1
  6. package/dist/client/generateClient.js +64 -8
  7. package/dist/client/generateOperations.d.ts.map +1 -1
  8. package/dist/client/generateOperations.js +29 -6
  9. package/dist/client/generateTypes.d.ts.map +1 -1
  10. package/dist/client/generateTypes.js +13 -0
  11. package/dist/compiler/schemaCompiler.d.ts +44 -11
  12. package/dist/compiler/schemaCompiler.d.ts.map +1 -1
  13. package/dist/compiler/schemaCompiler.js +102 -6
  14. package/dist/compiler/shapeResolver.d.ts +18 -0
  15. package/dist/compiler/shapeResolver.d.ts.map +1 -0
  16. package/dist/compiler/shapeResolver.js +280 -0
  17. package/dist/gateway/generateGateway.d.ts.map +1 -1
  18. package/dist/gateway/generateGateway.js +2 -1
  19. package/dist/gateway/generators.d.ts +13 -1
  20. package/dist/gateway/generators.d.ts.map +1 -1
  21. package/dist/gateway/generators.js +98 -13
  22. package/dist/gateway/helpers.d.ts +16 -0
  23. package/dist/gateway/helpers.d.ts.map +1 -1
  24. package/dist/gateway/helpers.js +1 -0
  25. package/dist/index.d.ts +6 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +23 -4
  28. package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
  29. package/dist/openapi/generateOpenAPI.js +30 -2
  30. package/dist/openapi/generatePaths.d.ts.map +1 -1
  31. package/dist/openapi/generatePaths.js +4 -2
  32. package/dist/openapi/generateSchemas.d.ts.map +1 -1
  33. package/dist/openapi/generateSchemas.js +20 -5
  34. package/dist/pipeline.d.ts +13 -0
  35. package/dist/pipeline.d.ts.map +1 -1
  36. package/dist/pipeline.js +17 -1
  37. package/dist/runtime/ndjson.d.ts +24 -0
  38. package/dist/runtime/ndjson.d.ts.map +1 -0
  39. package/dist/runtime/ndjson.js +30 -0
  40. package/dist/runtime/streamXml.d.ts +45 -0
  41. package/dist/runtime/streamXml.d.ts.map +1 -0
  42. package/dist/runtime/streamXml.js +212 -0
  43. package/dist/test/generators.d.ts +2 -2
  44. package/dist/test/generators.d.ts.map +1 -1
  45. package/dist/test/generators.js +79 -26
  46. package/dist/test/mockData.d.ts +12 -2
  47. package/dist/test/mockData.d.ts.map +1 -1
  48. package/dist/test/mockData.js +17 -8
  49. package/dist/util/cli.d.ts +3 -0
  50. package/dist/util/cli.d.ts.map +1 -1
  51. package/dist/util/cli.js +6 -1
  52. package/dist/util/runtimeSource.d.ts +2 -0
  53. package/dist/util/runtimeSource.d.ts.map +1 -0
  54. package/dist/util/runtimeSource.js +38 -0
  55. package/dist/util/streamConfig.d.ts +59 -0
  56. package/dist/util/streamConfig.d.ts.map +1 -0
  57. package/dist/util/streamConfig.js +230 -0
  58. package/docs/README.md +1 -0
  59. package/docs/api-reference.md +146 -0
  60. package/docs/architecture.md +27 -5
  61. package/docs/cli-reference.md +30 -0
  62. package/docs/concepts.md +150 -11
  63. package/docs/configuration.md +40 -0
  64. package/docs/decisions/002-streamable-responses.md +308 -0
  65. package/docs/gateway-guide.md +37 -0
  66. package/docs/generated-code.md +21 -0
  67. package/docs/migration-playbook.md +33 -0
  68. package/docs/migration.md +31 -6
  69. package/docs/output-anatomy.md +49 -0
  70. package/docs/production.md +32 -0
  71. package/docs/start-here.md +33 -0
  72. package/docs/supported-patterns.md +29 -0
  73. package/docs/testing.md +14 -0
  74. package/docs/troubleshooting.md +18 -0
  75. package/package.json +9 -6
  76. package/src/runtime/clientStreamMethods.tpl.txt +183 -0
  77. package/src/runtime/ndjson.ts +32 -0
  78. package/src/runtime/operationsStreamHelper.tpl.txt +13 -0
  79. package/src/runtime/streamXml.ts +293 -0
@@ -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,CAwDf"}
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,14 +67,26 @@ 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
- // check if we have any types and operations
66
- if (compiled.types.length === 0) {
67
- throw new Error(`No types compiled from WSDL: ${input.wsdl}`);
80
+ // check if we have any data models and operations
81
+ if (compiled.types.length === 0 && compiled.aliases.length === 0) {
82
+ throw new Error(`No types or aliases compiled from WSDL: ${input.wsdl}`);
68
83
  }
69
84
  else {
70
85
  info(`Types discovered: ${compiled.types.length}`);
86
+ info(`Aliases discovered: ${compiled.aliases.length}`);
87
+ for (const note of compiled.diagnostics?.notes ?? []) {
88
+ info(`Note: ${note}`);
89
+ }
71
90
  }
72
91
  if (compiled.operations.length === 0) {
73
92
  throw new Error(`No operations compiled from WSDL: ${input.wsdl}`);
@@ -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"}
@@ -149,7 +149,7 @@ export async function generateOpenAPI(opts) {
149
149
  };
150
150
  const extensionSchemas = {};
151
151
  for (const op of compiled.operations) {
152
- const payloadType = op.outputElement?.local;
152
+ const payloadType = op.outputTypeName ?? op.outputElement?.local;
153
153
  const payloadRef = payloadType && schemas[payloadType] ? { $ref: `#/components/schemas/${payloadType}` } : { type: "object" };
154
154
  const baseForExt = payloadType || op.name;
155
155
  const extName = joinWithNamespace(baseForExt, envelopeNamespace);
@@ -183,7 +183,35 @@ export async function generateOpenAPI(opts) {
183
183
  const op = compiled.operations.find(o => o.name === opId);
184
184
  if (!op)
185
185
  continue;
186
- const payloadType = op.outputElement?.local;
186
+ if (op.stream) {
187
+ // Stream operations bypass the standard JSON envelope for 200. Emit
188
+ // the configured media type with an itemSchema carried under the
189
+ // x-wsdl-tsc-stream extension so generated tooling can find the
190
+ // record schema without pretending NDJSON is a single JSON document.
191
+ // Errors raised before the first byte still use the standard envelope.
192
+ const recordType = op.stream.recordTypeName;
193
+ const itemRef = { $ref: `#/components/schemas/${recordType}` };
194
+ if (methodObj.responses?.["200"]) {
195
+ methodObj.responses["200"] = {
196
+ description: "Successful streamed SOAP operation response",
197
+ content: {
198
+ [op.stream.mediaType]: {
199
+ schema: { type: "string" },
200
+ "x-wsdl-tsc-stream": {
201
+ format: op.stream.format,
202
+ itemSchema: itemRef,
203
+ },
204
+ },
205
+ },
206
+ };
207
+ }
208
+ if (methodObj.responses?.default) {
209
+ methodObj.responses.default.description = "Error raised before any stream record was emitted (standard envelope).";
210
+ methodObj.responses.default.content = { "application/json": { schema: { $ref: `#/components/schemas/${baseEnvelopeName}` } } };
211
+ }
212
+ continue;
213
+ }
214
+ const payloadType = op.outputTypeName ?? op.outputElement?.local;
187
215
  const baseForExt = payloadType || op.name;
188
216
  const extName = joinWithNamespace(baseForExt, envelopeNamespace);
189
217
  if (methodObj.responses?.["200"]) {
@@ -1 +1 @@
1
- {"version":3,"file":"generatePaths.d.ts","sourceRoot":"","sources":["../../src/openapi/generatePaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAG3C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,EAAE,MAAM,GAAG;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAE9B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;IAC9C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,uBAkElF"}
1
+ {"version":3,"file":"generatePaths.d.ts","sourceRoot":"","sources":["../../src/openapi/generatePaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAG3C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,EAAE,MAAM,GAAG;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAE9B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;IAC9C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,uBAoElF"}
@@ -8,8 +8,10 @@ export function generatePaths(compiled, opts) {
8
8
  const override = opts.overrides?.[op.name] || {};
9
9
  const method = (override.method || opts.defaultMethod || "post").toLowerCase();
10
10
  const tag = opts.tagsMap?.[op.name] || opts.defaultTag;
11
- const inputRef = op.inputElement?.local ? { $ref: `#/components/schemas/${op.inputElement.local}` } : { type: "object" };
12
- const outputRef = op.outputElement?.local ? { $ref: `#/components/schemas/${op.outputElement.local}` } : { type: "object" };
11
+ const inputTypeName = op.inputTypeName ?? op.inputElement?.local;
12
+ const outputTypeName = op.outputTypeName ?? op.outputElement?.local;
13
+ const inputRef = inputTypeName ? { $ref: `#/components/schemas/${inputTypeName}` } : { type: "object" };
14
+ const outputRef = outputTypeName ? { $ref: `#/components/schemas/${outputTypeName}` } : { type: "object" };
13
15
  const parameters = [];
14
16
  // Header parameters from security builder
15
17
  const headerParamNames = opts.opHeaderParameters[op.name] || [];
@@ -1 +1 @@
1
- {"version":3,"file":"generateSchemas.d.ts","sourceRoot":"","sources":["../../src/openapi/generateSchemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAgB,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAgKpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,sBAAsB,GAAG,iBAAiB,CA+C1G"}
1
+ {"version":3,"file":"generateSchemas.d.ts","sourceRoot":"","sources":["../../src/openapi/generateSchemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAgB,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAwKpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,sBAAsB,GAAG,iBAAiB,CAoD1G"}
@@ -57,6 +57,16 @@ function isArrayWrapper(t) {
57
57
  return null;
58
58
  return { itemType: e.tsType };
59
59
  }
60
+ function isSyntheticAliasSelfWrapper(t, aliasNames) {
61
+ if (!aliasNames.has(t.name))
62
+ return false;
63
+ if (t.attrs.length !== 0)
64
+ return false;
65
+ if (t.elems.length !== 1)
66
+ return false;
67
+ const e = t.elems[0];
68
+ return e.name === "$value" && e.tsType === t.name;
69
+ }
60
70
  function buildComplexSchema(t, closed, knownTypeNames, aliasNames, flattenWrappers) {
61
71
  // Use knownTypeNames/aliasNames to validate $ref targets so we surface
62
72
  // compiler issues early instead of emitting dangling references in OpenAPI output.
@@ -181,16 +191,21 @@ export function generateSchemas(compiled, opts) {
181
191
  schemas[a.name] = buildAliasSchema(a);
182
192
  }
183
193
  for (const t of compiled.types) {
194
+ if (isSyntheticAliasSelfWrapper(t, aliasNames)) {
195
+ continue;
196
+ }
184
197
  schemas[t.name] = buildComplexSchema(t, closed, typeNames, aliasNames, flattenWrappers);
185
198
  }
186
199
  if (opts.pruneUnusedSchemas) {
187
- // Root references: each operation's inputElement.local, outputElement.local
200
+ // Root references: operation type names when available, falling back to element local names.
188
201
  const roots = new Set();
189
202
  for (const op of compiled.operations) {
190
- if (op.inputElement?.local)
191
- roots.add(op.inputElement.local);
192
- if (op.outputElement?.local)
193
- roots.add(op.outputElement.local);
203
+ const inputRoot = op.inputTypeName ?? op.inputElement?.local;
204
+ const outputRoot = op.outputTypeName ?? op.outputElement?.local;
205
+ if (inputRoot)
206
+ roots.add(inputRoot);
207
+ if (outputRoot)
208
+ roots.add(outputRoot);
194
209
  }
195
210
  // BFS through $ref graph
196
211
  const reachable = new Set();
@@ -1,6 +1,7 @@
1
1
  import { type GenerateOpenAPIOptions } from "./openapi/generateOpenAPI.js";
2
2
  import { type GenerateGatewayOptions } from "./gateway/generateGateway.js";
3
3
  import { type CompilerOptions } from "./config.js";
4
+ import { type StreamConfig } from "./util/streamConfig.js";
4
5
  /**
5
6
  * Configuration options for the generation pipeline
6
7
  *
@@ -45,6 +46,18 @@ export interface PipelineOptions {
45
46
  force?: boolean;
46
47
  flattenArrayWrappers?: boolean;
47
48
  };
49
+ /**
50
+ * Path to a stream configuration JSON file. Loaded and parsed once before
51
+ * compilation; the parsed value takes precedence over `streamConfig` when
52
+ * both are set. Leave undefined to preserve byte-for-byte buffered output.
53
+ */
54
+ streamConfigFile?: string;
55
+ /**
56
+ * In-memory stream configuration. Useful for programmatic callers that
57
+ * do not want to round-trip through a file. Ignored when
58
+ * `streamConfigFile` is also provided.
59
+ */
60
+ streamConfig?: StreamConfig;
48
61
  }
49
62
  /**
50
63
  * Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI/Gateway/App
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAC,KAAK,eAAe,EAAyB,MAAM,aAAa,CAAC;AAGzE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAC;CAAE,CAAC,CAiJhH"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAC,KAAK,eAAe,EAAyB,MAAM,aAAa,CAAC;AAEzE,OAAO,EAAuB,KAAK,YAAY,EAAC,MAAM,wBAAwB,CAAC;AAG/E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;IACF;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAC;CAAE,CAAC,CAiKhH"}
package/dist/pipeline.js CHANGED
@@ -19,6 +19,8 @@ import { generateOpenAPI } from "./openapi/generateOpenAPI.js";
19
19
  import { generateGateway } from "./gateway/generateGateway.js";
20
20
  import { resolveCompilerOptions } from "./config.js";
21
21
  import { emitClientArtifacts, reportCompilationStats, reportOpenApiSuccess, success } from "./util/cli.js";
22
+ import { loadStreamConfigFile } from "./util/streamConfig.js";
23
+ import { applyShapeCatalogs } from "./compiler/shapeResolver.js";
22
24
  /**
23
25
  * Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI/Gateway/App
24
26
  *
@@ -52,10 +54,24 @@ export async function runGenerationPipeline(opts) {
52
54
  wsdl: opts.wsdl,
53
55
  out: workingDir,
54
56
  });
57
+ // Resolve stream configuration (file path takes precedence over in-memory).
58
+ const streamConfig = opts.streamConfigFile
59
+ ? loadStreamConfigFile(opts.streamConfigFile)
60
+ : opts.streamConfig;
55
61
  // Step 1: Load and parse the WSDL document
56
62
  const wsdlCatalog = await loadWsdl(opts.wsdl);
57
63
  // Step 2: Compile the WSDL into a structured catalog
58
- const compiled = compileCatalog(wsdlCatalog, finalCompiler);
64
+ const compiled = compileCatalog(wsdlCatalog, finalCompiler, streamConfig);
65
+ // Step 2b: Apply companion-catalog shape resolution when a stream config is
66
+ // provided. Relative paths in shapeCatalogs resolve against the stream
67
+ // config file's directory, or the WSDL's directory when the config came in
68
+ // programmatically.
69
+ if (streamConfig) {
70
+ const shapeBaseDir = opts.streamConfigFile
71
+ ? path.dirname(path.resolve(opts.streamConfigFile))
72
+ : path.dirname(path.resolve(opts.wsdl));
73
+ await applyShapeCatalogs(compiled, streamConfig, { baseDir: shapeBaseDir });
74
+ }
59
75
  // Report compilation statistics
60
76
  reportCompilationStats(wsdlCatalog, compiled);
61
77
  // Step 3: Emit catalog.json (always, to the specified catalogOut path)
@@ -0,0 +1,24 @@
1
+ /**
2
+ * NDJSON adapter for record iterables.
3
+ *
4
+ * Phase 3 of ADR-002. Given an `AsyncIterable<T>` (typically the output of
5
+ * `parseRecords`), produce a Node `Readable` that emits one JSON-encoded line
6
+ * per record and respects downstream backpressure.
7
+ *
8
+ * Terminal-error policy (Q3 resolved): the stream aborts on source errors.
9
+ * Before-first-byte errors surface as the stream's `error` event before any
10
+ * bytes are pushed, so Fastify can translate them into a normal JSON error
11
+ * envelope. Errors after the first byte propagate as `error` events too, but
12
+ * callers should treat them as a truncated response.
13
+ */
14
+ import { Readable } from "node:stream";
15
+ /**
16
+ * Wrap an async iterable of records in a Node `Readable` stream that emits
17
+ * NDJSON (one JSON document per line, LF-terminated). Downstream backpressure
18
+ * is honored via `Readable.from`'s default behavior: the iterator's `next()`
19
+ * is not called until the internal buffer has room.
20
+ *
21
+ * Source errors are forwarded to the returned stream's `error` event.
22
+ */
23
+ export declare function toNdjson<T>(records: AsyncIterable<T>): Readable;
24
+ //# sourceMappingURL=ndjson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ndjson.d.ts","sourceRoot":"","sources":["../../src/runtime/ndjson.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;AAErC;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,QAAQ,CAE/D"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * NDJSON adapter for record iterables.
3
+ *
4
+ * Phase 3 of ADR-002. Given an `AsyncIterable<T>` (typically the output of
5
+ * `parseRecords`), produce a Node `Readable` that emits one JSON-encoded line
6
+ * per record and respects downstream backpressure.
7
+ *
8
+ * Terminal-error policy (Q3 resolved): the stream aborts on source errors.
9
+ * Before-first-byte errors surface as the stream's `error` event before any
10
+ * bytes are pushed, so Fastify can translate them into a normal JSON error
11
+ * envelope. Errors after the first byte propagate as `error` events too, but
12
+ * callers should treat them as a truncated response.
13
+ */
14
+ import { Readable } from "node:stream";
15
+ /**
16
+ * Wrap an async iterable of records in a Node `Readable` stream that emits
17
+ * NDJSON (one JSON document per line, LF-terminated). Downstream backpressure
18
+ * is honored via `Readable.from`'s default behavior: the iterator's `next()`
19
+ * is not called until the internal buffer has room.
20
+ *
21
+ * Source errors are forwarded to the returned stream's `error` event.
22
+ */
23
+ export function toNdjson(records) {
24
+ return Readable.from(encode(records), { objectMode: false, encoding: "utf-8" });
25
+ }
26
+ async function* encode(records) {
27
+ for await (const record of records) {
28
+ yield JSON.stringify(record) + "\n";
29
+ }
30
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Catalog-driven parse specification. `recordPath` is an ordered XML element
3
+ * path from the SOAP body payload down to the repeated record element. The
4
+ * path is matched as a suffix of the open-tag stack, so callers may either
5
+ * pre-strip the SOAP envelope or feed the entire response body.
6
+ *
7
+ * Duplicate local names in `recordPath` are supported and expected (Escapia's
8
+ * EVRN content service nests two elements named `EVRN_UnitDescriptiveInfoRS`).
9
+ */
10
+ export interface RecordParseSpec {
11
+ recordPath: string[];
12
+ /** TypeScript type name of the record, used to look up propMeta. */
13
+ recordTypeName: string;
14
+ /** Attribute bag key to stash XML attributes under. Defaults to "$attributes". */
15
+ attributesKey?: string;
16
+ /**
17
+ * Compiled-catalog child-type map: `childType[typeName][propName] = tsType`.
18
+ * Used to descend into nested complex types and to detect array-valued
19
+ * props (trailing `[]`). Optional — absent means occurrence-based array
20
+ * detection only.
21
+ */
22
+ childType?: Record<string, Record<string, string>>;
23
+ /**
24
+ * Compiled-catalog prop-meta map: carries min/max/nillable/declaredType.
25
+ * When present, `max > 1` or `max === "unbounded"` drives array emission
26
+ * even for props that happen to occur just once in a given record.
27
+ */
28
+ propMeta?: Record<string, Record<string, PropMeta>>;
29
+ }
30
+ export interface PropMeta {
31
+ min?: number;
32
+ max?: number | "unbounded";
33
+ nillable?: boolean;
34
+ declaredType?: string;
35
+ }
36
+ /**
37
+ * Consume an async iterable of XML bytes/strings and yield parsed records.
38
+ *
39
+ * The returned iterator is single-pass; iteration must complete (or be
40
+ * interrupted by the consumer via `break`/`return`) before the upstream
41
+ * source is released. Errors from the source or from the SAX parser abort
42
+ * the iteration via a rejected `next()`.
43
+ */
44
+ export declare function parseRecords<T = unknown>(source: AsyncIterable<string | Uint8Array>, spec: RecordParseSpec): AsyncIterable<T>;
45
+ //# sourceMappingURL=streamXml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamXml.d.ts","sourceRoot":"","sources":["../../src/runtime/streamXml.ts"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,oEAAoE;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAuB,YAAY,CAAC,CAAC,GAAG,OAAO,EAC7C,MAAM,EAAE,aAAa,CAAC,MAAM,GAAG,UAAU,CAAC,EAC1C,IAAI,EAAE,eAAe,GACpB,aAAa,CAAC,CAAC,CAAC,CAqGlB"}