@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
|
@@ -153,7 +153,7 @@ function extractAnnotationDocumentation(node) {
|
|
|
153
153
|
* 4. Extract WSDL operations: pick the appropriate SOAP binding (v1.1 or v1.2), resolve its
|
|
154
154
|
* portType reference, then enumerate operations and their soapAction URIs.
|
|
155
155
|
*/
|
|
156
|
-
export function compileCatalog(cat, options) {
|
|
156
|
+
export function compileCatalog(cat, options, streamConfig) {
|
|
157
157
|
// symbol tables discovered across all schemas
|
|
158
158
|
const complexTypes = new Map();
|
|
159
159
|
const simpleTypes = new Map();
|
|
@@ -190,6 +190,13 @@ export function compileCatalog(cat, options) {
|
|
|
190
190
|
const attrType = {};
|
|
191
191
|
const childType = {};
|
|
192
192
|
const propMeta = {};
|
|
193
|
+
const diagnostics = {};
|
|
194
|
+
const addDiagnosticNote = (note) => {
|
|
195
|
+
diagnostics.notes ??= [];
|
|
196
|
+
if (!diagnostics.notes.includes(note)) {
|
|
197
|
+
diagnostics.notes.push(note);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
193
200
|
/** Compile a simpleType node to TS */
|
|
194
201
|
function compileSimpleTypeNode(simpleNode, schemaNS, prefixes) {
|
|
195
202
|
const rest = getFirstWithLocalName(simpleNode, "restriction");
|
|
@@ -346,6 +353,33 @@ export function compileCatalog(cat, options) {
|
|
|
346
353
|
}
|
|
347
354
|
return out;
|
|
348
355
|
};
|
|
356
|
+
// Walks the same compositor structure as collectParticles and returns any
|
|
357
|
+
// xs:any wildcard particles found. Kept as a sibling to avoid reshaping
|
|
358
|
+
// collectParticles' return type at the three call sites below.
|
|
359
|
+
const collectWildcards = (node) => {
|
|
360
|
+
const out = [];
|
|
361
|
+
const recurse = (groupNode) => {
|
|
362
|
+
for (const a of getChildrenWithLocalName(groupNode, "any")) {
|
|
363
|
+
const min = a["@_minOccurs"] ? Number(a["@_minOccurs"]) : 1;
|
|
364
|
+
const maxAttr = a["@_maxOccurs"];
|
|
365
|
+
const max = maxAttr === "unbounded" ? "unbounded" : maxAttr ? Number(maxAttr) : 1;
|
|
366
|
+
const pc = a["@_processContents"];
|
|
367
|
+
out.push({
|
|
368
|
+
min,
|
|
369
|
+
max,
|
|
370
|
+
...(a["@_namespace"] ? { namespace: a["@_namespace"] } : {}),
|
|
371
|
+
...(pc === "lax" || pc === "strict" || pc === "skip" ? { processContents: pc } : {}),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
for (const comp of ["sequence", "all", "choice"]) {
|
|
375
|
+
for (const sub of getChildrenWithLocalName(groupNode, comp)) {
|
|
376
|
+
recurse(sub);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
recurse(node);
|
|
381
|
+
return out;
|
|
382
|
+
};
|
|
349
383
|
const collectParticles = (ownerTypeName, node) => {
|
|
350
384
|
const out = [];
|
|
351
385
|
// process a compositor or element container recursively
|
|
@@ -417,8 +451,12 @@ export function compileCatalog(cat, options) {
|
|
|
417
451
|
// On duplicate definitions, merge new attributes and elements
|
|
418
452
|
const newAttrs = collectAttributes(cnode);
|
|
419
453
|
const newElems = collectParticles(outName, cnode);
|
|
454
|
+
const newWildcards = collectWildcards(cnode);
|
|
420
455
|
mergeAttrs(present.attrs, newAttrs);
|
|
421
456
|
mergeElems(present.elems, newElems);
|
|
457
|
+
if (newWildcards.length > 0) {
|
|
458
|
+
present.wildcards = [...(present.wildcards ?? []), ...newWildcards];
|
|
459
|
+
}
|
|
422
460
|
if (!present.doc && typeDoc) {
|
|
423
461
|
present.doc = typeDoc;
|
|
424
462
|
}
|
|
@@ -453,6 +491,7 @@ export function compileCatalog(cat, options) {
|
|
|
453
491
|
// collect local additions/overrides
|
|
454
492
|
const locals = collectAttributes(node);
|
|
455
493
|
const localElems = collectParticles(outName, node);
|
|
494
|
+
const localWildcards = collectWildcards(node);
|
|
456
495
|
attrs.push(...locals);
|
|
457
496
|
elems.push(...localElems);
|
|
458
497
|
const result = {
|
|
@@ -463,7 +502,8 @@ export function compileCatalog(cat, options) {
|
|
|
463
502
|
doc: typeDoc,
|
|
464
503
|
base: baseName,
|
|
465
504
|
localAttrs: locals,
|
|
466
|
-
localElems
|
|
505
|
+
localElems,
|
|
506
|
+
...(localWildcards.length > 0 ? { wildcards: localWildcards } : {}),
|
|
467
507
|
};
|
|
468
508
|
compiledMap.set(key, result);
|
|
469
509
|
inProgress.delete(key);
|
|
@@ -506,7 +546,15 @@ export function compileCatalog(cat, options) {
|
|
|
506
546
|
// Attributes + particles
|
|
507
547
|
mergeAttrs(attrs, collectAttributes(cnode));
|
|
508
548
|
mergeElems(elems, collectParticles(outName, cnode));
|
|
509
|
-
const
|
|
549
|
+
const wildcards = collectWildcards(cnode);
|
|
550
|
+
const result = {
|
|
551
|
+
name: outName,
|
|
552
|
+
ns: schemaNS,
|
|
553
|
+
attrs,
|
|
554
|
+
elems,
|
|
555
|
+
doc: typeDoc,
|
|
556
|
+
...(wildcards.length > 0 ? { wildcards } : {}),
|
|
557
|
+
};
|
|
510
558
|
compiledMap.set(key, result);
|
|
511
559
|
inProgress.delete(rawKey);
|
|
512
560
|
return result;
|
|
@@ -585,6 +633,19 @@ export function compileCatalog(cat, options) {
|
|
|
585
633
|
const srec = simpleTypes.get(qkey(q));
|
|
586
634
|
if (srec) {
|
|
587
635
|
const a = getOrCompileAlias(q.local, srec.node, srec.tns, srec.prefixes);
|
|
636
|
+
if (outName === a.name) {
|
|
637
|
+
addDiagnosticNote(`Element {${schemaNS}}${name} reuses same-name simple type alias ${a.name} instead of emitting a wrapper interface.`);
|
|
638
|
+
if (!a.doc && elementDoc) {
|
|
639
|
+
a.doc = elementDoc;
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
name: a.name,
|
|
643
|
+
ns: schemaNS,
|
|
644
|
+
attrs: [],
|
|
645
|
+
elems: [],
|
|
646
|
+
doc: elementDoc,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
588
649
|
const t = {
|
|
589
650
|
name: outName,
|
|
590
651
|
ns: schemaNS,
|
|
@@ -812,7 +873,7 @@ export function compileCatalog(cat, options) {
|
|
|
812
873
|
.filter((x) => x != null)
|
|
813
874
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
814
875
|
// build operations list
|
|
815
|
-
const ops =
|
|
876
|
+
const ops = pOps
|
|
816
877
|
.map(po => {
|
|
817
878
|
const name = po?.["@_name"];
|
|
818
879
|
if (!name)
|
|
@@ -825,9 +886,18 @@ export function compileCatalog(cat, options) {
|
|
|
825
886
|
// Derive TypeScript type names from element local names
|
|
826
887
|
const inputTypeName = inputElement ? pascal(inputElement.local) : undefined;
|
|
827
888
|
const outputTypeName = outputElement ? pascal(outputElement.local) : undefined;
|
|
828
|
-
|
|
889
|
+
const op = {
|
|
890
|
+
name,
|
|
891
|
+
soapAction: bOps.get(name) || "",
|
|
892
|
+
...(inputElement ? { inputElement } : {}),
|
|
893
|
+
...(outputElement ? { outputElement } : {}),
|
|
894
|
+
...(inputTypeName ? { inputTypeName } : {}),
|
|
895
|
+
...(outputTypeName ? { outputTypeName } : {}),
|
|
896
|
+
...(doc ? { doc } : {}),
|
|
897
|
+
};
|
|
898
|
+
return op;
|
|
829
899
|
})
|
|
830
|
-
.filter((x) => x != null)
|
|
900
|
+
.filter((x) => x != null);
|
|
831
901
|
// --- WS-Policy: scan for security requirements (inline policies only) ---
|
|
832
902
|
const bindingPolicies = getChildrenWithLocalName(soapBinding || {}, "Policy");
|
|
833
903
|
const bindingSec = collectSecurityFromPolicyNodes(bindingPolicies);
|
|
@@ -838,6 +908,31 @@ export function compileCatalog(cat, options) {
|
|
|
838
908
|
const secSet = new Set([...bindingSec, ...opSec]);
|
|
839
909
|
op.security = Array.from(secSet);
|
|
840
910
|
}
|
|
911
|
+
// --- Stream config application ---
|
|
912
|
+
// A stream-configured operation is matched by name against the WSDL's
|
|
913
|
+
// portType operations. Unknown operation names are fatal so that consumers
|
|
914
|
+
// find out about stale configs at generation time, not at runtime.
|
|
915
|
+
if (streamConfig) {
|
|
916
|
+
const opByName = new Map(ops.map((op) => [op.name, op]));
|
|
917
|
+
for (const [opName, meta] of Object.entries(streamConfig.operations)) {
|
|
918
|
+
const op = opByName.get(opName);
|
|
919
|
+
if (!op) {
|
|
920
|
+
throw new WsdlCompilationError(`Stream config references operation "${opName}" but the WSDL portType does not declare it.`, {
|
|
921
|
+
element: opName,
|
|
922
|
+
suggestion: `Remove the entry under "operations.${opName}" in the stream config, or correct the operation name to match one defined in the WSDL.`,
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
op.stream = {
|
|
926
|
+
mode: meta.mode,
|
|
927
|
+
format: meta.format,
|
|
928
|
+
mediaType: meta.mediaType,
|
|
929
|
+
recordPath: [...meta.recordPath],
|
|
930
|
+
recordTypeName: meta.recordTypeName,
|
|
931
|
+
...(meta.shapeCatalogName ? { shapeCatalogName: meta.shapeCatalogName } : {}),
|
|
932
|
+
...(op.outputTypeName ? { sourceOutputTypeName: op.outputTypeName } : {}),
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
}
|
|
841
936
|
// --- Service discovery (for client naming) ---
|
|
842
937
|
let serviceName;
|
|
843
938
|
const soapBindingName = soapBinding?.["@_name"];
|
|
@@ -869,5 +964,6 @@ export function compileCatalog(cat, options) {
|
|
|
869
964
|
serviceName,
|
|
870
965
|
wsdlUri: cat.wsdlUri,
|
|
871
966
|
...(wsdlDocs ? { wsdlDocs } : {}),
|
|
967
|
+
...(diagnostics.notes?.length ? { diagnostics } : {}),
|
|
872
968
|
};
|
|
873
969
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type CompiledCatalog } from "./schemaCompiler.js";
|
|
2
|
+
import type { StreamConfig } from "../util/streamConfig.js";
|
|
3
|
+
export interface ApplyShapeCatalogsOptions {
|
|
4
|
+
/** Directory against which relative `catalogFile`/`wsdlSource` paths resolve. Defaults to process.cwd(). */
|
|
5
|
+
baseDir?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Apply a parsed StreamConfig to a compiled catalog:
|
|
9
|
+
* 1. Verify every opted-in operation's record type is resolvable.
|
|
10
|
+
* 2. For each operation that names a shapeCatalog, load the companion
|
|
11
|
+
* catalog (once, cached) and copy the reachable record-type graph.
|
|
12
|
+
*
|
|
13
|
+
* Mutates `compiled` in place. Safe to call with a StreamConfig that has
|
|
14
|
+
* zero shapeCatalogs — it will still validate record-type presence against
|
|
15
|
+
* the current catalog.
|
|
16
|
+
*/
|
|
17
|
+
export declare function applyShapeCatalogs(compiled: CompiledCatalog, streamConfig: StreamConfig, options?: ApplyShapeCatalogsOptions): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=shapeResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shapeResolver.d.ts","sourceRoot":"","sources":["../../src/compiler/shapeResolver.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAqC,KAAK,eAAe,EAAoB,MAAM,qBAAqB,CAAC;AAGhH,OAAO,KAAK,EAAkB,YAAY,EAAC,MAAM,yBAAyB,CAAC;AAG3E,MAAM,WAAW,yBAAyB;IACxC,4GAA4G;IAC5G,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,eAAe,EACzB,YAAY,EAAE,YAAY,EAC1B,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,IAAI,CAAC,CAoDf"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Companion-catalog shape resolver (phase-2 of ADR-002).
|
|
3
|
+
*
|
|
4
|
+
* For stream operations whose record types live in a different WSDL (e.g.
|
|
5
|
+
* Escapia's main EVRN service provides the concrete UnitDescriptiveContentType
|
|
6
|
+
* while the content-service WSDL only exposes an xs:any-wrapped envelope),
|
|
7
|
+
* load the companion catalog and copy the reachable record-type graph into
|
|
8
|
+
* the current catalog.
|
|
9
|
+
*
|
|
10
|
+
* Invariants:
|
|
11
|
+
* - A name collision against a type already in the current catalog is only
|
|
12
|
+
* allowed when the two types are *structurally identical* — otherwise we
|
|
13
|
+
* fail the build rather than silently rename public API types.
|
|
14
|
+
* - Existing buffered generation stays byte-for-byte identical when no
|
|
15
|
+
* streamConfig.shapeCatalogs entries are declared.
|
|
16
|
+
*/
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { compileCatalog } from "./schemaCompiler.js";
|
|
20
|
+
import { loadWsdl } from "../loader/wsdlLoader.js";
|
|
21
|
+
import { resolveCompilerOptions } from "../config.js";
|
|
22
|
+
import { WsdlCompilationError } from "../util/errors.js";
|
|
23
|
+
/**
|
|
24
|
+
* Apply a parsed StreamConfig to a compiled catalog:
|
|
25
|
+
* 1. Verify every opted-in operation's record type is resolvable.
|
|
26
|
+
* 2. For each operation that names a shapeCatalog, load the companion
|
|
27
|
+
* catalog (once, cached) and copy the reachable record-type graph.
|
|
28
|
+
*
|
|
29
|
+
* Mutates `compiled` in place. Safe to call with a StreamConfig that has
|
|
30
|
+
* zero shapeCatalogs — it will still validate record-type presence against
|
|
31
|
+
* the current catalog.
|
|
32
|
+
*/
|
|
33
|
+
export async function applyShapeCatalogs(compiled, streamConfig, options = {}) {
|
|
34
|
+
const baseDir = options.baseDir ?? process.cwd();
|
|
35
|
+
// Load each declared shape catalog once, on-demand. Shape catalogs declared
|
|
36
|
+
// but never referenced are harmless — we only load the ones an operation
|
|
37
|
+
// actually uses.
|
|
38
|
+
const referencedCatalogs = new Set();
|
|
39
|
+
for (const meta of Object.values(streamConfig.operations)) {
|
|
40
|
+
if (meta.shapeCatalogName)
|
|
41
|
+
referencedCatalogs.add(meta.shapeCatalogName);
|
|
42
|
+
}
|
|
43
|
+
const companionByName = new Map();
|
|
44
|
+
for (const name of referencedCatalogs) {
|
|
45
|
+
const ref = streamConfig.shapeCatalogs[name];
|
|
46
|
+
if (!ref) {
|
|
47
|
+
throw new WsdlCompilationError(`Stream config references shape catalog "${name}" that is not declared under "shapeCatalogs".`, {
|
|
48
|
+
suggestion: `Add a "shapeCatalogs.${name}" entry pointing to a wsdlSource or catalogFile, or remove the reference.`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
companionByName.set(name, await loadCompanionCatalog(name, ref, baseDir, compiled));
|
|
52
|
+
}
|
|
53
|
+
for (const [opName, meta] of Object.entries(streamConfig.operations)) {
|
|
54
|
+
if (meta.shapeCatalogName) {
|
|
55
|
+
const companion = companionByName.get(meta.shapeCatalogName);
|
|
56
|
+
if (!companion) {
|
|
57
|
+
// Guarded by the loop above; this path is defensive.
|
|
58
|
+
throw new WsdlCompilationError(`Stream config for "${opName}" references shape catalog "${meta.shapeCatalogName}" which failed to load.`);
|
|
59
|
+
}
|
|
60
|
+
copyReachableGraph(compiled, companion, meta.recordTypeName, {
|
|
61
|
+
opName,
|
|
62
|
+
shapeCatalog: meta.shapeCatalogName,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// No companion: the record type must already live in the main catalog.
|
|
67
|
+
if (!hasNamedType(compiled, meta.recordTypeName)) {
|
|
68
|
+
throw new WsdlCompilationError(`Stream config for operation "${opName}" references record type "${meta.recordTypeName}" which is not present in the compiled catalog.`, {
|
|
69
|
+
element: meta.recordTypeName,
|
|
70
|
+
suggestion: `Either declare a "shapeCatalogs.<name>" entry and set shapeCatalog on the operation, or correct the recordType to a type defined in the WSDL.`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function loadCompanionCatalog(name, ref, baseDir, compiled) {
|
|
77
|
+
if (ref.catalogFile) {
|
|
78
|
+
const abs = path.resolve(baseDir, ref.catalogFile);
|
|
79
|
+
let text;
|
|
80
|
+
try {
|
|
81
|
+
text = fs.readFileSync(abs, "utf-8");
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
throw new WsdlCompilationError(`Failed to read companion catalog "${name}" at ${abs}: ${err.message}`, { suggestion: `Check that shapeCatalogs.${name}.catalogFile points to an existing catalog.json.` });
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(text);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
throw new WsdlCompilationError(`Companion catalog "${name}" at ${abs} is not valid JSON: ${err.message}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (ref.wsdlSource) {
|
|
94
|
+
// Relative paths resolve against baseDir; URLs pass through untouched.
|
|
95
|
+
const src = isLikelyUrl(ref.wsdlSource) ? ref.wsdlSource : path.resolve(baseDir, ref.wsdlSource);
|
|
96
|
+
const wsdlCatalog = await loadWsdl(src);
|
|
97
|
+
// Reuse the current catalog's compiler options so primitives / attrs keys
|
|
98
|
+
// / choice handling stay consistent with the main generation.
|
|
99
|
+
const companionOptions = resolveCompilerOptions({ ...compiled.options, catalog: false }, { wsdl: src, out: baseDir });
|
|
100
|
+
return compileCatalog(wsdlCatalog, companionOptions);
|
|
101
|
+
}
|
|
102
|
+
throw new WsdlCompilationError(`Shape catalog "${name}" declares neither wsdlSource nor catalogFile.`);
|
|
103
|
+
}
|
|
104
|
+
function isLikelyUrl(s) {
|
|
105
|
+
return /^[a-z][a-z0-9+.-]*:\/\//i.test(s);
|
|
106
|
+
}
|
|
107
|
+
function hasNamedType(cat, name) {
|
|
108
|
+
return cat.types.some((t) => t.name === name) || cat.aliases.some((a) => a.name === name);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Walk the reachable type graph from `rootTypeName` within the companion
|
|
112
|
+
* catalog and copy each reachable entry into `dst`. Types with the same name
|
|
113
|
+
* already present in `dst` are checked for structural equality; any divergence
|
|
114
|
+
* is fatal.
|
|
115
|
+
*/
|
|
116
|
+
function copyReachableGraph(dst, src, rootTypeName, context) {
|
|
117
|
+
const srcTypes = new Map(src.types.map((t) => [t.name, t]));
|
|
118
|
+
const srcAliases = new Map(src.aliases.map((a) => [a.name, a]));
|
|
119
|
+
if (!srcTypes.has(rootTypeName) && !srcAliases.has(rootTypeName)) {
|
|
120
|
+
throw new WsdlCompilationError(`Stream config for operation "${context.opName}" references record type "${rootTypeName}" but it is not present in shape catalog "${context.shapeCatalog}".`, {
|
|
121
|
+
element: rootTypeName,
|
|
122
|
+
suggestion: `Check the companion catalog or correct the recordType.`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const known = new Set([...srcTypes.keys(), ...srcAliases.keys()]);
|
|
126
|
+
const queue = [rootTypeName];
|
|
127
|
+
const visited = new Set();
|
|
128
|
+
while (queue.length > 0) {
|
|
129
|
+
const typeName = queue.shift();
|
|
130
|
+
if (visited.has(typeName))
|
|
131
|
+
continue;
|
|
132
|
+
visited.add(typeName);
|
|
133
|
+
const srcType = srcTypes.get(typeName);
|
|
134
|
+
if (srcType) {
|
|
135
|
+
mergeType(dst, src, srcType, context);
|
|
136
|
+
// Enqueue referenced names from elems, attrs, base.
|
|
137
|
+
for (const e of srcType.elems ?? []) {
|
|
138
|
+
for (const ref of extractReferencedNames(e.tsType, known))
|
|
139
|
+
queue.push(ref);
|
|
140
|
+
}
|
|
141
|
+
for (const a of srcType.attrs ?? []) {
|
|
142
|
+
for (const ref of extractReferencedNames(a.tsType, known))
|
|
143
|
+
queue.push(ref);
|
|
144
|
+
}
|
|
145
|
+
if (srcType.base && known.has(srcType.base))
|
|
146
|
+
queue.push(srcType.base);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const srcAlias = srcAliases.get(typeName);
|
|
150
|
+
if (srcAlias) {
|
|
151
|
+
mergeAlias(dst, src, srcAlias, context);
|
|
152
|
+
for (const ref of extractReferencedNames(srcAlias.tsType, known))
|
|
153
|
+
queue.push(ref);
|
|
154
|
+
}
|
|
155
|
+
// Not in companion; silently skip — either a primitive or a type that
|
|
156
|
+
// the main catalog is expected to own.
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function mergeType(dst, src, srcType, context) {
|
|
160
|
+
const existingIdx = dst.types.findIndex((t) => t.name === srcType.name);
|
|
161
|
+
if (existingIdx < 0) {
|
|
162
|
+
dst.types.push({ ...srcType });
|
|
163
|
+
copyTypeMeta(dst, src, srcType.name);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const existing = dst.types[existingIdx];
|
|
167
|
+
if (!structurallyEqualType(existing, srcType)) {
|
|
168
|
+
throw new WsdlCompilationError(`Companion catalog "${context.shapeCatalog}" declares type "${srcType.name}" that conflicts structurally with the current catalog.`, {
|
|
169
|
+
element: srcType.name,
|
|
170
|
+
suggestion: `Rename one of the conflicting types in the source WSDL, or resolve the conflict before streaming. wsdl-tsc refuses to silently rename public API types.`,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// Structurally identical — keep the existing entry (and its meta) as-is.
|
|
174
|
+
}
|
|
175
|
+
function mergeAlias(dst, src, srcAlias, context) {
|
|
176
|
+
const existingIdx = dst.aliases.findIndex((a) => a.name === srcAlias.name);
|
|
177
|
+
if (existingIdx < 0) {
|
|
178
|
+
dst.aliases.push({ ...srcAlias });
|
|
179
|
+
// Aliases that point at a complex type get meta synonyms in compileCatalog;
|
|
180
|
+
// the main catalog already owns that path for its own types, so we only
|
|
181
|
+
// need to add meta if the companion aliases a complex type whose type we
|
|
182
|
+
// also just copied.
|
|
183
|
+
copyAliasSynonymMeta(dst, src, srcAlias.name);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const existing = dst.aliases[existingIdx];
|
|
187
|
+
if (!structurallyEqualAlias(existing, srcAlias)) {
|
|
188
|
+
throw new WsdlCompilationError(`Companion catalog "${context.shapeCatalog}" declares alias "${srcAlias.name}" that conflicts structurally with the current catalog.`, {
|
|
189
|
+
element: srcAlias.name,
|
|
190
|
+
suggestion: `Resolve the conflicting type name before streaming. wsdl-tsc refuses to silently rename public API types.`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function copyTypeMeta(dst, src, typeName) {
|
|
195
|
+
if (typeName in src.meta.attrSpec)
|
|
196
|
+
dst.meta.attrSpec[typeName] = [...src.meta.attrSpec[typeName]];
|
|
197
|
+
if (typeName in src.meta.attrType)
|
|
198
|
+
dst.meta.attrType[typeName] = { ...src.meta.attrType[typeName] };
|
|
199
|
+
if (typeName in src.meta.childType)
|
|
200
|
+
dst.meta.childType[typeName] = { ...src.meta.childType[typeName] };
|
|
201
|
+
if (typeName in src.meta.propMeta)
|
|
202
|
+
dst.meta.propMeta[typeName] = { ...src.meta.propMeta[typeName] };
|
|
203
|
+
}
|
|
204
|
+
function copyAliasSynonymMeta(dst, src, aliasName) {
|
|
205
|
+
// Phase-2 MVP: only copy meta for aliases whose name already keys into the
|
|
206
|
+
// source's meta maps. Aliases that are just simple-type renames don't need
|
|
207
|
+
// child/attr meta at all.
|
|
208
|
+
copyTypeMeta(dst, src, aliasName);
|
|
209
|
+
}
|
|
210
|
+
const TS_BUILTINS = new Set([
|
|
211
|
+
"string", "number", "boolean", "bigint", "null", "undefined", "void", "any", "unknown", "never",
|
|
212
|
+
"Date", "Array", "Record", "Map", "Set", "Promise", "Buffer", "Object", "Function",
|
|
213
|
+
]);
|
|
214
|
+
/**
|
|
215
|
+
* Extract PascalCase identifiers from a TypeScript type expression that are
|
|
216
|
+
* known to the companion catalog. Deliberately cheap — matches identifiers
|
|
217
|
+
* starting with an uppercase letter, skips TS built-ins and anything not
|
|
218
|
+
* present in the companion's known-names set.
|
|
219
|
+
*/
|
|
220
|
+
function extractReferencedNames(tsType, known) {
|
|
221
|
+
if (!tsType)
|
|
222
|
+
return [];
|
|
223
|
+
const out = [];
|
|
224
|
+
// Identifiers starting with an uppercase letter; bare word boundaries.
|
|
225
|
+
for (const m of tsType.matchAll(/\b[A-Z][A-Za-z0-9_]*\b/g)) {
|
|
226
|
+
const name = m[0];
|
|
227
|
+
if (TS_BUILTINS.has(name))
|
|
228
|
+
continue;
|
|
229
|
+
if (known.has(name))
|
|
230
|
+
out.push(name);
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function structurallyEqualType(a, b) {
|
|
235
|
+
return canonicalizeType(a) === canonicalizeType(b);
|
|
236
|
+
}
|
|
237
|
+
function structurallyEqualAlias(a, b) {
|
|
238
|
+
return canonicalizeAlias(a) === canonicalizeAlias(b);
|
|
239
|
+
}
|
|
240
|
+
function canonicalizeType(t) {
|
|
241
|
+
// Drop fields that legitimately differ between catalogs (ns, docs) and
|
|
242
|
+
// stabilize ordering. Stream metadata is on operations, not on types.
|
|
243
|
+
return JSON.stringify({
|
|
244
|
+
name: t.name,
|
|
245
|
+
attrs: (t.attrs ?? []).map(canonicalizeAttr),
|
|
246
|
+
elems: (t.elems ?? []).map(canonicalizeElem),
|
|
247
|
+
base: t.base ?? null,
|
|
248
|
+
wildcards: (t.wildcards ?? []).map((w) => ({
|
|
249
|
+
min: w.min,
|
|
250
|
+
max: w.max,
|
|
251
|
+
namespace: w.namespace ?? null,
|
|
252
|
+
processContents: w.processContents ?? null,
|
|
253
|
+
})),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function canonicalizeAlias(a) {
|
|
257
|
+
return JSON.stringify({
|
|
258
|
+
name: a.name,
|
|
259
|
+
tsType: a.tsType,
|
|
260
|
+
declared: a.declared,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function canonicalizeAttr(a) {
|
|
264
|
+
return {
|
|
265
|
+
name: a.name,
|
|
266
|
+
tsType: a.tsType,
|
|
267
|
+
use: a.use ?? "optional",
|
|
268
|
+
declaredType: a.declaredType,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function canonicalizeElem(e) {
|
|
272
|
+
return {
|
|
273
|
+
name: e.name,
|
|
274
|
+
tsType: e.tsType,
|
|
275
|
+
min: e.min,
|
|
276
|
+
max: e.max,
|
|
277
|
+
nillable: !!e.nillable,
|
|
278
|
+
declaredType: e.declaredType,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAE/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"generateGateway.d.ts","sourceRoot":"","sources":["../../src/gateway/generateGateway.ts"],"names":[],"mappings":"AA6CA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAE/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8JjF"}
|
|
@@ -183,7 +183,8 @@ export async function generateGateway(opts) {
|
|
|
183
183
|
}
|
|
184
184
|
// Step 6: Emit runtime.ts (if enabled)
|
|
185
185
|
if (emitRuntime) {
|
|
186
|
-
|
|
186
|
+
const hasStreams = operations.some((o) => !!o.stream);
|
|
187
|
+
emitRuntimeModule(outDir, versionSlug, serviceSlug, shouldUnwrap ? catalog : undefined, { hasStreams });
|
|
187
188
|
}
|
|
188
189
|
// Step 7: Emit plugin.ts and type-check fixture (if enabled)
|
|
189
190
|
if (emitPlugin) {
|
|
@@ -40,6 +40,16 @@ export interface OperationMetadata {
|
|
|
40
40
|
description?: string;
|
|
41
41
|
/** When true, response schema is omitted from route registration to avoid fast-json-stringify stack overflow on deeply nested $ref graphs */
|
|
42
42
|
skipResponseSchema?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Stream metadata populated from the OpenAPI `x-wsdl-tsc-stream` extension.
|
|
45
|
+
* When present, the route handler pipes `result.records` through the NDJSON
|
|
46
|
+
* helper instead of envelope-wrapping a single response object.
|
|
47
|
+
*/
|
|
48
|
+
stream?: {
|
|
49
|
+
mediaType: string;
|
|
50
|
+
format: "ndjson" | "json-array";
|
|
51
|
+
recordTypeName?: string;
|
|
52
|
+
};
|
|
43
53
|
}
|
|
44
54
|
/**
|
|
45
55
|
* Emits Fastify-compatible operation schema files
|
|
@@ -107,7 +117,9 @@ export declare function emitSchemasModule(outDir: string, modelsDir: string, ver
|
|
|
107
117
|
* @param {"js"|"ts"|"bare"} importsMode - Import-extension mode for generated TypeScript modules
|
|
108
118
|
*/
|
|
109
119
|
export declare function emitRouteFiles(outDir: string, routesDir: string, versionSlug: string, serviceSlug: string, operations: OperationMetadata[], importsMode: "js" | "ts" | "bare"): void;
|
|
110
|
-
export declare function emitRuntimeModule(outDir: string, versionSlug: string, serviceSlug: string, catalog?: any
|
|
120
|
+
export declare function emitRuntimeModule(outDir: string, versionSlug: string, serviceSlug: string, catalog?: any, opts?: {
|
|
121
|
+
hasStreams?: boolean;
|
|
122
|
+
}): void;
|
|
111
123
|
/**
|
|
112
124
|
* Emits plugin.ts module as the primary Fastify plugin wrapper
|
|
113
125
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"AAcA,OAAO,EAAC,KAAK,UAAU,EAAE,KAAK,eAAe,EAAyE,MAAM,cAAc,CAAC;AAG3I;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6BxB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6IAA6I;IAC7I,kBAAkB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"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"}
|