@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
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Streaming SOAP-payload to record iterator.
3
+ *
4
+ * Phase 3 of ADR-002. Driven by saxes (chunk-boundary safe — proven in
5
+ * test/research/sax-record-path.test.ts) and the compiled-catalog metadata
6
+ * that the buffered client already relies on. The parser accepts an async
7
+ * iterable of bytes/strings (typically an upstream SOAP HTTP response) and
8
+ * yields fully-materialized record objects as the corresponding end tags
9
+ * close. Consumers never see partial records.
10
+ *
11
+ * Open questions resolved here:
12
+ * Q3 (terminal error policy): stream aborts. Errors that happen before the
13
+ * first record bubble out as a rejected promise from the first
14
+ * iterator.next(). Errors after a record was emitted throw from a
15
+ * later iterator.next() — callers are expected to treat that as a
16
+ * truncated stream.
17
+ * Q4 (saxes placement): runtime dependency of the wsdl-tsc package; the
18
+ * generated client imports the emitted copy of this module and
19
+ * inherits saxes via its own dependency tree.
20
+ */
21
+ import { SaxesParser } from "saxes";
22
+ /**
23
+ * Consume an async iterable of XML bytes/strings and yield parsed records.
24
+ *
25
+ * The returned iterator is single-pass; iteration must complete (or be
26
+ * interrupted by the consumer via `break`/`return`) before the upstream
27
+ * source is released. Errors from the source or from the SAX parser abort
28
+ * the iteration via a rejected `next()`.
29
+ */
30
+ export async function* parseRecords(source, spec) {
31
+ const parser = new SaxesParser({ xmlns: false, position: false });
32
+ const recordPath = spec.recordPath;
33
+ if (recordPath.length === 0) {
34
+ throw new Error("parseRecords: recordPath must not be empty");
35
+ }
36
+ const attrsKey = spec.attributesKey ?? "$attributes";
37
+ // Global tag stack, maintained across the entire document.
38
+ const stack = [];
39
+ // Records materialized during the current chunk write, awaiting yield.
40
+ const pending = [];
41
+ // When a parser.on('error') fires we buffer the error for re-throw on the
42
+ // next yield cycle; saxes emits 'error' and continues unless we stop it.
43
+ let parseError = null;
44
+ const openNodes = [];
45
+ parser.on("opentag", (tag) => {
46
+ stack.push(tag.name);
47
+ if (openNodes.length === 0) {
48
+ // Not yet inside a record. Check whether entering the path tail.
49
+ if (tailMatches(stack, recordPath)) {
50
+ openNodes.push(newNode(tag, spec.recordTypeName, null, attrsKey));
51
+ }
52
+ return;
53
+ }
54
+ // Inside a record: descend.
55
+ const parent = openNodes[openNodes.length - 1];
56
+ parent.hadChildren = true;
57
+ const parentType = parent.typeName;
58
+ const childTypeRaw = parentType ? spec.childType?.[parentType]?.[tag.name] : undefined;
59
+ const childTypeName = childTypeRaw ? stripArraySuffix(childTypeRaw) : null;
60
+ openNodes.push(newNode(tag, childTypeName, tag.name, attrsKey));
61
+ });
62
+ const appendText = (t) => {
63
+ if (openNodes.length > 0)
64
+ openNodes[openNodes.length - 1].textBuf.push(t);
65
+ };
66
+ parser.on("text", appendText);
67
+ parser.on("cdata", appendText);
68
+ parser.on("closetag", (_tag) => {
69
+ if (openNodes.length > 0) {
70
+ const closing = openNodes.pop();
71
+ const value = materialize(closing, attrsKey);
72
+ if (openNodes.length === 0) {
73
+ // We just closed the record root. Stack tail must match once more.
74
+ if (tailMatches(stack, recordPath)) {
75
+ pending.push(value);
76
+ }
77
+ }
78
+ else {
79
+ assignChild(openNodes[openNodes.length - 1], closing.propName, value, spec);
80
+ }
81
+ }
82
+ stack.pop();
83
+ });
84
+ parser.on("error", (err) => {
85
+ parseError = err;
86
+ });
87
+ try {
88
+ for await (const chunk of source) {
89
+ if (parseError)
90
+ throw parseError;
91
+ const text = typeof chunk === "string" ? chunk : decodeUtf8(chunk);
92
+ parser.write(text);
93
+ if (parseError)
94
+ throw parseError;
95
+ while (pending.length > 0) {
96
+ yield pending.shift();
97
+ }
98
+ }
99
+ parser.close();
100
+ if (parseError)
101
+ throw parseError;
102
+ while (pending.length > 0) {
103
+ yield pending.shift();
104
+ }
105
+ }
106
+ finally {
107
+ // Best-effort cleanup: detach handlers so the parser can be GC'd even
108
+ // when the consumer aborts iteration early.
109
+ parser.off("opentag");
110
+ parser.off("closetag");
111
+ parser.off("text");
112
+ parser.off("cdata");
113
+ parser.off("error");
114
+ }
115
+ }
116
+ function newNode(tag, typeName, propName, attrsKey) {
117
+ const attrs = tag.attributes;
118
+ const obj = {};
119
+ let isNil = false;
120
+ const attrKeys = Object.keys(attrs);
121
+ if (attrKeys.length > 0) {
122
+ // Detect xsi:nil and drop it from the attribute bag — it's a wire-level
123
+ // concern that should not pollute the user-visible record.
124
+ const cleaned = {};
125
+ for (const k of attrKeys) {
126
+ if ((k === "xsi:nil" || k === "nil") && attrs[k] === "true") {
127
+ isNil = true;
128
+ continue;
129
+ }
130
+ cleaned[k] = attrs[k];
131
+ }
132
+ if (Object.keys(cleaned).length > 0) {
133
+ obj[attrsKey] = cleaned;
134
+ }
135
+ }
136
+ return {
137
+ obj,
138
+ typeName,
139
+ propName,
140
+ textBuf: [],
141
+ hadChildren: false,
142
+ isNil,
143
+ };
144
+ }
145
+ function materialize(node, attrsKey) {
146
+ if (node.isNil)
147
+ return null;
148
+ if (!node.hadChildren) {
149
+ // Leaf with no child elements: it's simple text. Preserve attributes via
150
+ // a `$value` pairing when present, mirroring how the buffered mapper
151
+ // surfaces simpleContent-with-attributes types.
152
+ const text = node.textBuf.join("");
153
+ if (attrsKey in node.obj) {
154
+ return { ...node.obj, $value: text };
155
+ }
156
+ return text;
157
+ }
158
+ return node.obj;
159
+ }
160
+ function assignChild(parent, propName, value, spec) {
161
+ const parentType = parent.typeName;
162
+ const propMetaEntry = parentType ? spec.propMeta?.[parentType]?.[propName] : undefined;
163
+ const childTypeHint = parentType ? spec.childType?.[parentType]?.[propName] : undefined;
164
+ // Array if: (a) propMeta says max > 1 or "unbounded", or (b) childType hint
165
+ // ends in `[]`, or (c) the slot is already occupied (implicit repetition).
166
+ const metaSaysArray = propMetaEntry?.max === "unbounded" ||
167
+ (typeof propMetaEntry?.max === "number" && propMetaEntry.max > 1);
168
+ const hintSaysArray = !!childTypeHint && childTypeHint.endsWith("[]");
169
+ const existing = parent.obj[propName];
170
+ const slotTaken = existing !== undefined;
171
+ if (metaSaysArray || hintSaysArray) {
172
+ if (Array.isArray(existing)) {
173
+ existing.push(value);
174
+ }
175
+ else if (slotTaken) {
176
+ parent.obj[propName] = [existing, value];
177
+ }
178
+ else {
179
+ parent.obj[propName] = [value];
180
+ }
181
+ return;
182
+ }
183
+ if (slotTaken) {
184
+ // Schema said scalar but the wire repeated it. Promote to array rather
185
+ // than drop data.
186
+ if (Array.isArray(existing)) {
187
+ existing.push(value);
188
+ }
189
+ else {
190
+ parent.obj[propName] = [existing, value];
191
+ }
192
+ return;
193
+ }
194
+ parent.obj[propName] = value;
195
+ }
196
+ function stripArraySuffix(tsType) {
197
+ return tsType.endsWith("[]") ? tsType.slice(0, -2) : tsType;
198
+ }
199
+ function tailMatches(stack, path) {
200
+ if (stack.length < path.length)
201
+ return false;
202
+ const offset = stack.length - path.length;
203
+ for (let i = 0; i < path.length; i++) {
204
+ if (stack[offset + i] !== path[i])
205
+ return false;
206
+ }
207
+ return true;
208
+ }
209
+ const UTF8_DECODER = new TextDecoder("utf-8");
210
+ function decodeUtf8(buf) {
211
+ return UTF8_DECODER.decode(buf);
212
+ }
@@ -2,8 +2,8 @@ import type { ClientMeta, ResolvedOperationMeta } from "../gateway/helpers.js";
2
2
  import type { CatalogForMocks } from "./mockData.js";
3
3
  /** Pre-computed mock data map passed from the orchestrator to all emitters. */
4
4
  export type OperationMocksMap = Map<string, {
5
- request: Record<string, unknown>;
6
- response: Record<string, unknown>;
5
+ request: unknown;
6
+ response: unknown;
7
7
  }>;
8
8
  /**
9
9
  * Emits vitest.config.ts content.
@@ -1 +1 @@
1
- {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/test/generators.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAGnD,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAerH;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAkBzC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CA+CR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,GACrB,MAAM,CAsCR;AAED;;;;;;;GAOG;AAEH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CA8CR;AAED;;;;;;;GAOG;AAEH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CAiIR;AAED;;GAEG;AAEH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CAuER;AAED;;GAEG;AAEH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,GAClC,MAAM,CAoCR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,MAAM,CAgFR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,MAAM,CAgDR;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,OAAO,EAAE,eAAe,GACvB,MAAM,GAAG,IAAI,CAkEf"}
1
+ {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/test/generators.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAGnD,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAerF;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAkBzC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CAgFR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,GACrB,MAAM,CAsCR;AAED;;;;;;;GAOG;AAEH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CAuER;AAED;;;;;;;GAOG;AAEH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CAiIR;AAED;;GAEG;AAEH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CAuER;AAED;;GAEG;AAEH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,GAClC,MAAM,CAoCR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,MAAM,CAgFR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,MAAM,CAgDR;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,OAAO,EAAE,eAAe,GACvB,MAAM,GAAG,IAAI,CAkEf"}
@@ -60,46 +60,75 @@ export default defineProject({
60
60
  export function emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations, mocks) {
61
61
  const helpersDir = `${testDir}/helpers`;
62
62
  const operationsImport = computeRelativeImport(helpersDir, `${clientDir}/operations`, importsMode);
63
+ const operationsType = `${clientMeta.className}Operations`;
63
64
  // Sort operations for deterministic output
64
65
  const sortedOps = [...operations].sort((a, b) => a.operationId.localeCompare(b.operationId));
66
+ const hasStream = sortedOps.some((op) => !!op.stream);
65
67
  const methodEntries = sortedOps.map((op) => {
66
68
  const mockData = mocks.get(op.operationId);
67
69
  const response = mockData?.response ?? {};
70
+ if (op.stream) {
71
+ // For stream operations, the mock emits a single-record iterable derived
72
+ // from the mock data. Tests that need multi-record sequences can pass an
73
+ // override via createMockClient({...}).
74
+ const recordJson = JSON.stringify(response, null, 6).replace(/\n/g, "\n ");
75
+ return ` ${op.operationId}: async () => ({
76
+ records: asyncIterableOf(${recordJson}),
77
+ headers: {},
78
+ })`;
79
+ }
68
80
  const responseJson = JSON.stringify(response, null, 6).replace(/\n/g, "\n ");
69
81
  return ` ${op.operationId}: async () => ({
70
82
  response: ${responseJson},
71
83
  headers: {},
72
84
  })`;
73
85
  }).join(",\n");
74
- return `/**
75
- * Mock SOAP Client Helper
76
- *
77
- * Creates a mock client implementing the ${clientMeta.className}Operations interface
78
- * with full default responses per operation. Override individual methods as needed.
79
- *
80
- * Auto-generated by wsdl-tsc --test-dir. Customize freely.
81
- */
82
- import type { ${clientMeta.className}Operations } from "${operationsImport}";
83
-
86
+ const streamHelper = hasStream
87
+ ? `
84
88
  /**
85
- * Creates a mock SOAP client with default responses for all operations.
86
- *
87
- * Response shapes match the real SOAP client wrapper structure.
88
- * ArrayOf* types use wrapper objects (e.g. { Forecast: [...] }).
89
- * The generated unwrapArrayWrappers() function strips these at runtime.
90
- *
91
- * @param overrides - Override specific operation implementations
92
- * @returns Mock client implementing ${clientMeta.className}Operations
89
+ * Wraps a single record (or an array of records) as an async iterable, matching
90
+ * the StreamOperationResponse.records shape the real client would return.
93
91
  */
94
- export function createMockClient(
95
- overrides: Partial<${clientMeta.className}Operations> = {}
96
- ): ${clientMeta.className}Operations {
97
- return {
98
- ${methodEntries},
99
- ...overrides,
100
- } as ${clientMeta.className}Operations;
92
+ function asyncIterableOf<T>(value: T | T[]): AsyncIterable<T> {
93
+ const items = Array.isArray(value) ? value : [value];
94
+ return (async function* () {
95
+ for (const item of items) yield item;
96
+ })();
101
97
  }
102
- `;
98
+ `
99
+ : "";
100
+ return [
101
+ "/**",
102
+ " * Mock SOAP Client Helper",
103
+ " *",
104
+ ` * Creates a mock client implementing the ${operationsType} interface`,
105
+ " * with full default responses per operation. Override individual methods as needed.",
106
+ " *",
107
+ " * Auto-generated by wsdl-tsc --test-dir. Customize freely.",
108
+ " */",
109
+ "import type { " + operationsType + ` } from "${operationsImport}";`,
110
+ "",
111
+ "/**",
112
+ " * Creates a mock SOAP client with default responses for all operations.",
113
+ " *",
114
+ " * Response shapes match the real SOAP client wrapper structure.",
115
+ " * ArrayOf* types use wrapper objects (e.g. { Forecast: [...] }).",
116
+ " * The generated unwrapArrayWrappers() function strips these at runtime.",
117
+ " *",
118
+ " * @param overrides - Override specific operation implementations",
119
+ ` * @returns Mock client implementing ${operationsType}`,
120
+ " */",
121
+ "export function createMockClient(",
122
+ " overrides: Partial<" + operationsType + "> = {}",
123
+ "): " + operationsType + " {",
124
+ " return {",
125
+ methodEntries ? `${methodEntries},` : "",
126
+ " ...overrides,",
127
+ " } as " + operationsType + ";",
128
+ "}",
129
+ ...(streamHelper ? [streamHelper] : []),
130
+ "",
131
+ ].join("\n");
103
132
  }
104
133
  /**
105
134
  * Emits helpers/test-app.ts content.
@@ -166,6 +195,30 @@ export function emitRoutesTest(testDir, importsMode, operations, mocks) {
166
195
  const requestPayload = JSON.stringify(mockData?.request ?? {}, null, 4).replace(/\n/g, "\n ");
167
196
  const hint = formatOperationHint(op.summary, op.description);
168
197
  const hintComment = hint ? ` // ${hint}\n` : "";
198
+ if (op.stream) {
199
+ // Stream routes return NDJSON lines, not a single JSON envelope. Assert
200
+ // on content-type and at least one parseable record per line.
201
+ return `${hintComment} it("${op.method.toUpperCase()} ${op.path} streams ${op.stream.format} records", async () => {
202
+ const app = await createTestApp();
203
+ try {
204
+ const res = await app.inject({
205
+ method: "${op.method.toUpperCase()}",
206
+ url: "${op.path}",
207
+ payload: ${requestPayload},
208
+ });
209
+ expect(res.statusCode).toBe(200);
210
+ expect(String(res.headers["content-type"] ?? "")).toContain(${JSON.stringify(op.stream.mediaType)});
211
+ const lines = res.body.split("\\n").filter((l: string) => l.length > 0);
212
+ expect(lines.length).toBeGreaterThan(0);
213
+ for (const line of lines) {
214
+ const record = JSON.parse(line);
215
+ expect(record).toBeDefined();
216
+ }
217
+ } finally {
218
+ await app.close();
219
+ }
220
+ });`;
221
+ }
169
222
  return `${hintComment} it("${op.method.toUpperCase()} ${op.path} returns SUCCESS envelope", async () => {
170
223
  const app = await createTestApp();
171
224
  try {
@@ -22,6 +22,12 @@ export interface CatalogForMocks {
22
22
  name: string;
23
23
  inputTypeName?: string;
24
24
  outputTypeName?: string;
25
+ stream?: {
26
+ format: "ndjson" | "json-array";
27
+ mediaType: string;
28
+ recordTypeName: string;
29
+ recordPath: string[];
30
+ };
25
31
  }>;
26
32
  types?: Array<{
27
33
  name: string;
@@ -33,6 +39,10 @@ export interface CatalogForMocks {
33
39
  max: number | "unbounded";
34
40
  }>;
35
41
  }>;
42
+ aliases?: Array<{
43
+ name: string;
44
+ tsType: string;
45
+ }>;
36
46
  }
37
47
  /**
38
48
  * Generates a mock primitive value based on the TypeScript type and property name.
@@ -75,7 +85,7 @@ export interface GenerateAllMocksOptions {
75
85
  * @returns Map from operation name to { request, response } mock data
76
86
  */
77
87
  export declare function generateAllOperationMocks(catalog: CatalogForMocks, opts?: GenerateAllMocksOptions): Map<string, {
78
- request: Record<string, unknown>;
79
- response: Record<string, unknown>;
88
+ request: unknown;
89
+ response: unknown;
80
90
  }>;
81
91
  //# sourceMappingURL=mockData.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mockData.d.ts","sourceRoot":"","sources":["../../src/test/mockData.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACnD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;YACvC,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;YAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;SACpB,CAAC,CAAC,CAAC;KACL,CAAC;IACF,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC/B,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;SAAE,CAAC,CAAC;KAC3D,CAAC,CAAC;CACJ;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAwCjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoDzB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CA+BtF"}
1
+ {"version":3,"file":"mockData.d.ts","sourceRoot":"","sources":["../../src/test/mockData.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACnD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;YACvC,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;YAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;SACpB,CAAC,CAAC,CAAC;KACL,CAAC;IACF,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE;YACP,MAAM,EAAE,QAAQ,GAAG,YAAY,CAAC;YAChC,SAAS,EAAE,MAAM,CAAC;YAClB,cAAc,EAAE,MAAM,CAAC;YACvB,UAAU,EAAE,MAAM,EAAE,CAAC;SACtB,CAAC;KACH,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC/B,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;SAAE,CAAC,CAAC;KAC3D,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAwCjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoDzB;AAWD;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CA+BtD"}
@@ -131,6 +131,15 @@ export function generateMockData(typeName, catalog, opts, visited, depth) {
131
131
  }
132
132
  return result;
133
133
  }
134
+ function generateOperationPayload(typeName, catalog) {
135
+ if (!typeName)
136
+ return {};
137
+ const alias = catalog.aliases?.find(a => a.name === typeName);
138
+ if (alias) {
139
+ return generateMockPrimitive(alias.tsType, typeName);
140
+ }
141
+ return generateMockData(typeName, catalog);
142
+ }
134
143
  /**
135
144
  * Generates mock request and response data for all operations in the catalog.
136
145
  *
@@ -156,17 +165,17 @@ export function generateAllOperationMocks(catalog, opts) {
156
165
  const shouldFlatten = Object.keys(arrayWrappers).length > 0;
157
166
  const childTypeMap = catalog.meta?.childType ?? {};
158
167
  for (const op of catalog.operations) {
159
- let request = op.inputTypeName
160
- ? generateMockData(op.inputTypeName, catalog)
161
- : {};
168
+ let request = generateOperationPayload(op.inputTypeName, catalog);
162
169
  // Flatten request payloads when array wrappers are active
163
- if (shouldFlatten && op.inputTypeName) {
170
+ if (shouldFlatten && op.inputTypeName && request != null && typeof request === "object" && !Array.isArray(request)) {
164
171
  request = flattenMockPayload(request, op.inputTypeName, childTypeMap, arrayWrappers);
165
172
  }
166
- // Response stays SOAP-shaped (pre-unwrap) since runtime unwrapArrayWrappers() handles it
167
- const response = op.outputTypeName
168
- ? generateMockData(op.outputTypeName, catalog)
169
- : {};
173
+ // Response: for stream ops we generate a single record of the configured
174
+ // recordType so the mock client can yield it through asyncIterableOf().
175
+ // Otherwise the SOAP-shaped outputType envelope (pre-unwrap).
176
+ const response = op.stream
177
+ ? generateOperationPayload(op.stream.recordTypeName, catalog)
178
+ : generateOperationPayload(op.outputTypeName, catalog);
170
179
  result.set(op.name, { request, response });
171
180
  }
172
181
  return result;
@@ -79,6 +79,9 @@ export declare function reportCompilationStats(wsdlCatalog: {
79
79
  }, compiled: {
80
80
  types: any[];
81
81
  operations: any[];
82
+ diagnostics?: {
83
+ notes?: string[];
84
+ };
82
85
  }): void;
83
86
  /**
84
87
  * Emit TypeScript client artifacts (client.ts, types.ts, utils.ts, operations.ts)
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/util/cli.ts"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAc1E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAMhE;AAED;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAU,GAAG,KAAK,CAQ7E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,IAAI,CASP;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE;IAAE,OAAO,EAAE,GAAG,EAAE,CAAA;CAAE,EAC/B,QAAQ,EAAE;IAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAAC,UAAU,EAAE,GAAG,EAAE,CAAA;CAAE,GAC5C,IAAI,CAIN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,GAAG,EACb,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,EACrD,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,EACpD,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,EACpD,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,GACzD,IAAI,CAWN;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,oBAAoB,EAAE,MAAM,GAAG,SAAS,GACvC,IAAI,CAUN"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/util/cli.ts"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAc1E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAMhE;AAED;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAU,GAAG,KAAK,CAY7E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,IAAI,CASP;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE;IAAE,OAAO,EAAE,GAAG,EAAE,CAAA;CAAE,EAC/B,QAAQ,EAAE;IAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAAC,UAAU,EAAE,GAAG,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;CAAE,GAChF,IAAI,CAON;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,GAAG,EACb,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,EACrD,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,EACpD,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,EACpD,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,GACzD,IAAI,CAWN;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,oBAAoB,EAAE,MAAM,GAAG,SAAS,GACvC,IAAI,CAUN"}
package/dist/util/cli.js CHANGED
@@ -95,7 +95,9 @@ export function info(message) {
95
95
  * @param exitCode - Process exit code (default: 1)
96
96
  */
97
97
  export function handleCLIError(errorObj, exitCode = 1) {
98
- if (errorObj instanceof WsdlCompilationError) {
98
+ // Known structured errors carry richer context; print their multi-line form.
99
+ if (errorObj instanceof WsdlCompilationError ||
100
+ (errorObj instanceof Error && typeof errorObj.toUserMessage === "function")) {
99
101
  error(errorObj.toUserMessage());
100
102
  }
101
103
  else {
@@ -129,6 +131,9 @@ export function reportCompilationStats(wsdlCatalog, compiled) {
129
131
  info(`Schemas discovered: ${wsdlCatalog.schemas.length}`);
130
132
  info(`Compiled types: ${compiled.types.length}`);
131
133
  info(`Operations: ${compiled.operations.length}`);
134
+ for (const note of compiled.diagnostics?.notes ?? []) {
135
+ info(`Note: ${note}`);
136
+ }
132
137
  }
133
138
  /**
134
139
  * Emit TypeScript client artifacts (client.ts, types.ts, utils.ts, operations.ts)
@@ -0,0 +1,2 @@
1
+ export declare function loadRuntimeSource(filename: string): string;
2
+ //# sourceMappingURL=runtimeSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeSource.d.ts","sourceRoot":"","sources":["../../src/util/runtimeSource.ts"],"names":[],"mappings":"AAsCA,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Helpers to load wsdl-tsc's own runtime modules as source text, for embedding
3
+ * into generated client and gateway output.
4
+ *
5
+ * The runtime modules live at `src/runtime/*.ts` and are published alongside
6
+ * `dist/` via the `files` field in package.json. Resolving via
7
+ * `import.meta.url` works both in dev (tsx runs the .ts directly from src/) and
8
+ * in a published install (generator code lives under dist/, and `../../src/runtime/…`
9
+ * climbs to the package root where the source .ts files sit beside `dist/`).
10
+ */
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
15
+ const HEADER = (sourceName) => `// -----------------------------------------------------------------------------
16
+ // Embedded from @techspokes/typescript-wsdl-client runtime (${sourceName}).
17
+ // This file is generated output — do not edit; regenerate via wsdl-tsc.
18
+ // -----------------------------------------------------------------------------
19
+ `;
20
+ function resolveRuntimeSourcePath(filename) {
21
+ // Walk candidates in order: dev layout (src/util -> src/runtime),
22
+ // then published layout (dist/util -> ../src/runtime), then package root.
23
+ const candidates = [
24
+ path.resolve(MODULE_DIR, "..", "runtime", filename),
25
+ path.resolve(MODULE_DIR, "..", "..", "src", "runtime", filename),
26
+ path.resolve(MODULE_DIR, "..", "..", "..", "src", "runtime", filename),
27
+ ];
28
+ for (const c of candidates) {
29
+ if (fs.existsSync(c))
30
+ return c;
31
+ }
32
+ throw new Error(`Could not locate runtime source "${filename}". Looked in:\n${candidates.join("\n")}`);
33
+ }
34
+ export function loadRuntimeSource(filename) {
35
+ const abs = resolveRuntimeSourcePath(filename);
36
+ const raw = fs.readFileSync(abs, "utf-8");
37
+ return HEADER(filename) + "\n" + raw;
38
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Per-operation stream metadata, normalized form. Exported so that the
3
+ * compiler, emitters, and gateway all consume the same shape.
4
+ */
5
+ export interface OperationStreamMetadata {
6
+ mode: "stream";
7
+ format: "ndjson" | "json-array";
8
+ mediaType: string;
9
+ recordPath: string[];
10
+ recordTypeName: string;
11
+ shapeCatalogName?: string;
12
+ sourceOutputTypeName?: string;
13
+ }
14
+ /**
15
+ * Reference to a companion catalog that supplies record shapes. Exactly one
16
+ * of `wsdlSource` or `catalogFile` must be set.
17
+ */
18
+ export interface ShapeCatalogRef {
19
+ wsdlSource?: string;
20
+ catalogFile?: string;
21
+ }
22
+ /**
23
+ * Parsed and normalized stream configuration.
24
+ */
25
+ export interface StreamConfig {
26
+ shapeCatalogs: Record<string, ShapeCatalogRef>;
27
+ operations: Record<string, OperationStreamMetadata>;
28
+ }
29
+ /**
30
+ * Structured error thrown when a stream configuration cannot be parsed.
31
+ * The CLI error handler prints `.toUserMessage()` verbatim.
32
+ */
33
+ export declare class StreamConfigError extends Error {
34
+ readonly context: {
35
+ file?: string;
36
+ pointer?: string;
37
+ suggestion?: string;
38
+ };
39
+ readonly name = "StreamConfigError";
40
+ constructor(message: string, context?: {
41
+ file?: string;
42
+ pointer?: string;
43
+ suggestion?: string;
44
+ });
45
+ toUserMessage(): string;
46
+ }
47
+ /**
48
+ * Parse a stream configuration from an in-memory JSON value. Used directly
49
+ * by tests; the file-based variant below wraps this.
50
+ */
51
+ export declare function parseStreamConfig(raw: unknown, opts?: {
52
+ file?: string;
53
+ }): StreamConfig;
54
+ /**
55
+ * Load and parse a stream configuration file. Relative paths are resolved
56
+ * against the caller's cwd.
57
+ */
58
+ export declare function loadStreamConfigFile(filePath: string): StreamConfig;
59
+ //# sourceMappingURL=streamConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamConfig.d.ts","sourceRoot":"","sources":["../../src/util/streamConfig.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,QAAQ,GAAG,YAAY,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;CACrD;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAKxB,OAAO,EAAE;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAC;IAJjF,SAAkB,IAAI,uBAAuB;gBAG3C,OAAO,EAAE,MAAM,EACC,OAAO,GAAE;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAM;IAKtF,aAAa,IAAI,MAAM;CAOxB;AASD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,GAAE;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAM,GAAG,YAAY,CAwBxF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAqBnE"}