@techspokes/typescript-wsdl-client 0.11.5 → 0.11.6
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/dist/app/generateApp.js +1 -1
- package/dist/compiler/schemaCompiler.d.ts +1 -0
- package/dist/compiler/schemaCompiler.d.ts.map +1 -1
- package/dist/compiler/schemaCompiler.js +24 -1
- package/dist/gateway/generators.d.ts +2 -0
- package/dist/gateway/generators.d.ts.map +1 -1
- package/dist/gateway/generators.js +57 -23
- package/dist/gateway/helpers.d.ts +13 -0
- package/dist/gateway/helpers.d.ts.map +1 -1
- package/dist/gateway/helpers.js +58 -0
- package/dist/pipeline.d.ts +1 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +1 -0
- package/dist/test/generateTests.d.ts +1 -0
- package/dist/test/generateTests.d.ts.map +1 -1
- package/dist/test/generateTests.js +9 -4
- package/dist/test/generators.d.ts +12 -7
- package/dist/test/generators.d.ts.map +1 -1
- package/dist/test/generators.js +41 -37
- package/dist/test/mockData.d.ts +23 -10
- package/dist/test/mockData.d.ts.map +1 -1
- package/dist/test/mockData.js +44 -4
- package/dist/util/catalogMeta.d.ts +33 -0
- package/dist/util/catalogMeta.d.ts.map +1 -0
- package/dist/util/catalogMeta.js +95 -0
- package/package.json +1 -1
package/dist/app/generateApp.js
CHANGED
|
@@ -105,6 +105,7 @@ export type CompiledCatalog = {
|
|
|
105
105
|
aliases: CompiledAlias[];
|
|
106
106
|
meta: {
|
|
107
107
|
attrSpec: Record<string, string[]>;
|
|
108
|
+
attrType: Record<string, Record<string, string>>;
|
|
108
109
|
childType: Record<string, Record<string, string>>;
|
|
109
110
|
propMeta: Record<string, Record<string, any>>;
|
|
110
111
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"schemaCompiler.d.ts","sourceRoot":"","sources":["../../src/compiler/schemaCompiler.ts"],"names":[],"mappings":"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"}
|
|
@@ -136,6 +136,7 @@ export function compileCatalog(cat, options) {
|
|
|
136
136
|
const inProgress = new Set();
|
|
137
137
|
// meta accumulators
|
|
138
138
|
const attrSpec = {};
|
|
139
|
+
const attrType = {};
|
|
139
140
|
const childType = {};
|
|
140
141
|
const propMeta = {};
|
|
141
142
|
/** Compile a simpleType node to TS */
|
|
@@ -536,9 +537,28 @@ export function compileCatalog(cat, options) {
|
|
|
536
537
|
// emit lists
|
|
537
538
|
const typesList = Array.from(compiledMap.values());
|
|
538
539
|
const aliasList = Array.from(aliasMap.values());
|
|
540
|
+
// Build alias resolution map for simple type aliases (e.g. YesNoType -> '"Yes" | "No"')
|
|
541
|
+
const aliasResolve = new Map();
|
|
542
|
+
for (const a of aliasList) {
|
|
543
|
+
if (a.tsType && a.tsType !== a.name) {
|
|
544
|
+
aliasResolve.set(a.name, a.tsType);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
539
547
|
// meta
|
|
540
548
|
for (const t of typesList) {
|
|
541
549
|
attrSpec[t.name] = (t.attrs || []).map(a => a.name);
|
|
550
|
+
const attrMap = {};
|
|
551
|
+
for (const a of t.attrs || []) {
|
|
552
|
+
let tsType = typeof a.tsType === "string" ? a.tsType : "string";
|
|
553
|
+
// Resolve alias names to their underlying TS types for mock data generation
|
|
554
|
+
if (aliasResolve.has(tsType)) {
|
|
555
|
+
tsType = aliasResolve.get(tsType);
|
|
556
|
+
}
|
|
557
|
+
attrMap[a.name] = tsType;
|
|
558
|
+
}
|
|
559
|
+
if (Object.keys(attrMap).length > 0) {
|
|
560
|
+
attrType[t.name] = attrMap;
|
|
561
|
+
}
|
|
542
562
|
const child = {};
|
|
543
563
|
const meta = {};
|
|
544
564
|
for (const e of t.elems || []) {
|
|
@@ -561,6 +581,9 @@ export function compileCatalog(cat, options) {
|
|
|
561
581
|
childType[a.name] = childType[a.tsType];
|
|
562
582
|
propMeta[a.name] = propMeta[a.tsType];
|
|
563
583
|
attrSpec[a.name] = attrSpec[a.tsType];
|
|
584
|
+
if (a.tsType in attrType) {
|
|
585
|
+
attrType[a.name] = attrType[a.tsType];
|
|
586
|
+
}
|
|
564
587
|
}
|
|
565
588
|
}
|
|
566
589
|
}
|
|
@@ -664,7 +687,7 @@ export function compileCatalog(cat, options) {
|
|
|
664
687
|
options: options,
|
|
665
688
|
types: typesList,
|
|
666
689
|
aliases: aliasList,
|
|
667
|
-
meta: { attrSpec, childType, propMeta },
|
|
690
|
+
meta: { attrSpec, attrType, childType, propMeta },
|
|
668
691
|
operations: ops,
|
|
669
692
|
wsdlTargetNS: defs?.["@_targetNamespace"] || "",
|
|
670
693
|
serviceName,
|
|
@@ -36,6 +36,8 @@ export interface OperationMetadata {
|
|
|
36
36
|
clientMethodName?: string;
|
|
37
37
|
requestTypeName?: string;
|
|
38
38
|
responseTypeName?: string;
|
|
39
|
+
/** When true, response schema is omitted from route registration to avoid fast-json-stringify stack overflow on deeply nested $ref graphs */
|
|
40
|
+
skipResponseSchema?: boolean;
|
|
39
41
|
}
|
|
40
42
|
/**
|
|
41
43
|
* Emits Fastify-compatible operation schema files
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/gateway/generators.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
|
-
import { flattenAllOf, rewriteSchemaRefs, slugName, } from "./helpers.js";
|
|
15
|
+
import { flattenAllOf, measureSchemaRefComplexity, rewriteSchemaRefs, slugName, } from "./helpers.js";
|
|
16
|
+
import { detectArrayWrappers as detectArrayWrappersShared } from "../util/catalogMeta.js";
|
|
16
17
|
/**
|
|
17
18
|
* Emits individual JSON Schema files for each OpenAPI component schema
|
|
18
19
|
*
|
|
@@ -174,12 +175,29 @@ export function emitOperationSchemas(doc, opsDir, versionSlug, serviceSlug, sche
|
|
|
174
175
|
opSchema.querystring = querySchema;
|
|
175
176
|
if (headersSchema)
|
|
176
177
|
opSchema.headers = headersSchema;
|
|
178
|
+
// Check response schema $ref complexity — fast-json-stringify stack-overflows
|
|
179
|
+
// on deeply nested $ref graphs (typically > 150 unique schema references).
|
|
180
|
+
const REF_COMPLEXITY_LIMIT = 150;
|
|
181
|
+
let skipResponseSchema = false;
|
|
182
|
+
const allSchemas = doc.components?.schemas ?? {};
|
|
183
|
+
for (const code of Object.keys(responseObj)) {
|
|
184
|
+
const refId = responseObj[code]?.$ref;
|
|
185
|
+
if (!refId)
|
|
186
|
+
continue;
|
|
187
|
+
// Extract component name from URN: look up inverse of schemaIdByName
|
|
188
|
+
const schemaName = Object.keys(schemaIdByName).find(n => schemaIdByName[n] + "#" === refId);
|
|
189
|
+
if (schemaName && measureSchemaRefComplexity(schemaName, allSchemas, REF_COMPLEXITY_LIMIT) >= REF_COMPLEXITY_LIMIT) {
|
|
190
|
+
skipResponseSchema = true;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
177
194
|
const opOutPath = path.join(opsDir, `${operationSlug}.json`);
|
|
178
195
|
fs.writeFileSync(opOutPath, JSON.stringify(opSchema, null, 2), "utf8");
|
|
179
196
|
operations.push({
|
|
180
197
|
operationSlug,
|
|
181
198
|
method: lowerMethod,
|
|
182
199
|
path: p,
|
|
200
|
+
skipResponseSchema,
|
|
183
201
|
});
|
|
184
202
|
}
|
|
185
203
|
}
|
|
@@ -255,10 +273,22 @@ export function emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, oper
|
|
|
255
273
|
routeTs += `import type { FastifyInstance } from "fastify";\n`;
|
|
256
274
|
routeTs += `import schema from "../schemas/operations/${op.operationSlug}.json" with { type: "json" };\n\n`;
|
|
257
275
|
routeTs += `export async function ${fnName}(fastify: FastifyInstance) {\n`;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
276
|
+
if (op.skipResponseSchema) {
|
|
277
|
+
// Response schema too complex for fast-json-stringify — strip it so Fastify
|
|
278
|
+
// falls back to JSON.stringify for response serialization.
|
|
279
|
+
routeTs += ` // Response schema omitted: $ref graph exceeds fast-json-stringify depth limit\n`;
|
|
280
|
+
routeTs += ` const { response: _response, ...routeSchema } = schema as Record<string, unknown>;\n`;
|
|
281
|
+
routeTs += ` fastify.route({\n`;
|
|
282
|
+
routeTs += ` method: "${op.method.toUpperCase()}",\n`;
|
|
283
|
+
routeTs += ` url: "${op.path}",\n`;
|
|
284
|
+
routeTs += ` schema: routeSchema as any,\n`;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
routeTs += ` fastify.route({\n`;
|
|
288
|
+
routeTs += ` method: "${op.method.toUpperCase()}",\n`;
|
|
289
|
+
routeTs += ` url: "${op.path}",\n`;
|
|
290
|
+
routeTs += ` schema: schema as any,\n`;
|
|
291
|
+
}
|
|
262
292
|
routeTs += ` handler: async (request, reply) => {\n`;
|
|
263
293
|
routeTs += ` throw new Error("Not implemented");\n`;
|
|
264
294
|
routeTs += ` }\n`;
|
|
@@ -291,25 +321,13 @@ export function emitRouteFiles(outDir, routesDir, versionSlug, serviceSlug, oper
|
|
|
291
321
|
/**
|
|
292
322
|
* Detects ArrayOf* wrapper types from catalog type metadata.
|
|
293
323
|
*
|
|
294
|
-
*
|
|
295
|
-
* and no attributes — mirroring the logic in generateSchemas.ts isArrayWrapper().
|
|
324
|
+
* Delegates to the shared utility in src/util/catalogMeta.ts.
|
|
296
325
|
*
|
|
297
326
|
* @param catalog - Compiled catalog object
|
|
298
327
|
* @returns Record mapping wrapper type name to inner element property name
|
|
299
328
|
*/
|
|
300
329
|
function detectArrayWrappers(catalog) {
|
|
301
|
-
|
|
302
|
-
for (const t of catalog.types || []) {
|
|
303
|
-
if (t.attrs && t.attrs.length !== 0)
|
|
304
|
-
continue;
|
|
305
|
-
if (!t.elems || t.elems.length !== 1)
|
|
306
|
-
continue;
|
|
307
|
-
const e = t.elems[0];
|
|
308
|
-
if (e.max !== "unbounded" && !(e.max > 1))
|
|
309
|
-
continue;
|
|
310
|
-
wrappers[t.name] = e.name;
|
|
311
|
-
}
|
|
312
|
-
return wrappers;
|
|
330
|
+
return detectArrayWrappersShared(catalog.types || []);
|
|
313
331
|
}
|
|
314
332
|
export function emitRuntimeModule(outDir, versionSlug, serviceSlug, catalog) {
|
|
315
333
|
const vSlug = slugName(versionSlug);
|
|
@@ -351,7 +369,13 @@ export function unwrapArrayWrappers(data: unknown, typeName: string): unknown {
|
|
|
351
369
|
// If this type is itself a wrapper, unwrap it
|
|
352
370
|
if (typeName in ARRAY_WRAPPERS) {
|
|
353
371
|
const innerKey = ARRAY_WRAPPERS[typeName];
|
|
354
|
-
|
|
372
|
+
const arr = (data as Record<string, unknown>)[innerKey] ?? [];
|
|
373
|
+
// Recurse into each item using the element's type from CHILDREN_TYPES
|
|
374
|
+
if (Array.isArray(arr) && typeName in CHILDREN_TYPES) {
|
|
375
|
+
const elemType = CHILDREN_TYPES[typeName][innerKey];
|
|
376
|
+
if (elemType) return arr.map(item => unwrapArrayWrappers(item, elemType));
|
|
377
|
+
}
|
|
378
|
+
return arr;
|
|
355
379
|
}
|
|
356
380
|
|
|
357
381
|
// Recurse into children whose types may contain wrappers
|
|
@@ -360,7 +384,11 @@ export function unwrapArrayWrappers(data: unknown, typeName: string): unknown {
|
|
|
360
384
|
for (const [propName, propType] of Object.entries(children)) {
|
|
361
385
|
const val = (data as Record<string, unknown>)[propName];
|
|
362
386
|
if (val !== undefined) {
|
|
363
|
-
|
|
387
|
+
if (Array.isArray(val)) {
|
|
388
|
+
(data as Record<string, unknown>)[propName] = val.map(item => unwrapArrayWrappers(item, propType));
|
|
389
|
+
} else {
|
|
390
|
+
(data as Record<string, unknown>)[propName] = unwrapArrayWrappers(val, propType);
|
|
391
|
+
}
|
|
364
392
|
}
|
|
365
393
|
}
|
|
366
394
|
}
|
|
@@ -746,6 +774,12 @@ export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, servi
|
|
|
746
774
|
? `return buildSuccessEnvelope(unwrapArrayWrappers(result.response, "${resTypeName}"));`
|
|
747
775
|
: `return buildSuccessEnvelope(result.response);`;
|
|
748
776
|
// Note: op.path comes from OpenAPI and already includes any base path
|
|
777
|
+
const schemaBinding = op.skipResponseSchema
|
|
778
|
+
? `\n// Response schema omitted: $ref graph exceeds fast-json-stringify depth limit\nconst { response: _response, ...routeSchema } = schema as Record<string, unknown>;\n`
|
|
779
|
+
: "";
|
|
780
|
+
const schemaLine = op.skipResponseSchema
|
|
781
|
+
? " schema: routeSchema,"
|
|
782
|
+
: " schema,";
|
|
749
783
|
let routeTs = `/**
|
|
750
784
|
* Route: ${op.method.toUpperCase()} ${op.path}
|
|
751
785
|
* Operation: ${op.operationId || op.operationSlug}
|
|
@@ -756,12 +790,12 @@ export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, servi
|
|
|
756
790
|
import type { FastifyInstance } from "fastify";
|
|
757
791
|
${typeImport}import schema from "../schemas/operations/${op.operationSlug}.json" with { type: "json" };
|
|
758
792
|
${runtimeImport}
|
|
759
|
-
|
|
793
|
+
${schemaBinding}
|
|
760
794
|
export async function ${fnName}(fastify: FastifyInstance) {
|
|
761
795
|
fastify.route${routeGeneric}({
|
|
762
796
|
method: "${op.method.toUpperCase()}",
|
|
763
797
|
url: "${op.path}",
|
|
764
|
-
|
|
798
|
+
${schemaLine}
|
|
765
799
|
handler: async (request) => {
|
|
766
800
|
const client = fastify.${clientMeta.decoratorName};
|
|
767
801
|
const result = await client.${clientMethod}(${bodyArg});
|
|
@@ -222,4 +222,17 @@ export declare function resolveOperationMeta(operationId: string, operationSlug:
|
|
|
222
222
|
inputTypeName?: string;
|
|
223
223
|
outputTypeName?: string;
|
|
224
224
|
}>): ResolvedOperationMeta;
|
|
225
|
+
/**
|
|
226
|
+
* Measures the $ref graph complexity of a schema component.
|
|
227
|
+
*
|
|
228
|
+
* Walks all $ref chains from the starting schema and counts the number of
|
|
229
|
+
* unique schemas referenced. Used to detect response schemas that are too
|
|
230
|
+
* complex for fast-json-stringify to compile without stack overflow.
|
|
231
|
+
*
|
|
232
|
+
* @param startSchemaName - Name of the schema component to start from
|
|
233
|
+
* @param allSchemas - All component schemas (from doc.components.schemas)
|
|
234
|
+
* @param limit - Stop walking after this many unique refs (default 200)
|
|
235
|
+
* @returns Number of unique schema components reachable via $ref
|
|
236
|
+
*/
|
|
237
|
+
export declare function measureSchemaRefComplexity(startSchemaName: string, allSchemas: Record<string, any>, limit?: number): number;
|
|
225
238
|
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway/helpers.js
CHANGED
|
@@ -428,3 +428,61 @@ export function resolveOperationMeta(operationId, operationSlug, method, path, c
|
|
|
428
428
|
responseTypeName,
|
|
429
429
|
};
|
|
430
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Measures the $ref graph complexity of a schema component.
|
|
433
|
+
*
|
|
434
|
+
* Walks all $ref chains from the starting schema and counts the number of
|
|
435
|
+
* unique schemas referenced. Used to detect response schemas that are too
|
|
436
|
+
* complex for fast-json-stringify to compile without stack overflow.
|
|
437
|
+
*
|
|
438
|
+
* @param startSchemaName - Name of the schema component to start from
|
|
439
|
+
* @param allSchemas - All component schemas (from doc.components.schemas)
|
|
440
|
+
* @param limit - Stop walking after this many unique refs (default 200)
|
|
441
|
+
* @returns Number of unique schema components reachable via $ref
|
|
442
|
+
*/
|
|
443
|
+
export function measureSchemaRefComplexity(startSchemaName, allSchemas, limit = 200) {
|
|
444
|
+
const visited = new Set();
|
|
445
|
+
function walkSchema(node) {
|
|
446
|
+
if (!node || typeof node !== "object" || visited.size >= limit)
|
|
447
|
+
return;
|
|
448
|
+
if (Array.isArray(node)) {
|
|
449
|
+
for (const item of node)
|
|
450
|
+
walkSchema(item);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (node.$ref && typeof node.$ref === "string") {
|
|
454
|
+
const prefix = "#/components/schemas/";
|
|
455
|
+
if (node.$ref.startsWith(prefix)) {
|
|
456
|
+
const refName = node.$ref.slice(prefix.length);
|
|
457
|
+
if (!visited.has(refName)) {
|
|
458
|
+
visited.add(refName);
|
|
459
|
+
if (allSchemas[refName])
|
|
460
|
+
walkSchema(allSchemas[refName]);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (node.properties) {
|
|
466
|
+
for (const propSchema of Object.values(node.properties))
|
|
467
|
+
walkSchema(propSchema);
|
|
468
|
+
}
|
|
469
|
+
if (node.items)
|
|
470
|
+
walkSchema(node.items);
|
|
471
|
+
if (node.additionalProperties && typeof node.additionalProperties === "object") {
|
|
472
|
+
walkSchema(node.additionalProperties);
|
|
473
|
+
}
|
|
474
|
+
if (node.allOf)
|
|
475
|
+
for (const m of node.allOf)
|
|
476
|
+
walkSchema(m);
|
|
477
|
+
if (node.anyOf)
|
|
478
|
+
for (const m of node.anyOf)
|
|
479
|
+
walkSchema(m);
|
|
480
|
+
if (node.oneOf)
|
|
481
|
+
for (const m of node.oneOf)
|
|
482
|
+
walkSchema(m);
|
|
483
|
+
}
|
|
484
|
+
visited.add(startSchemaName);
|
|
485
|
+
if (allSchemas[startSchemaName])
|
|
486
|
+
walkSchema(allSchemas[startSchemaName]);
|
|
487
|
+
return visited.size;
|
|
488
|
+
}
|
package/dist/pipeline.d.ts
CHANGED
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;AAGzE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAC,KAAK,eAAe,EAAyB,MAAM,aAAa,CAAC;AAGzE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAC;CAAE,CAAC,CAiJhH"}
|
package/dist/pipeline.js
CHANGED
|
@@ -143,6 +143,7 @@ export async function runGenerationPipeline(opts) {
|
|
|
143
143
|
force: opts.test.force,
|
|
144
144
|
versionSlug: opts.gateway.versionSlug,
|
|
145
145
|
serviceSlug: opts.gateway.serviceSlug,
|
|
146
|
+
flattenArrayWrappers: opts.test.flattenArrayWrappers ?? opts.openapi?.flattenArrayWrappers,
|
|
146
147
|
});
|
|
147
148
|
}
|
|
148
149
|
// Return the compiled catalog and OpenAPI doc for potential further processing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateTests.d.ts","sourceRoot":"","sources":["../../src/test/generateTests.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generateTests.d.ts","sourceRoot":"","sources":["../../src/test/generateTests.ts"],"names":[],"mappings":"AAiCA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAuBD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiK7E"}
|
|
@@ -15,6 +15,7 @@ import fs from "node:fs";
|
|
|
15
15
|
import path from "node:path";
|
|
16
16
|
import { info, success } from "../util/cli.js";
|
|
17
17
|
import { resolveClientMeta, resolveOperationMeta } from "../gateway/helpers.js";
|
|
18
|
+
import { generateAllOperationMocks } from "./mockData.js";
|
|
18
19
|
import { emitVitestConfig, emitMockClientHelper, emitTestAppHelper, emitRoutesTest, emitErrorsTest, emitEnvelopeTest, emitValidationTest, emitClassifyErrorTest, emitEnvelopeBuildersTest, emitUnwrapTest, } from "./generators.js";
|
|
19
20
|
/**
|
|
20
21
|
* Checks whether a test file should be written.
|
|
@@ -109,18 +110,22 @@ export async function generateTests(opts) {
|
|
|
109
110
|
fs.mkdirSync(path.join(testDir, "helpers"), { recursive: true });
|
|
110
111
|
fs.mkdirSync(path.join(testDir, "gateway"), { recursive: true });
|
|
111
112
|
fs.mkdirSync(path.join(testDir, "runtime"), { recursive: true });
|
|
113
|
+
// Compute mock data once for all emitters
|
|
114
|
+
const mocks = generateAllOperationMocks(catalog, {
|
|
115
|
+
flattenArrayWrappers: opts.flattenArrayWrappers,
|
|
116
|
+
});
|
|
112
117
|
// Emit vitest.config.ts
|
|
113
118
|
writeTestFile(path.join(testDir, "vitest.config.ts"), emitVitestConfig(), force);
|
|
114
119
|
// Emit helpers/mock-client.ts
|
|
115
|
-
writeTestFile(path.join(testDir, "helpers", "mock-client.ts"), emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations,
|
|
120
|
+
writeTestFile(path.join(testDir, "helpers", "mock-client.ts"), emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations, mocks), force);
|
|
116
121
|
// Emit helpers/test-app.ts
|
|
117
122
|
writeTestFile(path.join(testDir, "helpers", "test-app.ts"), emitTestAppHelper(testDir, gatewayDir, importsMode, clientMeta), force);
|
|
118
123
|
// Emit gateway/routes.test.ts
|
|
119
|
-
writeTestFile(path.join(testDir, "gateway", "routes.test.ts"), emitRoutesTest(testDir, importsMode, operations,
|
|
124
|
+
writeTestFile(path.join(testDir, "gateway", "routes.test.ts"), emitRoutesTest(testDir, importsMode, operations, mocks), force);
|
|
120
125
|
// Emit gateway/errors.test.ts
|
|
121
|
-
writeTestFile(path.join(testDir, "gateway", "errors.test.ts"), emitErrorsTest(testDir, importsMode, operations,
|
|
126
|
+
writeTestFile(path.join(testDir, "gateway", "errors.test.ts"), emitErrorsTest(testDir, importsMode, operations, mocks), force);
|
|
122
127
|
// Emit gateway/envelope.test.ts
|
|
123
|
-
writeTestFile(path.join(testDir, "gateway", "envelope.test.ts"), emitEnvelopeTest(testDir, importsMode, operations,
|
|
128
|
+
writeTestFile(path.join(testDir, "gateway", "envelope.test.ts"), emitEnvelopeTest(testDir, importsMode, operations, mocks), force);
|
|
124
129
|
// Emit gateway/validation.test.ts
|
|
125
130
|
writeTestFile(path.join(testDir, "gateway", "validation.test.ts"), emitValidationTest(testDir, importsMode, operations), force);
|
|
126
131
|
// Emit runtime/classify-error.test.ts
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ClientMeta, ResolvedOperationMeta } from "../gateway/helpers.js";
|
|
2
2
|
import type { CatalogForMocks } from "./mockData.js";
|
|
3
|
+
/** Pre-computed mock data map passed from the orchestrator to all emitters. */
|
|
4
|
+
export type OperationMocksMap = Map<string, {
|
|
5
|
+
request: Record<string, unknown>;
|
|
6
|
+
response: Record<string, unknown>;
|
|
7
|
+
}>;
|
|
3
8
|
/**
|
|
4
9
|
* Emits vitest.config.ts content.
|
|
5
10
|
*
|
|
@@ -15,9 +20,9 @@ export declare function emitVitestConfig(): string;
|
|
|
15
20
|
* @param importsMode - Import extension mode
|
|
16
21
|
* @param clientMeta - Client metadata
|
|
17
22
|
* @param operations - Resolved operation metadata
|
|
18
|
-
* @param
|
|
23
|
+
* @param mocks - Pre-computed mock data map
|
|
19
24
|
*/
|
|
20
|
-
export declare function emitMockClientHelper(testDir: string, clientDir: string, importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta, operations: ResolvedOperationMeta[],
|
|
25
|
+
export declare function emitMockClientHelper(testDir: string, clientDir: string, importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta, operations: ResolvedOperationMeta[], mocks: OperationMocksMap): string;
|
|
21
26
|
/**
|
|
22
27
|
* Emits helpers/test-app.ts content.
|
|
23
28
|
*
|
|
@@ -33,22 +38,22 @@ export declare function emitTestAppHelper(testDir: string, gatewayDir: string, i
|
|
|
33
38
|
* @param testDir - Absolute path to test output directory
|
|
34
39
|
* @param importsMode - Import extension mode
|
|
35
40
|
* @param operations - Resolved operation metadata
|
|
36
|
-
* @param
|
|
41
|
+
* @param mocks - Pre-computed mock data map
|
|
37
42
|
*/
|
|
38
|
-
export declare function emitRoutesTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[],
|
|
43
|
+
export declare function emitRoutesTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], mocks: OperationMocksMap): string;
|
|
39
44
|
/**
|
|
40
45
|
* Emits gateway/errors.test.ts with error classification tests through Fastify.
|
|
41
46
|
*
|
|
42
47
|
* @param testDir - Absolute path to test output directory
|
|
43
48
|
* @param importsMode - Import extension mode
|
|
44
49
|
* @param operations - Resolved operation metadata (uses first operation for error tests)
|
|
45
|
-
* @param
|
|
50
|
+
* @param mocks - Pre-computed mock data map
|
|
46
51
|
*/
|
|
47
|
-
export declare function emitErrorsTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[],
|
|
52
|
+
export declare function emitErrorsTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], mocks: OperationMocksMap): string;
|
|
48
53
|
/**
|
|
49
54
|
* Emits gateway/envelope.test.ts with SUCCESS/ERROR structure assertions.
|
|
50
55
|
*/
|
|
51
|
-
export declare function emitEnvelopeTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[],
|
|
56
|
+
export declare function emitEnvelopeTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], mocks: OperationMocksMap): string;
|
|
52
57
|
/**
|
|
53
58
|
* Emits gateway/validation.test.ts with invalid payload tests per route.
|
|
54
59
|
*/
|
|
@@ -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;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/test/generators.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAGnD,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAErH;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAkBzC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CA+CR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,GACrB,MAAM,CAsCR;AAED;;;;;;;GAOG;AAEH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,KAAK,EAAE,iBAAiB,GACvB,MAAM,CA4CR;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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* via computeRelativeImport() from src/util/imports.ts.
|
|
10
10
|
*/
|
|
11
11
|
import { computeRelativeImport } from "../util/imports.js";
|
|
12
|
-
import {
|
|
12
|
+
import { detectArrayWrappers, detectChildrenTypes } from "../util/catalogMeta.js";
|
|
13
13
|
/**
|
|
14
14
|
* Emits vitest.config.ts content.
|
|
15
15
|
*
|
|
@@ -17,12 +17,13 @@ import { generateAllOperationMocks } from "./mockData.js";
|
|
|
17
17
|
* config file location, not the working directory.
|
|
18
18
|
*/
|
|
19
19
|
export function emitVitestConfig() {
|
|
20
|
-
return `import {
|
|
20
|
+
return `import { defineProject } from "vitest/config";
|
|
21
|
+
import { dirname } from "node:path";
|
|
21
22
|
import { fileURLToPath } from "node:url";
|
|
22
23
|
|
|
23
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
|
|
25
|
-
export default {
|
|
26
|
+
export default defineProject({
|
|
26
27
|
test: {
|
|
27
28
|
root: __dirname,
|
|
28
29
|
include: [
|
|
@@ -31,7 +32,7 @@ export default {
|
|
|
31
32
|
],
|
|
32
33
|
testTimeout: 30000,
|
|
33
34
|
},
|
|
34
|
-
};
|
|
35
|
+
});
|
|
35
36
|
`;
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
@@ -42,12 +43,11 @@ export default {
|
|
|
42
43
|
* @param importsMode - Import extension mode
|
|
43
44
|
* @param clientMeta - Client metadata
|
|
44
45
|
* @param operations - Resolved operation metadata
|
|
45
|
-
* @param
|
|
46
|
+
* @param mocks - Pre-computed mock data map
|
|
46
47
|
*/
|
|
47
|
-
export function emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations,
|
|
48
|
+
export function emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations, mocks) {
|
|
48
49
|
const helpersDir = `${testDir}/helpers`;
|
|
49
50
|
const operationsImport = computeRelativeImport(helpersDir, `${clientDir}/operations`, importsMode);
|
|
50
|
-
const mocks = generateAllOperationMocks(catalog);
|
|
51
51
|
// Sort operations for deterministic output
|
|
52
52
|
const sortedOps = [...operations].sort((a, b) => a.operationId.localeCompare(b.operationId));
|
|
53
53
|
const methodEntries = sortedOps.map((op) => {
|
|
@@ -141,12 +141,12 @@ export async function createTestApp(
|
|
|
141
141
|
* @param testDir - Absolute path to test output directory
|
|
142
142
|
* @param importsMode - Import extension mode
|
|
143
143
|
* @param operations - Resolved operation metadata
|
|
144
|
-
* @param
|
|
144
|
+
* @param mocks - Pre-computed mock data map
|
|
145
145
|
*/
|
|
146
|
-
|
|
146
|
+
// noinspection JSUnusedLocalSymbols
|
|
147
|
+
export function emitRoutesTest(testDir, importsMode, operations, mocks) {
|
|
147
148
|
const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
|
|
148
149
|
const testAppImport = `../helpers/test-app${suffix}`;
|
|
149
|
-
const mocks = generateAllOperationMocks(catalog);
|
|
150
150
|
// Sort operations for deterministic output
|
|
151
151
|
const sortedOps = [...operations].sort((a, b) => a.operationId.localeCompare(b.operationId));
|
|
152
152
|
const testCases = sortedOps.map((op) => {
|
|
@@ -191,9 +191,10 @@ ${testCases}
|
|
|
191
191
|
* @param testDir - Absolute path to test output directory
|
|
192
192
|
* @param importsMode - Import extension mode
|
|
193
193
|
* @param operations - Resolved operation metadata (uses first operation for error tests)
|
|
194
|
-
* @param
|
|
194
|
+
* @param mocks - Pre-computed mock data map
|
|
195
195
|
*/
|
|
196
|
-
|
|
196
|
+
// noinspection JSUnusedLocalSymbols
|
|
197
|
+
export function emitErrorsTest(testDir, importsMode, operations, mocks) {
|
|
197
198
|
const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
|
|
198
199
|
const testAppImport = `../helpers/test-app${suffix}`;
|
|
199
200
|
const mockClientImport = `../helpers/mock-client${suffix}`;
|
|
@@ -202,7 +203,6 @@ export function emitErrorsTest(testDir, importsMode, operations, catalog) {
|
|
|
202
203
|
const op = sortedOps[0];
|
|
203
204
|
if (!op)
|
|
204
205
|
return "// No operations found\n";
|
|
205
|
-
const mocks = generateAllOperationMocks(catalog);
|
|
206
206
|
const mockData = mocks.get(op.operationId);
|
|
207
207
|
const requestPayload = JSON.stringify(mockData?.request ?? {}, null, 4).replace(/\n/g, "\n ");
|
|
208
208
|
return `/**
|
|
@@ -325,7 +325,8 @@ describe("gateway routes — error handling", () => {
|
|
|
325
325
|
/**
|
|
326
326
|
* Emits gateway/envelope.test.ts with SUCCESS/ERROR structure assertions.
|
|
327
327
|
*/
|
|
328
|
-
|
|
328
|
+
// noinspection JSUnusedLocalSymbols
|
|
329
|
+
export function emitEnvelopeTest(testDir, importsMode, operations, mocks) {
|
|
329
330
|
// noinspection DuplicatedCode
|
|
330
331
|
const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
|
|
331
332
|
const testAppImport = `../helpers/test-app${suffix}`;
|
|
@@ -334,7 +335,6 @@ export function emitEnvelopeTest(testDir, importsMode, operations, catalog) {
|
|
|
334
335
|
const op = sortedOps[0];
|
|
335
336
|
if (!op)
|
|
336
337
|
return "// No operations found\n";
|
|
337
|
-
const mocks = generateAllOperationMocks(catalog);
|
|
338
338
|
const mockData = mocks.get(op.operationId);
|
|
339
339
|
const requestPayload = JSON.stringify(mockData?.request ?? {}, null, 4).replace(/\n/g, "\n ");
|
|
340
340
|
return `/**
|
|
@@ -399,6 +399,7 @@ describe("gateway — envelope structure", () => {
|
|
|
399
399
|
/**
|
|
400
400
|
* Emits gateway/validation.test.ts with invalid payload tests per route.
|
|
401
401
|
*/
|
|
402
|
+
// noinspection JSUnusedLocalSymbols
|
|
402
403
|
export function emitValidationTest(testDir, importsMode, operations) {
|
|
403
404
|
const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
|
|
404
405
|
const testAppImport = `../helpers/test-app${suffix}`;
|
|
@@ -572,10 +573,14 @@ describe("buildErrorEnvelope", () => {
|
|
|
572
573
|
* Returns null if no array wrappers exist in the catalog.
|
|
573
574
|
*/
|
|
574
575
|
export function emitUnwrapTest(testDir, gatewayDir, importsMode, catalog) {
|
|
575
|
-
// Detect array wrappers
|
|
576
|
-
const
|
|
576
|
+
// Detect array wrappers using shared utility with full type definitions
|
|
577
|
+
const types = catalog.types ?? [];
|
|
578
|
+
const wrappers = detectArrayWrappers(types);
|
|
577
579
|
if (Object.keys(wrappers).length === 0)
|
|
578
580
|
return null;
|
|
581
|
+
// Detect children-only types (in CHILDREN_TYPES but NOT in ARRAY_WRAPPERS)
|
|
582
|
+
const childTypeMap = catalog.meta?.childType ?? {};
|
|
583
|
+
const childrenOnly = detectChildrenTypes(childTypeMap, wrappers);
|
|
579
584
|
const runtimeDir = `${testDir}/runtime`;
|
|
580
585
|
const runtimeImport = computeRelativeImport(runtimeDir, `${gatewayDir}/runtime`, importsMode);
|
|
581
586
|
const wrapperTests = Object.entries(wrappers).map(([wrapperType, innerKey]) => {
|
|
@@ -585,6 +590,24 @@ export function emitUnwrapTest(testDir, gatewayDir, importsMode, catalog) {
|
|
|
585
590
|
expect(result).toEqual([{ id: 1 }]);
|
|
586
591
|
});`;
|
|
587
592
|
}).join("\n\n");
|
|
593
|
+
// Generate tests for children-only types that have nested wrappers
|
|
594
|
+
const childrenTests = [];
|
|
595
|
+
for (const [typeName, children] of Object.entries(childrenOnly)) {
|
|
596
|
+
// Find children whose types are array wrappers (these get recursively unwrapped)
|
|
597
|
+
const wrappedChildren = Object.entries(children).filter(([, childType]) => childType in wrappers);
|
|
598
|
+
if (wrappedChildren.length === 0)
|
|
599
|
+
continue;
|
|
600
|
+
const [childProp, childType] = wrappedChildren[0];
|
|
601
|
+
const innerKey = wrappers[childType];
|
|
602
|
+
childrenTests.push(` it("recursively unwraps nested wrappers in ${typeName}", () => {
|
|
603
|
+
const input = { ${childProp}: { ${innerKey}: [{ id: 1 }] } };
|
|
604
|
+
const result = unwrapArrayWrappers(input, "${typeName}");
|
|
605
|
+
expect(result).toEqual({ ${childProp}: [{ id: 1 }] });
|
|
606
|
+
});`);
|
|
607
|
+
}
|
|
608
|
+
const childrenTestBlock = childrenTests.length > 0
|
|
609
|
+
? "\n" + childrenTests.join("\n\n") + "\n"
|
|
610
|
+
: "";
|
|
588
611
|
return `/**
|
|
589
612
|
* unwrapArrayWrappers() Unit Tests
|
|
590
613
|
*
|
|
@@ -597,7 +620,7 @@ import { unwrapArrayWrappers } from "${runtimeImport}";
|
|
|
597
620
|
|
|
598
621
|
describe("unwrapArrayWrappers", () => {
|
|
599
622
|
${wrapperTests}
|
|
600
|
-
|
|
623
|
+
${childrenTestBlock}
|
|
601
624
|
it("returns empty array when inner key is missing", () => {
|
|
602
625
|
const result = unwrapArrayWrappers({}, "${Object.keys(wrappers)[0]}");
|
|
603
626
|
expect(result).toEqual([]);
|
|
@@ -611,22 +634,3 @@ ${wrapperTests}
|
|
|
611
634
|
});
|
|
612
635
|
`;
|
|
613
636
|
}
|
|
614
|
-
/**
|
|
615
|
-
* Detects ArrayOf* wrapper types from catalog (mirrors the logic in generators.ts).
|
|
616
|
-
*/
|
|
617
|
-
function detectArrayWrappersFromCatalog(catalog) {
|
|
618
|
-
const wrappers = {};
|
|
619
|
-
const childTypes = catalog.meta?.childType ?? {};
|
|
620
|
-
const propMeta = catalog.meta?.propMeta ?? {};
|
|
621
|
-
for (const [typeName, children] of Object.entries(childTypes)) {
|
|
622
|
-
const childEntries = Object.entries(children);
|
|
623
|
-
if (childEntries.length !== 1)
|
|
624
|
-
continue;
|
|
625
|
-
const [propName] = childEntries[0];
|
|
626
|
-
const meta = propMeta[typeName]?.[propName];
|
|
627
|
-
if (meta?.max === "unbounded" || (typeof meta?.max === "number" && meta.max > 1)) {
|
|
628
|
-
wrappers[typeName] = propName;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
return wrappers;
|
|
632
|
-
}
|
package/dist/test/mockData.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mock Data Generator
|
|
3
|
-
*
|
|
4
|
-
* Generates realistic mock data trees from compiled catalog type metadata.
|
|
5
|
-
* Used by the test generator to create full default responses per operation
|
|
6
|
-
* so that generated tests pass out of the box.
|
|
7
|
-
*
|
|
8
|
-
* Pure logic — no I/O or side effects.
|
|
9
|
-
*/
|
|
10
1
|
/**
|
|
11
2
|
* Options for mock data generation
|
|
12
3
|
*/
|
|
@@ -18,6 +9,7 @@ export interface MockDataOptions {
|
|
|
18
9
|
*/
|
|
19
10
|
export interface CatalogForMocks {
|
|
20
11
|
meta?: {
|
|
12
|
+
attrType?: Record<string, Record<string, string>>;
|
|
21
13
|
childType?: Record<string, Record<string, string>>;
|
|
22
14
|
propMeta?: Record<string, Record<string, {
|
|
23
15
|
declaredType?: string;
|
|
@@ -31,6 +23,16 @@ export interface CatalogForMocks {
|
|
|
31
23
|
inputTypeName?: string;
|
|
32
24
|
outputTypeName?: string;
|
|
33
25
|
}>;
|
|
26
|
+
types?: Array<{
|
|
27
|
+
name: string;
|
|
28
|
+
attrs: Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
}>;
|
|
31
|
+
elems: Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
max: number | "unbounded";
|
|
34
|
+
}>;
|
|
35
|
+
}>;
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* Generates a mock primitive value based on the TypeScript type and property name.
|
|
@@ -52,16 +54,27 @@ export declare function generateMockPrimitive(tsType: string, propName: string):
|
|
|
52
54
|
* @returns Mock data object matching the type structure
|
|
53
55
|
*/
|
|
54
56
|
export declare function generateMockData(typeName: string, catalog: CatalogForMocks, opts?: MockDataOptions, visited?: Set<string>, depth?: number): Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* Options for bulk mock generation
|
|
59
|
+
*/
|
|
60
|
+
export interface GenerateAllMocksOptions {
|
|
61
|
+
flattenArrayWrappers?: boolean;
|
|
62
|
+
}
|
|
55
63
|
/**
|
|
56
64
|
* Generates mock request and response data for all operations in the catalog.
|
|
57
65
|
*
|
|
58
66
|
* Response data uses the pre-unwrap shape (e.g. { WeatherDescription: [{...}] }
|
|
59
67
|
* not [{...}]) since the generated route handler calls unwrapArrayWrappers() at runtime.
|
|
60
68
|
*
|
|
69
|
+
* When flattenArrayWrappers is enabled, request payloads are post-processed to
|
|
70
|
+
* flatten ArrayOf* wrapper objects into plain arrays, matching the OpenAPI schema
|
|
71
|
+
* shape that AJV validates against.
|
|
72
|
+
*
|
|
61
73
|
* @param catalog - The compiled catalog with operations and type metadata
|
|
74
|
+
* @param opts - Optional generation options
|
|
62
75
|
* @returns Map from operation name to { request, response } mock data
|
|
63
76
|
*/
|
|
64
|
-
export declare function generateAllOperationMocks(catalog: CatalogForMocks): Map<string, {
|
|
77
|
+
export declare function generateAllOperationMocks(catalog: CatalogForMocks, opts?: GenerateAllMocksOptions): Map<string, {
|
|
65
78
|
request: Record<string, unknown>;
|
|
66
79
|
response: Record<string, unknown>;
|
|
67
80
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mockData.d.ts","sourceRoot":"","sources":["../../src/test/mockData.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mockData.d.ts","sourceRoot":"","sources":["../../src/test/mockData.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACnD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;YACvC,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;YAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;SACpB,CAAC,CAAC,CAAC;KACL,CAAC;IACF,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC/B,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;SAAE,CAAC,CAAC;KAC3D,CAAC,CAAC;CACJ;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAwCjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoDzB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CA+BtF"}
|
package/dist/test/mockData.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Pure logic — no I/O or side effects.
|
|
9
9
|
*/
|
|
10
|
+
import { detectArrayWrappers, flattenMockPayload } from "../util/catalogMeta.js";
|
|
10
11
|
/**
|
|
11
12
|
* Generates a mock primitive value based on the TypeScript type and property name.
|
|
12
13
|
* Uses contextual defaults based on common property names.
|
|
@@ -33,6 +34,13 @@ export function generateMockPrimitive(tsType, propName) {
|
|
|
33
34
|
return 1013;
|
|
34
35
|
return 0;
|
|
35
36
|
}
|
|
37
|
+
// String union / enum type — pick the first literal value
|
|
38
|
+
// e.g. '"Test" | "Production"' → "Test"
|
|
39
|
+
if (tsType.includes("|") && tsType.includes('"')) {
|
|
40
|
+
const match = tsType.match(/"([^"]+)"/);
|
|
41
|
+
if (match)
|
|
42
|
+
return match[1];
|
|
43
|
+
}
|
|
36
44
|
// string type — use contextual defaults
|
|
37
45
|
if (lower === "zip" || lower === "zipcode" || lower === "postalcode")
|
|
38
46
|
return "10001";
|
|
@@ -84,14 +92,15 @@ export function generateMockData(typeName, catalog, opts, visited, depth) {
|
|
|
84
92
|
return {};
|
|
85
93
|
}
|
|
86
94
|
const childTypes = catalog.meta?.childType?.[typeName];
|
|
87
|
-
|
|
95
|
+
const attrTypes = catalog.meta?.attrType?.[typeName];
|
|
96
|
+
if ((!childTypes || Object.keys(childTypes).length === 0) && !attrTypes) {
|
|
88
97
|
return {};
|
|
89
98
|
}
|
|
90
99
|
const propMeta = catalog.meta?.propMeta?.[typeName] ?? {};
|
|
91
100
|
const newVisited = new Set(currentVisited);
|
|
92
101
|
newVisited.add(typeName);
|
|
93
102
|
const result = {};
|
|
94
|
-
for (const [propName, propType] of Object.entries(childTypes)) {
|
|
103
|
+
for (const [propName, propType] of Object.entries(childTypes ?? {})) {
|
|
95
104
|
const meta = propMeta[propName];
|
|
96
105
|
const isArray = meta?.max === "unbounded" || (typeof meta?.max === "number" && meta.max > 1);
|
|
97
106
|
// Check if it's a primitive type
|
|
@@ -105,6 +114,21 @@ export function generateMockData(typeName, catalog, opts, visited, depth) {
|
|
|
105
114
|
result[propName] = isArray ? [childData] : childData;
|
|
106
115
|
}
|
|
107
116
|
}
|
|
117
|
+
// Include XML attributes (not in childType, stored separately in attrType)
|
|
118
|
+
if (attrTypes) {
|
|
119
|
+
for (const [attrName, attrTsType] of Object.entries(attrTypes)) {
|
|
120
|
+
if (!(attrName in result)) {
|
|
121
|
+
// Handle array-typed attributes (e.g. "string[]")
|
|
122
|
+
if (attrTsType.endsWith("[]")) {
|
|
123
|
+
const baseType = attrTsType.slice(0, -2);
|
|
124
|
+
result[attrName] = [generateMockPrimitive(baseType, attrName)];
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
result[attrName] = generateMockPrimitive(attrTsType, attrName);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
108
132
|
return result;
|
|
109
133
|
}
|
|
110
134
|
/**
|
|
@@ -113,17 +137,33 @@ export function generateMockData(typeName, catalog, opts, visited, depth) {
|
|
|
113
137
|
* Response data uses the pre-unwrap shape (e.g. { WeatherDescription: [{...}] }
|
|
114
138
|
* not [{...}]) since the generated route handler calls unwrapArrayWrappers() at runtime.
|
|
115
139
|
*
|
|
140
|
+
* When flattenArrayWrappers is enabled, request payloads are post-processed to
|
|
141
|
+
* flatten ArrayOf* wrapper objects into plain arrays, matching the OpenAPI schema
|
|
142
|
+
* shape that AJV validates against.
|
|
143
|
+
*
|
|
116
144
|
* @param catalog - The compiled catalog with operations and type metadata
|
|
145
|
+
* @param opts - Optional generation options
|
|
117
146
|
* @returns Map from operation name to { request, response } mock data
|
|
118
147
|
*/
|
|
119
|
-
export function generateAllOperationMocks(catalog) {
|
|
148
|
+
export function generateAllOperationMocks(catalog, opts) {
|
|
120
149
|
const result = new Map();
|
|
121
150
|
if (!catalog.operations)
|
|
122
151
|
return result;
|
|
152
|
+
// Detect array wrappers once for all operations
|
|
153
|
+
const arrayWrappers = opts?.flattenArrayWrappers !== false && catalog.types
|
|
154
|
+
? detectArrayWrappers(catalog.types)
|
|
155
|
+
: {};
|
|
156
|
+
const shouldFlatten = Object.keys(arrayWrappers).length > 0;
|
|
157
|
+
const childTypeMap = catalog.meta?.childType ?? {};
|
|
123
158
|
for (const op of catalog.operations) {
|
|
124
|
-
|
|
159
|
+
let request = op.inputTypeName
|
|
125
160
|
? generateMockData(op.inputTypeName, catalog)
|
|
126
161
|
: {};
|
|
162
|
+
// Flatten request payloads when array wrappers are active
|
|
163
|
+
if (shouldFlatten && op.inputTypeName) {
|
|
164
|
+
request = flattenMockPayload(request, op.inputTypeName, childTypeMap, arrayWrappers);
|
|
165
|
+
}
|
|
166
|
+
// Response stays SOAP-shaped (pre-unwrap) since runtime unwrapArrayWrappers() handles it
|
|
127
167
|
const response = op.outputTypeName
|
|
128
168
|
? generateMockData(op.outputTypeName, catalog)
|
|
129
169
|
: {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared catalog analysis utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides canonical array-wrapper and children-type detection used by both
|
|
5
|
+
* the gateway generator (runtime.ts emission) and the test generator.
|
|
6
|
+
*/
|
|
7
|
+
export interface CatalogTypeDef {
|
|
8
|
+
name: string;
|
|
9
|
+
attrs: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
}>;
|
|
12
|
+
elems: Array<{
|
|
13
|
+
name: string;
|
|
14
|
+
max: number | "unbounded";
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Detects ArrayOf* wrapper types: exactly 1 element with max unbounded, no attributes.
|
|
19
|
+
* Returns Record<wrapperTypeName, innerPropertyName>.
|
|
20
|
+
*/
|
|
21
|
+
export declare function detectArrayWrappers(types: CatalogTypeDef[]): Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Builds CHILDREN_TYPES map: all childType entries that are NOT array wrappers.
|
|
24
|
+
* Used by the runtime unwrapArrayWrappers() for recursive child processing.
|
|
25
|
+
*/
|
|
26
|
+
export declare function detectChildrenTypes(childTypeMap: Record<string, Record<string, string>>, arrayWrappers: Record<string, string>): Record<string, Record<string, string>>;
|
|
27
|
+
/**
|
|
28
|
+
* Post-processes a mock request payload to flatten array-wrapper fields.
|
|
29
|
+
* Replaces { InnerProp: [...items] } with [...items] for wrapper types.
|
|
30
|
+
* Recursion follows the childType map.
|
|
31
|
+
*/
|
|
32
|
+
export declare function flattenMockPayload(data: Record<string, unknown>, typeName: string, childTypeMap: Record<string, Record<string, string>>, arrayWrappers: Record<string, string>): Record<string, unknown>;
|
|
33
|
+
//# sourceMappingURL=catalogMeta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogMeta.d.ts","sourceRoot":"","sources":["../../src/util/catalogMeta.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/B,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC,CAAC;CAC3D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUnF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACpD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAOxC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACpD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoDzB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared catalog analysis utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides canonical array-wrapper and children-type detection used by both
|
|
5
|
+
* the gateway generator (runtime.ts emission) and the test generator.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Detects ArrayOf* wrapper types: exactly 1 element with max unbounded, no attributes.
|
|
9
|
+
* Returns Record<wrapperTypeName, innerPropertyName>.
|
|
10
|
+
*/
|
|
11
|
+
export function detectArrayWrappers(types) {
|
|
12
|
+
const wrappers = {};
|
|
13
|
+
for (const t of types) {
|
|
14
|
+
if (t.attrs && t.attrs.length !== 0)
|
|
15
|
+
continue;
|
|
16
|
+
if (!t.elems || t.elems.length !== 1)
|
|
17
|
+
continue;
|
|
18
|
+
const e = t.elems[0];
|
|
19
|
+
if (e.max !== "unbounded" && !(typeof e.max === "number" && e.max > 1))
|
|
20
|
+
continue;
|
|
21
|
+
wrappers[t.name] = e.name;
|
|
22
|
+
}
|
|
23
|
+
return wrappers;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Builds CHILDREN_TYPES map: all childType entries that are NOT array wrappers.
|
|
27
|
+
* Used by the runtime unwrapArrayWrappers() for recursive child processing.
|
|
28
|
+
*/
|
|
29
|
+
export function detectChildrenTypes(childTypeMap, arrayWrappers) {
|
|
30
|
+
const result = {};
|
|
31
|
+
for (const [typeName, children] of Object.entries(childTypeMap)) {
|
|
32
|
+
if (typeName in arrayWrappers)
|
|
33
|
+
continue;
|
|
34
|
+
result[typeName] = children;
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Post-processes a mock request payload to flatten array-wrapper fields.
|
|
40
|
+
* Replaces { InnerProp: [...items] } with [...items] for wrapper types.
|
|
41
|
+
* Recursion follows the childType map.
|
|
42
|
+
*/
|
|
43
|
+
export function flattenMockPayload(data, typeName, childTypeMap, arrayWrappers) {
|
|
44
|
+
const children = childTypeMap[typeName];
|
|
45
|
+
if (!children)
|
|
46
|
+
return data;
|
|
47
|
+
const result = {};
|
|
48
|
+
for (const [key, value] of Object.entries(data)) {
|
|
49
|
+
const childTypeName = children[key];
|
|
50
|
+
if (childTypeName && childTypeName in arrayWrappers && value != null && typeof value === "object" && !Array.isArray(value)) {
|
|
51
|
+
// This field's type is an array wrapper — flatten it
|
|
52
|
+
const innerKey = arrayWrappers[childTypeName];
|
|
53
|
+
const innerValue = value[innerKey];
|
|
54
|
+
// Recurse into each item of the unwrapped array
|
|
55
|
+
const itemTypeName = childTypeMap[childTypeName]?.[innerKey];
|
|
56
|
+
if (Array.isArray(innerValue) && itemTypeName) {
|
|
57
|
+
result[key] = innerValue.map(item => item != null && typeof item === "object" && !Array.isArray(item)
|
|
58
|
+
? flattenMockPayload(item, itemTypeName, childTypeMap, arrayWrappers)
|
|
59
|
+
: item);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
result[key] = innerValue ?? [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (childTypeName && value != null && typeof value === "object" && !Array.isArray(value)) {
|
|
66
|
+
// Recurse into complex types
|
|
67
|
+
result[key] = flattenMockPayload(value, childTypeName, childTypeMap, arrayWrappers);
|
|
68
|
+
}
|
|
69
|
+
else if (childTypeName && Array.isArray(value)) {
|
|
70
|
+
// Array of complex types — flatten each item
|
|
71
|
+
result[key] = value.map(item => {
|
|
72
|
+
if (item != null && typeof item === "object" && !Array.isArray(item)) {
|
|
73
|
+
if (childTypeName in arrayWrappers) {
|
|
74
|
+
const innerKey = arrayWrappers[childTypeName];
|
|
75
|
+
const innerValue = item[innerKey];
|
|
76
|
+
// Recurse into each item of the unwrapped array
|
|
77
|
+
const itemTypeName = childTypeMap[childTypeName]?.[innerKey];
|
|
78
|
+
if (Array.isArray(innerValue) && itemTypeName) {
|
|
79
|
+
return innerValue.map(subItem => subItem != null && typeof subItem === "object" && !Array.isArray(subItem)
|
|
80
|
+
? flattenMockPayload(subItem, itemTypeName, childTypeMap, arrayWrappers)
|
|
81
|
+
: subItem);
|
|
82
|
+
}
|
|
83
|
+
return innerValue ?? [];
|
|
84
|
+
}
|
|
85
|
+
return flattenMockPayload(item, childTypeName, childTypeMap, arrayWrappers);
|
|
86
|
+
}
|
|
87
|
+
return item;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
result[key] = value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techspokes/typescript-wsdl-client",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.6",
|
|
4
4
|
"description": "Generate type-safe TypeScript SOAP clients, OpenAPI 3.1 specs, and production-ready Fastify REST gateways from WSDL/XSD definitions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wsdl",
|