@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.
- package/README.md +5 -0
- package/dist/app/generateApp.d.ts.map +1 -1
- package/dist/app/generateApp.js +4 -3
- package/dist/cli.js +46 -2
- package/dist/client/generateClient.d.ts.map +1 -1
- package/dist/client/generateClient.js +64 -8
- package/dist/client/generateOperations.d.ts.map +1 -1
- package/dist/client/generateOperations.js +29 -6
- package/dist/client/generateTypes.d.ts.map +1 -1
- package/dist/client/generateTypes.js +13 -0
- package/dist/compiler/schemaCompiler.d.ts +44 -11
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +102 -6
- package/dist/compiler/shapeResolver.d.ts +18 -0
- package/dist/compiler/shapeResolver.d.ts.map +1 -0
- package/dist/compiler/shapeResolver.js +280 -0
- package/dist/gateway/generateGateway.d.ts.map +1 -1
- package/dist/gateway/generateGateway.js +2 -1
- package/dist/gateway/generators.d.ts +13 -1
- package/dist/gateway/generators.d.ts.map +1 -1
- package/dist/gateway/generators.js +98 -13
- package/dist/gateway/helpers.d.ts +16 -0
- package/dist/gateway/helpers.d.ts.map +1 -1
- package/dist/gateway/helpers.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -4
- package/dist/openapi/generateOpenAPI.d.ts.map +1 -1
- package/dist/openapi/generateOpenAPI.js +30 -2
- package/dist/openapi/generatePaths.d.ts.map +1 -1
- package/dist/openapi/generatePaths.js +4 -2
- package/dist/openapi/generateSchemas.d.ts.map +1 -1
- package/dist/openapi/generateSchemas.js +20 -5
- package/dist/pipeline.d.ts +13 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +17 -1
- package/dist/runtime/ndjson.d.ts +24 -0
- package/dist/runtime/ndjson.d.ts.map +1 -0
- package/dist/runtime/ndjson.js +30 -0
- package/dist/runtime/streamXml.d.ts +45 -0
- package/dist/runtime/streamXml.d.ts.map +1 -0
- package/dist/runtime/streamXml.js +212 -0
- package/dist/test/generators.d.ts +2 -2
- package/dist/test/generators.d.ts.map +1 -1
- package/dist/test/generators.js +79 -26
- package/dist/test/mockData.d.ts +12 -2
- package/dist/test/mockData.d.ts.map +1 -1
- package/dist/test/mockData.js +17 -8
- package/dist/util/cli.d.ts +3 -0
- package/dist/util/cli.d.ts.map +1 -1
- package/dist/util/cli.js +6 -1
- package/dist/util/runtimeSource.d.ts +2 -0
- package/dist/util/runtimeSource.d.ts.map +1 -0
- package/dist/util/runtimeSource.js +38 -0
- package/dist/util/streamConfig.d.ts +59 -0
- package/dist/util/streamConfig.d.ts.map +1 -0
- package/dist/util/streamConfig.js +230 -0
- package/docs/README.md +1 -0
- package/docs/api-reference.md +146 -0
- package/docs/architecture.md +27 -5
- package/docs/cli-reference.md +30 -0
- package/docs/concepts.md +150 -11
- package/docs/configuration.md +40 -0
- package/docs/decisions/002-streamable-responses.md +308 -0
- package/docs/gateway-guide.md +37 -0
- package/docs/generated-code.md +21 -0
- package/docs/migration-playbook.md +33 -0
- package/docs/migration.md +31 -6
- package/docs/output-anatomy.md +49 -0
- package/docs/production.md +32 -0
- package/docs/start-here.md +33 -0
- package/docs/supported-patterns.md +29 -0
- package/docs/testing.md +14 -0
- package/docs/troubleshooting.md +18 -0
- package/package.json +9 -6
- package/src/runtime/clientStreamMethods.tpl.txt +183 -0
- package/src/runtime/ndjson.ts +32 -0
- package/src/runtime/operationsStreamHelper.tpl.txt +13 -0
- 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:
|
|
6
|
-
response:
|
|
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,
|
|
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"}
|
package/dist/test/generators.js
CHANGED
|
@@ -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
|
-
|
|
75
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
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 {
|
package/dist/test/mockData.d.ts
CHANGED
|
@@ -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:
|
|
79
|
-
response:
|
|
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;
|
|
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"}
|
package/dist/test/mockData.js
CHANGED
|
@@ -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
|
|
167
|
-
|
|
168
|
-
|
|
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;
|
package/dist/util/cli.d.ts
CHANGED
|
@@ -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)
|
package/dist/util/cli.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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 @@
|
|
|
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"}
|