@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
|
@@ -148,11 +148,19 @@ export function emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, sche
|
|
|
148
148
|
// Build response schemas
|
|
149
149
|
const responses = operation.responses || {};
|
|
150
150
|
const responseObj = {};
|
|
151
|
+
// Detect streaming operation: OpenAPI emits a non-JSON content entry with
|
|
152
|
+
// an `x-wsdl-tsc-stream` extension on the 200 response.
|
|
153
|
+
const streamEntry = detectStreamResponse(responses["200"]);
|
|
151
154
|
const explicitCodes = Object.keys(responses).filter(isNumeric);
|
|
152
155
|
for (const code of explicitCodes) {
|
|
153
156
|
const r = responses[code];
|
|
154
157
|
if (!r || typeof r !== "object")
|
|
155
158
|
continue;
|
|
159
|
+
// Stream ops intentionally skip JSON response-schema construction on the
|
|
160
|
+
// streamed status code; the body is an NDJSON stream, not a single
|
|
161
|
+
// JSON object, so fast-json-stringify cannot serialize it anyway.
|
|
162
|
+
if (streamEntry && code === "200")
|
|
163
|
+
continue;
|
|
156
164
|
const rSchema = r.content?.["application/json"]?.schema;
|
|
157
165
|
if (!rSchema) {
|
|
158
166
|
throw new Error(`Response ${code} for operation '${operationId}' is missing application/json schema`);
|
|
@@ -222,12 +230,38 @@ export function emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, sche
|
|
|
222
230
|
path: p,
|
|
223
231
|
summary: normalizeDocCommentText(typeof operation.summary === "string" ? operation.summary : undefined),
|
|
224
232
|
description: normalizeDocCommentText(typeof operation.description === "string" ? operation.description : undefined),
|
|
225
|
-
|
|
233
|
+
// Stream ops always skip response schema because NDJSON is not a single
|
|
234
|
+
// JSON document and fast-json-stringify can't serialize it.
|
|
235
|
+
skipResponseSchema: skipResponseSchema || !!streamEntry,
|
|
236
|
+
...(streamEntry ? { stream: streamEntry } : {}),
|
|
226
237
|
});
|
|
227
238
|
}
|
|
228
239
|
}
|
|
229
240
|
return operations;
|
|
230
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Look for an `x-wsdl-tsc-stream` extension on the 200 response's content map.
|
|
244
|
+
* Returns the normalized stream metadata when found, or undefined otherwise.
|
|
245
|
+
*/
|
|
246
|
+
function detectStreamResponse(response200) {
|
|
247
|
+
const content = response200?.content;
|
|
248
|
+
if (!content || typeof content !== "object")
|
|
249
|
+
return undefined;
|
|
250
|
+
for (const [mediaType, entry] of Object.entries(content)) {
|
|
251
|
+
const ext = entry && typeof entry === "object" ? entry["x-wsdl-tsc-stream"] : undefined;
|
|
252
|
+
if (!ext || typeof ext !== "object")
|
|
253
|
+
continue;
|
|
254
|
+
const format = ext.format;
|
|
255
|
+
if (format !== "ndjson" && format !== "json-array")
|
|
256
|
+
continue;
|
|
257
|
+
const itemRef = ext.itemSchema?.$ref;
|
|
258
|
+
const recordTypeName = typeof itemRef === "string"
|
|
259
|
+
? itemRef.split("/").pop()
|
|
260
|
+
: undefined;
|
|
261
|
+
return { mediaType, format, recordTypeName };
|
|
262
|
+
}
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
231
265
|
/**
|
|
232
266
|
* Emits schemas.ts module that registers all model schemas with Fastify
|
|
233
267
|
*
|
|
@@ -361,7 +395,7 @@ import type { FastifyInstance } from "fastify";
|
|
|
361
395
|
function detectArrayWrappers(catalog) {
|
|
362
396
|
return detectArrayWrappersShared(catalog.types || []);
|
|
363
397
|
}
|
|
364
|
-
export function emitRuntimeModule(outDir, versionSlug, serviceSlug, catalog) {
|
|
398
|
+
export function emitRuntimeModule(outDir, versionSlug, serviceSlug, catalog, opts) {
|
|
365
399
|
const vSlug = slugName(versionSlug);
|
|
366
400
|
const sSlug = slugName(serviceSlug);
|
|
367
401
|
// Build unwrap maps from catalog if provided
|
|
@@ -602,7 +636,43 @@ export function createGatewayErrorHandler_${vSlug}_${sSlug}() {
|
|
|
602
636
|
};
|
|
603
637
|
}
|
|
604
638
|
`;
|
|
605
|
-
|
|
639
|
+
const streamSection = opts?.hasStreams ? buildStreamRuntimeSection() : "";
|
|
640
|
+
fs.writeFileSync(path.join(outDir, "runtime.ts"), runtimeTs + unwrapSection + streamSection, "utf8");
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Returns the streaming helpers block for runtime.ts.
|
|
644
|
+
*
|
|
645
|
+
* The emitted `toNdjson` mirrors the reference implementation in
|
|
646
|
+
* src/runtime/ndjson.ts but is inlined here to avoid a cross-package import
|
|
647
|
+
* from the generated gateway (which would require wsdl-tsc to be a runtime
|
|
648
|
+
* dependency of the consumer's project).
|
|
649
|
+
*/
|
|
650
|
+
function buildStreamRuntimeSection() {
|
|
651
|
+
return `
|
|
652
|
+
// -----------------------------------------------------------------------------
|
|
653
|
+
// Streaming helpers (emitted because at least one operation is stream-configured).
|
|
654
|
+
// -----------------------------------------------------------------------------
|
|
655
|
+
|
|
656
|
+
import { Readable } from "node:stream";
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Wrap an async iterable of records in a Node Readable stream that emits
|
|
660
|
+
* NDJSON (one JSON document per line, LF-terminated). Downstream backpressure
|
|
661
|
+
* is honored by Readable.from's default iterator-pausing behavior. Source
|
|
662
|
+
* errors are forwarded to the returned stream's 'error' event — before-first-
|
|
663
|
+
* byte errors fire before any push(), so Fastify translates them into the
|
|
664
|
+
* standard JSON error envelope.
|
|
665
|
+
*/
|
|
666
|
+
export function toNdjson<T>(records: AsyncIterable<T>): Readable {
|
|
667
|
+
return Readable.from(encodeNdjson(records), { objectMode: false, encoding: "utf-8" });
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
async function* encodeNdjson<T>(records: AsyncIterable<T>): AsyncIterable<string> {
|
|
671
|
+
for await (const record of records) {
|
|
672
|
+
yield JSON.stringify(record) + "\\n";
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
`;
|
|
606
676
|
}
|
|
607
677
|
/**
|
|
608
678
|
* Emits plugin.ts module as the primary Fastify plugin wrapper
|
|
@@ -799,25 +869,44 @@ export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, servi
|
|
|
799
869
|
? `request.body as ${reqTypeName}`
|
|
800
870
|
: "request.body";
|
|
801
871
|
// Build the runtime import and return expression based on unwrap availability
|
|
802
|
-
const runtimeImport =
|
|
803
|
-
? `import {
|
|
804
|
-
:
|
|
872
|
+
const runtimeImport = op.stream
|
|
873
|
+
? `import { toNdjson } from "../runtime${suffix}";`
|
|
874
|
+
: hasUnwrap
|
|
875
|
+
? `import { buildSuccessEnvelope, unwrapArrayWrappers } from "../runtime${suffix}";`
|
|
876
|
+
: `import { buildSuccessEnvelope } from "../runtime${suffix}";`;
|
|
805
877
|
const returnExpr = hasUnwrap
|
|
806
878
|
? `return buildSuccessEnvelope(unwrapArrayWrappers(result.response, "${resTypeName}"));`
|
|
807
879
|
: `return buildSuccessEnvelope(result.response);`;
|
|
808
880
|
// Note: op.path comes from OpenAPI and already includes any base path
|
|
809
881
|
const schemaBinding = op.skipResponseSchema
|
|
810
|
-
? `\n// Response schema omitted: $
|
|
882
|
+
? `\n// Response schema omitted: ${op.stream
|
|
883
|
+
? "stream operations emit NDJSON, not a single JSON object"
|
|
884
|
+
: "$ref graph exceeds fast-json-stringify depth limit"}\nconst { response: _response, ...routeSchema } = schema as Record<string, unknown>;\n`
|
|
811
885
|
: "";
|
|
812
886
|
const schemaLine = op.skipResponseSchema
|
|
813
887
|
? " schema: routeSchema,"
|
|
814
888
|
: " schema,";
|
|
815
889
|
const operationDocLines = buildOperationDocLines(op);
|
|
890
|
+
const handlerBody = op.stream
|
|
891
|
+
? ` handler: async (request, reply) => {
|
|
892
|
+
const client = fastify.${clientMeta.decoratorName};
|
|
893
|
+
const result = await client.${clientMethod}(${bodyArg});
|
|
894
|
+
reply.type(${JSON.stringify(op.stream.mediaType)});
|
|
895
|
+
return reply.send(toNdjson(result.records));
|
|
896
|
+
},`
|
|
897
|
+
: ` handler: async (request) => {
|
|
898
|
+
const client = fastify.${clientMeta.decoratorName};
|
|
899
|
+
const result = await client.${clientMethod}(${bodyArg});
|
|
900
|
+
${returnExpr}
|
|
901
|
+
},`;
|
|
902
|
+
const responseTypeDoc = op.stream
|
|
903
|
+
? `${op.stream.recordTypeName ?? "records"} (streamed as ${op.stream.format})`
|
|
904
|
+
: `${resTypeName} (wrapped in envelope)`;
|
|
816
905
|
let routeTs = `/**
|
|
817
906
|
* Route: ${op.method.toUpperCase()} ${op.path}
|
|
818
907
|
* Operation: ${op.operationId || op.operationSlug}
|
|
819
908
|
${operationDocLines} * Request Type: ${reqTypeName}
|
|
820
|
-
* Response Type: ${
|
|
909
|
+
* Response Type: ${responseTypeDoc}
|
|
821
910
|
* Auto-generated - do not edit manually.
|
|
822
911
|
*/
|
|
823
912
|
import type { FastifyInstance } from "fastify";
|
|
@@ -829,11 +918,7 @@ export async function ${fnName}(fastify: FastifyInstance) {
|
|
|
829
918
|
method: "${op.method.toUpperCase()}",
|
|
830
919
|
url: "${op.path}",
|
|
831
920
|
${schemaLine}
|
|
832
|
-
|
|
833
|
-
const client = fastify.${clientMeta.decoratorName};
|
|
834
|
-
const result = await client.${clientMethod}(${bodyArg});
|
|
835
|
-
${returnExpr}
|
|
836
|
-
},
|
|
921
|
+
${handlerBody}
|
|
837
922
|
});
|
|
838
923
|
}
|
|
839
924
|
`;
|
|
@@ -176,6 +176,16 @@ export interface ResolvedOperationMeta {
|
|
|
176
176
|
responseTypeName?: string;
|
|
177
177
|
summary?: string;
|
|
178
178
|
description?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Populated for operations that opt into streaming via stream-config. Drives
|
|
181
|
+
* mock-client emission (async-iterable records) and test-payload shape.
|
|
182
|
+
*/
|
|
183
|
+
stream?: {
|
|
184
|
+
format: "ndjson" | "json-array";
|
|
185
|
+
mediaType: string;
|
|
186
|
+
recordTypeName: string;
|
|
187
|
+
recordPath: string[];
|
|
188
|
+
};
|
|
179
189
|
}
|
|
180
190
|
/**
|
|
181
191
|
* Options for resolving client metadata
|
|
@@ -226,6 +236,12 @@ export declare function resolveOperationMeta(operationId: string, operationSlug:
|
|
|
226
236
|
summary?: string;
|
|
227
237
|
description?: string;
|
|
228
238
|
doc?: string;
|
|
239
|
+
stream?: {
|
|
240
|
+
format: "ndjson" | "json-array";
|
|
241
|
+
mediaType: string;
|
|
242
|
+
recordTypeName: string;
|
|
243
|
+
recordPath: string[];
|
|
244
|
+
};
|
|
229
245
|
}>): ResolvedOperationMeta;
|
|
230
246
|
/**
|
|
231
247
|
* Measures the $ref graph complexity of a schema component.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAClC,CAAC;CACH;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,GAAG,CAqBxF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,GAAG,EACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,GAC/B,GAAG,CA4GL;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAcxD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,GAAG,GAAG,CAiB3E;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,aAAa,CAAC,EAAE,GAAG,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,GAAG,EACb,SAAS,EAAE,GAAG,EACd,GAAG,EAAE,eAAe,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,sBAAsB,CAmFxB;AAED;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway/helpers.js
CHANGED
|
@@ -430,6 +430,7 @@ export function resolveOperationMeta(operationId, operationSlug, method, path, c
|
|
|
430
430
|
responseTypeName,
|
|
431
431
|
...(summary ? { summary } : {}),
|
|
432
432
|
...(description ? { description } : {}),
|
|
433
|
+
...(catalogOp?.stream ? { stream: catalogOp.stream } : {}),
|
|
433
434
|
};
|
|
434
435
|
}
|
|
435
436
|
function normalizeOperationText(value) {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
|
3
3
|
export { generateGateway } from "./gateway/generateGateway.js";
|
|
4
4
|
export { generateTests } from "./test/generateTests.js";
|
|
5
5
|
export { runGenerationPipeline } from "./pipeline.js";
|
|
6
|
+
export { loadStreamConfigFile, parseStreamConfig, StreamConfigError, type OperationStreamMetadata, type ShapeCatalogRef, type StreamConfig, } from "./util/streamConfig.js";
|
|
7
|
+
export { applyShapeCatalogs, type ApplyShapeCatalogsOptions } from "./compiler/shapeResolver.js";
|
|
6
8
|
/**
|
|
7
9
|
* Compiles a WSDL file to TypeScript client code
|
|
8
10
|
*
|
|
@@ -27,5 +29,9 @@ export declare function compileWsdlToProject(input: {
|
|
|
27
29
|
wsdl: string;
|
|
28
30
|
outDir: string;
|
|
29
31
|
options?: CompilerOptions;
|
|
32
|
+
/** Path to a stream configuration JSON file (ADR-002). Takes precedence over `streamConfig` when both are set. */
|
|
33
|
+
streamConfigFile?: string;
|
|
34
|
+
/** In-memory stream configuration. Ignored when `streamConfigFile` is set. */
|
|
35
|
+
streamConfig?: import("./util/streamConfig.js").StreamConfig;
|
|
30
36
|
}): Promise<void>;
|
|
31
37
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAWjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAWjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAC,kBAAkB,EAAE,KAAK,yBAAyB,EAAC,MAAM,6BAA6B,CAAC;AAG/F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,kHAAkH;IAClH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,OAAO,wBAAwB,EAAE,YAAY,CAAC;CAC9D,GACA,OAAO,CAAC,IAAI,CAAC,CA2Ef"}
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,8 @@ export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
|
26
26
|
export { generateGateway } from "./gateway/generateGateway.js";
|
|
27
27
|
export { generateTests } from "./test/generateTests.js";
|
|
28
28
|
export { runGenerationPipeline } from "./pipeline.js";
|
|
29
|
+
export { loadStreamConfigFile, parseStreamConfig, StreamConfigError, } from "./util/streamConfig.js";
|
|
30
|
+
export { applyShapeCatalogs } from "./compiler/shapeResolver.js";
|
|
29
31
|
// noinspection JSUnusedGlobalSymbols
|
|
30
32
|
/**
|
|
31
33
|
* Compiles a WSDL file to TypeScript client code
|
|
@@ -53,6 +55,11 @@ export async function compileWsdlToProject(input) {
|
|
|
53
55
|
wsdl: input.wsdl,
|
|
54
56
|
out: input.outDir,
|
|
55
57
|
});
|
|
58
|
+
// Resolve stream configuration.
|
|
59
|
+
const { loadStreamConfigFile } = await import("./util/streamConfig.js");
|
|
60
|
+
const streamConfig = input.streamConfigFile
|
|
61
|
+
? loadStreamConfigFile(input.streamConfigFile)
|
|
62
|
+
: input.streamConfig;
|
|
56
63
|
// Load & compile
|
|
57
64
|
const wsdlCatalog = await loadWsdl(input.wsdl);
|
|
58
65
|
info(`Loaded WSDL: ${wsdlCatalog.wsdlUri}`);
|
|
@@ -60,14 +67,26 @@ export async function compileWsdlToProject(input) {
|
|
|
60
67
|
throw new Error(`No schemas found in WSDL: ${input.wsdl}`);
|
|
61
68
|
}
|
|
62
69
|
info(`Schemas discovered: ${wsdlCatalog.schemas.length}`);
|
|
63
|
-
const compiled = compileCatalog(wsdlCatalog, finalOptions);
|
|
70
|
+
const compiled = compileCatalog(wsdlCatalog, finalOptions, streamConfig);
|
|
71
|
+
// Apply companion-catalog shape resolution when a stream config is present.
|
|
72
|
+
if (streamConfig) {
|
|
73
|
+
const { applyShapeCatalogs } = await import("./compiler/shapeResolver.js");
|
|
74
|
+
const shapeBaseDir = input.streamConfigFile
|
|
75
|
+
? path.dirname(path.resolve(input.streamConfigFile))
|
|
76
|
+
: path.dirname(path.resolve(input.wsdl));
|
|
77
|
+
await applyShapeCatalogs(compiled, streamConfig, { baseDir: shapeBaseDir });
|
|
78
|
+
}
|
|
64
79
|
info(`Compiled WSDL: ${wsdlCatalog.wsdlUri}`);
|
|
65
|
-
// check if we have any
|
|
66
|
-
if (compiled.types.length === 0) {
|
|
67
|
-
throw new Error(`No types compiled from WSDL: ${input.wsdl}`);
|
|
80
|
+
// check if we have any data models and operations
|
|
81
|
+
if (compiled.types.length === 0 && compiled.aliases.length === 0) {
|
|
82
|
+
throw new Error(`No types or aliases compiled from WSDL: ${input.wsdl}`);
|
|
68
83
|
}
|
|
69
84
|
else {
|
|
70
85
|
info(`Types discovered: ${compiled.types.length}`);
|
|
86
|
+
info(`Aliases discovered: ${compiled.aliases.length}`);
|
|
87
|
+
for (const note of compiled.diagnostics?.notes ?? []) {
|
|
88
|
+
info(`Note: ${note}`);
|
|
89
|
+
}
|
|
71
90
|
}
|
|
72
91
|
if (compiled.operations.length === 0) {
|
|
73
92
|
throw new Error(`No operations compiled from WSDL: ${input.wsdl}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateOpenAPI.d.ts","sourceRoot":"","sources":["../../src/openapi/generateOpenAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,EAAiB,KAAK,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAKnF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC;IAC3E,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,
|
|
1
|
+
{"version":3,"file":"generateOpenAPI.d.ts","sourceRoot":"","sources":["../../src/openapi/generateOpenAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,EAAiB,KAAK,eAAe,EAAC,MAAM,+BAA+B,CAAC;AAKnF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC;IAC3E,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CAsTD"}
|
|
@@ -149,7 +149,7 @@ export async function generateOpenAPI(opts) {
|
|
|
149
149
|
};
|
|
150
150
|
const extensionSchemas = {};
|
|
151
151
|
for (const op of compiled.operations) {
|
|
152
|
-
const payloadType = op.outputElement?.local;
|
|
152
|
+
const payloadType = op.outputTypeName ?? op.outputElement?.local;
|
|
153
153
|
const payloadRef = payloadType && schemas[payloadType] ? { $ref: `#/components/schemas/${payloadType}` } : { type: "object" };
|
|
154
154
|
const baseForExt = payloadType || op.name;
|
|
155
155
|
const extName = joinWithNamespace(baseForExt, envelopeNamespace);
|
|
@@ -183,7 +183,35 @@ export async function generateOpenAPI(opts) {
|
|
|
183
183
|
const op = compiled.operations.find(o => o.name === opId);
|
|
184
184
|
if (!op)
|
|
185
185
|
continue;
|
|
186
|
-
|
|
186
|
+
if (op.stream) {
|
|
187
|
+
// Stream operations bypass the standard JSON envelope for 200. Emit
|
|
188
|
+
// the configured media type with an itemSchema carried under the
|
|
189
|
+
// x-wsdl-tsc-stream extension so generated tooling can find the
|
|
190
|
+
// record schema without pretending NDJSON is a single JSON document.
|
|
191
|
+
// Errors raised before the first byte still use the standard envelope.
|
|
192
|
+
const recordType = op.stream.recordTypeName;
|
|
193
|
+
const itemRef = { $ref: `#/components/schemas/${recordType}` };
|
|
194
|
+
if (methodObj.responses?.["200"]) {
|
|
195
|
+
methodObj.responses["200"] = {
|
|
196
|
+
description: "Successful streamed SOAP operation response",
|
|
197
|
+
content: {
|
|
198
|
+
[op.stream.mediaType]: {
|
|
199
|
+
schema: { type: "string" },
|
|
200
|
+
"x-wsdl-tsc-stream": {
|
|
201
|
+
format: op.stream.format,
|
|
202
|
+
itemSchema: itemRef,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (methodObj.responses?.default) {
|
|
209
|
+
methodObj.responses.default.description = "Error raised before any stream record was emitted (standard envelope).";
|
|
210
|
+
methodObj.responses.default.content = { "application/json": { schema: { $ref: `#/components/schemas/${baseEnvelopeName}` } } };
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const payloadType = op.outputTypeName ?? op.outputElement?.local;
|
|
187
215
|
const baseForExt = payloadType || op.name;
|
|
188
216
|
const extName = joinWithNamespace(baseForExt, envelopeNamespace);
|
|
189
217
|
if (methodObj.responses?.["200"]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generatePaths.d.ts","sourceRoot":"","sources":["../../src/openapi/generatePaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAG3C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,EAAE,MAAM,GAAG;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAE9B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;IAC9C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"generatePaths.d.ts","sourceRoot":"","sources":["../../src/openapi/generatePaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAG3C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,EAAE,MAAM,GAAG;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAE9B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;IAC9C,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,uBAoElF"}
|
|
@@ -8,8 +8,10 @@ export function generatePaths(compiled, opts) {
|
|
|
8
8
|
const override = opts.overrides?.[op.name] || {};
|
|
9
9
|
const method = (override.method || opts.defaultMethod || "post").toLowerCase();
|
|
10
10
|
const tag = opts.tagsMap?.[op.name] || opts.defaultTag;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const inputTypeName = op.inputTypeName ?? op.inputElement?.local;
|
|
12
|
+
const outputTypeName = op.outputTypeName ?? op.outputElement?.local;
|
|
13
|
+
const inputRef = inputTypeName ? { $ref: `#/components/schemas/${inputTypeName}` } : { type: "object" };
|
|
14
|
+
const outputRef = outputTypeName ? { $ref: `#/components/schemas/${outputTypeName}` } : { type: "object" };
|
|
13
15
|
const parameters = [];
|
|
14
16
|
// Header parameters from security builder
|
|
15
17
|
const headerParamNames = opts.opHeaderParameters[op.name] || [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateSchemas.d.ts","sourceRoot":"","sources":["../../src/openapi/generateSchemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAgB,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"generateSchemas.d.ts","sourceRoot":"","sources":["../../src/openapi/generateSchemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAgB,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAwKpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,sBAAsB,GAAG,iBAAiB,CAoD1G"}
|
|
@@ -57,6 +57,16 @@ function isArrayWrapper(t) {
|
|
|
57
57
|
return null;
|
|
58
58
|
return { itemType: e.tsType };
|
|
59
59
|
}
|
|
60
|
+
function isSyntheticAliasSelfWrapper(t, aliasNames) {
|
|
61
|
+
if (!aliasNames.has(t.name))
|
|
62
|
+
return false;
|
|
63
|
+
if (t.attrs.length !== 0)
|
|
64
|
+
return false;
|
|
65
|
+
if (t.elems.length !== 1)
|
|
66
|
+
return false;
|
|
67
|
+
const e = t.elems[0];
|
|
68
|
+
return e.name === "$value" && e.tsType === t.name;
|
|
69
|
+
}
|
|
60
70
|
function buildComplexSchema(t, closed, knownTypeNames, aliasNames, flattenWrappers) {
|
|
61
71
|
// Use knownTypeNames/aliasNames to validate $ref targets so we surface
|
|
62
72
|
// compiler issues early instead of emitting dangling references in OpenAPI output.
|
|
@@ -181,16 +191,21 @@ export function generateSchemas(compiled, opts) {
|
|
|
181
191
|
schemas[a.name] = buildAliasSchema(a);
|
|
182
192
|
}
|
|
183
193
|
for (const t of compiled.types) {
|
|
194
|
+
if (isSyntheticAliasSelfWrapper(t, aliasNames)) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
184
197
|
schemas[t.name] = buildComplexSchema(t, closed, typeNames, aliasNames, flattenWrappers);
|
|
185
198
|
}
|
|
186
199
|
if (opts.pruneUnusedSchemas) {
|
|
187
|
-
// Root references:
|
|
200
|
+
// Root references: operation type names when available, falling back to element local names.
|
|
188
201
|
const roots = new Set();
|
|
189
202
|
for (const op of compiled.operations) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (
|
|
193
|
-
roots.add(
|
|
203
|
+
const inputRoot = op.inputTypeName ?? op.inputElement?.local;
|
|
204
|
+
const outputRoot = op.outputTypeName ?? op.outputElement?.local;
|
|
205
|
+
if (inputRoot)
|
|
206
|
+
roots.add(inputRoot);
|
|
207
|
+
if (outputRoot)
|
|
208
|
+
roots.add(outputRoot);
|
|
194
209
|
}
|
|
195
210
|
// BFS through $ref graph
|
|
196
211
|
const reachable = new Set();
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type GenerateOpenAPIOptions } from "./openapi/generateOpenAPI.js";
|
|
2
2
|
import { type GenerateGatewayOptions } from "./gateway/generateGateway.js";
|
|
3
3
|
import { type CompilerOptions } from "./config.js";
|
|
4
|
+
import { type StreamConfig } from "./util/streamConfig.js";
|
|
4
5
|
/**
|
|
5
6
|
* Configuration options for the generation pipeline
|
|
6
7
|
*
|
|
@@ -45,6 +46,18 @@ export interface PipelineOptions {
|
|
|
45
46
|
force?: boolean;
|
|
46
47
|
flattenArrayWrappers?: boolean;
|
|
47
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Path to a stream configuration JSON file. Loaded and parsed once before
|
|
51
|
+
* compilation; the parsed value takes precedence over `streamConfig` when
|
|
52
|
+
* both are set. Leave undefined to preserve byte-for-byte buffered output.
|
|
53
|
+
*/
|
|
54
|
+
streamConfigFile?: string;
|
|
55
|
+
/**
|
|
56
|
+
* In-memory stream configuration. Useful for programmatic callers that
|
|
57
|
+
* do not want to round-trip through a file. Ignored when
|
|
58
|
+
* `streamConfigFile` is also provided.
|
|
59
|
+
*/
|
|
60
|
+
streamConfig?: StreamConfig;
|
|
48
61
|
}
|
|
49
62
|
/**
|
|
50
63
|
* Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI/Gateway/App
|
package/dist/pipeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAC,KAAK,eAAe,EAAyB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAC,KAAK,eAAe,EAAyB,MAAM,aAAa,CAAC;AAEzE,OAAO,EAAuB,KAAK,YAAY,EAAC,MAAM,wBAAwB,CAAC;AAG/E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;IACF;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAC;CAAE,CAAC,CAiKhH"}
|
package/dist/pipeline.js
CHANGED
|
@@ -19,6 +19,8 @@ import { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
|
19
19
|
import { generateGateway } from "./gateway/generateGateway.js";
|
|
20
20
|
import { resolveCompilerOptions } from "./config.js";
|
|
21
21
|
import { emitClientArtifacts, reportCompilationStats, reportOpenApiSuccess, success } from "./util/cli.js";
|
|
22
|
+
import { loadStreamConfigFile } from "./util/streamConfig.js";
|
|
23
|
+
import { applyShapeCatalogs } from "./compiler/shapeResolver.js";
|
|
22
24
|
/**
|
|
23
25
|
* Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI/Gateway/App
|
|
24
26
|
*
|
|
@@ -52,10 +54,24 @@ export async function runGenerationPipeline(opts) {
|
|
|
52
54
|
wsdl: opts.wsdl,
|
|
53
55
|
out: workingDir,
|
|
54
56
|
});
|
|
57
|
+
// Resolve stream configuration (file path takes precedence over in-memory).
|
|
58
|
+
const streamConfig = opts.streamConfigFile
|
|
59
|
+
? loadStreamConfigFile(opts.streamConfigFile)
|
|
60
|
+
: opts.streamConfig;
|
|
55
61
|
// Step 1: Load and parse the WSDL document
|
|
56
62
|
const wsdlCatalog = await loadWsdl(opts.wsdl);
|
|
57
63
|
// Step 2: Compile the WSDL into a structured catalog
|
|
58
|
-
const compiled = compileCatalog(wsdlCatalog, finalCompiler);
|
|
64
|
+
const compiled = compileCatalog(wsdlCatalog, finalCompiler, streamConfig);
|
|
65
|
+
// Step 2b: Apply companion-catalog shape resolution when a stream config is
|
|
66
|
+
// provided. Relative paths in shapeCatalogs resolve against the stream
|
|
67
|
+
// config file's directory, or the WSDL's directory when the config came in
|
|
68
|
+
// programmatically.
|
|
69
|
+
if (streamConfig) {
|
|
70
|
+
const shapeBaseDir = opts.streamConfigFile
|
|
71
|
+
? path.dirname(path.resolve(opts.streamConfigFile))
|
|
72
|
+
: path.dirname(path.resolve(opts.wsdl));
|
|
73
|
+
await applyShapeCatalogs(compiled, streamConfig, { baseDir: shapeBaseDir });
|
|
74
|
+
}
|
|
59
75
|
// Report compilation statistics
|
|
60
76
|
reportCompilationStats(wsdlCatalog, compiled);
|
|
61
77
|
// Step 3: Emit catalog.json (always, to the specified catalogOut path)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NDJSON adapter for record iterables.
|
|
3
|
+
*
|
|
4
|
+
* Phase 3 of ADR-002. Given an `AsyncIterable<T>` (typically the output of
|
|
5
|
+
* `parseRecords`), produce a Node `Readable` that emits one JSON-encoded line
|
|
6
|
+
* per record and respects downstream backpressure.
|
|
7
|
+
*
|
|
8
|
+
* Terminal-error policy (Q3 resolved): the stream aborts on source errors.
|
|
9
|
+
* Before-first-byte errors surface as the stream's `error` event before any
|
|
10
|
+
* bytes are pushed, so Fastify can translate them into a normal JSON error
|
|
11
|
+
* envelope. Errors after the first byte propagate as `error` events too, but
|
|
12
|
+
* callers should treat them as a truncated response.
|
|
13
|
+
*/
|
|
14
|
+
import { Readable } from "node:stream";
|
|
15
|
+
/**
|
|
16
|
+
* Wrap an async iterable of records in a Node `Readable` stream that emits
|
|
17
|
+
* NDJSON (one JSON document per line, LF-terminated). Downstream backpressure
|
|
18
|
+
* is honored via `Readable.from`'s default behavior: the iterator's `next()`
|
|
19
|
+
* is not called until the internal buffer has room.
|
|
20
|
+
*
|
|
21
|
+
* Source errors are forwarded to the returned stream's `error` event.
|
|
22
|
+
*/
|
|
23
|
+
export declare function toNdjson<T>(records: AsyncIterable<T>): Readable;
|
|
24
|
+
//# sourceMappingURL=ndjson.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ndjson.d.ts","sourceRoot":"","sources":["../../src/runtime/ndjson.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;AAErC;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,QAAQ,CAE/D"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NDJSON adapter for record iterables.
|
|
3
|
+
*
|
|
4
|
+
* Phase 3 of ADR-002. Given an `AsyncIterable<T>` (typically the output of
|
|
5
|
+
* `parseRecords`), produce a Node `Readable` that emits one JSON-encoded line
|
|
6
|
+
* per record and respects downstream backpressure.
|
|
7
|
+
*
|
|
8
|
+
* Terminal-error policy (Q3 resolved): the stream aborts on source errors.
|
|
9
|
+
* Before-first-byte errors surface as the stream's `error` event before any
|
|
10
|
+
* bytes are pushed, so Fastify can translate them into a normal JSON error
|
|
11
|
+
* envelope. Errors after the first byte propagate as `error` events too, but
|
|
12
|
+
* callers should treat them as a truncated response.
|
|
13
|
+
*/
|
|
14
|
+
import { Readable } from "node:stream";
|
|
15
|
+
/**
|
|
16
|
+
* Wrap an async iterable of records in a Node `Readable` stream that emits
|
|
17
|
+
* NDJSON (one JSON document per line, LF-terminated). Downstream backpressure
|
|
18
|
+
* is honored via `Readable.from`'s default behavior: the iterator's `next()`
|
|
19
|
+
* is not called until the internal buffer has room.
|
|
20
|
+
*
|
|
21
|
+
* Source errors are forwarded to the returned stream's `error` event.
|
|
22
|
+
*/
|
|
23
|
+
export function toNdjson(records) {
|
|
24
|
+
return Readable.from(encode(records), { objectMode: false, encoding: "utf-8" });
|
|
25
|
+
}
|
|
26
|
+
async function* encode(records) {
|
|
27
|
+
for await (const record of records) {
|
|
28
|
+
yield JSON.stringify(record) + "\n";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catalog-driven parse specification. `recordPath` is an ordered XML element
|
|
3
|
+
* path from the SOAP body payload down to the repeated record element. The
|
|
4
|
+
* path is matched as a suffix of the open-tag stack, so callers may either
|
|
5
|
+
* pre-strip the SOAP envelope or feed the entire response body.
|
|
6
|
+
*
|
|
7
|
+
* Duplicate local names in `recordPath` are supported and expected (Escapia's
|
|
8
|
+
* EVRN content service nests two elements named `EVRN_UnitDescriptiveInfoRS`).
|
|
9
|
+
*/
|
|
10
|
+
export interface RecordParseSpec {
|
|
11
|
+
recordPath: string[];
|
|
12
|
+
/** TypeScript type name of the record, used to look up propMeta. */
|
|
13
|
+
recordTypeName: string;
|
|
14
|
+
/** Attribute bag key to stash XML attributes under. Defaults to "$attributes". */
|
|
15
|
+
attributesKey?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Compiled-catalog child-type map: `childType[typeName][propName] = tsType`.
|
|
18
|
+
* Used to descend into nested complex types and to detect array-valued
|
|
19
|
+
* props (trailing `[]`). Optional — absent means occurrence-based array
|
|
20
|
+
* detection only.
|
|
21
|
+
*/
|
|
22
|
+
childType?: Record<string, Record<string, string>>;
|
|
23
|
+
/**
|
|
24
|
+
* Compiled-catalog prop-meta map: carries min/max/nillable/declaredType.
|
|
25
|
+
* When present, `max > 1` or `max === "unbounded"` drives array emission
|
|
26
|
+
* even for props that happen to occur just once in a given record.
|
|
27
|
+
*/
|
|
28
|
+
propMeta?: Record<string, Record<string, PropMeta>>;
|
|
29
|
+
}
|
|
30
|
+
export interface PropMeta {
|
|
31
|
+
min?: number;
|
|
32
|
+
max?: number | "unbounded";
|
|
33
|
+
nillable?: boolean;
|
|
34
|
+
declaredType?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Consume an async iterable of XML bytes/strings and yield parsed records.
|
|
38
|
+
*
|
|
39
|
+
* The returned iterator is single-pass; iteration must complete (or be
|
|
40
|
+
* interrupted by the consumer via `break`/`return`) before the upstream
|
|
41
|
+
* source is released. Errors from the source or from the SAX parser abort
|
|
42
|
+
* the iteration via a rejected `next()`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseRecords<T = unknown>(source: AsyncIterable<string | Uint8Array>, spec: RecordParseSpec): AsyncIterable<T>;
|
|
45
|
+
//# sourceMappingURL=streamXml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamXml.d.ts","sourceRoot":"","sources":["../../src/runtime/streamXml.ts"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,oEAAoE;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAuB,YAAY,CAAC,CAAC,GAAG,OAAO,EAC7C,MAAM,EAAE,aAAa,CAAC,MAAM,GAAG,UAAU,CAAC,EAC1C,IAAI,EAAE,eAAe,GACpB,aAAa,CAAC,CAAC,CAAC,CAqGlB"}
|