@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.
@@ -454,7 +454,7 @@ function generatePackageJson(appDir, force) {
454
454
  dependencies: {
455
455
  fastify: "^5.7.4",
456
456
  "fastify-plugin": "^5.1.0",
457
- soap: "^1.6.5",
457
+ soap: "^1.7.1",
458
458
  },
459
459
  devDependencies: {
460
460
  tsx: "^4.21.0",
@@ -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":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AAKzD;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;QAC9B,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;QAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;QAC9B,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;QAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACJ,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACjD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;KAC/C,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,KAAK,CAAC;QACrB,aAAa,CAAC,EAAE,KAAK,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAiGF;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,GAAG,eAAe,CAooB1F"}
@@ -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":"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"}
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
- routeTs += ` fastify.route({\n`;
259
- routeTs += ` method: "${op.method.toUpperCase()}",\n`;
260
- routeTs += ` url: "${op.path}",\n`;
261
- routeTs += ` schema: schema as any,\n`;
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
- * An ArrayOf* wrapper has exactly one element with max "unbounded" (or > 1)
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
- const wrappers = {};
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
- return (data as Record<string, unknown>)[innerKey] ?? [];
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
- (data as Record<string, unknown>)[propName] = unwrapArrayWrappers(val, propType);
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
- schema,
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":"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;CAC3B;AAGD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,UAAU,CAkD3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,iBAAiB,CAAC,EAAE,KAAK,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC,GACD,qBAAqB,CAmBvB"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/gateway/helpers.ts"],"names":[],"mappings":"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"}
@@ -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
+ }
@@ -43,6 +43,7 @@ export interface PipelineOptions {
43
43
  test?: {
44
44
  testDir: string;
45
45
  force?: boolean;
46
+ flattenArrayWrappers?: boolean;
46
47
  };
47
48
  }
48
49
  /**
@@ -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;KACjB,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,CAgJhH"}
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
@@ -10,6 +10,7 @@ export interface GenerateTestsOptions {
10
10
  force?: boolean;
11
11
  versionSlug?: string;
12
12
  serviceSlug?: string;
13
+ flattenArrayWrappers?: boolean;
13
14
  }
14
15
  /**
15
16
  * Generates a complete Vitest test suite for the generated gateway artifacts.
@@ -1 +1 @@
1
- {"version":3,"file":"generateTests.d.ts","sourceRoot":"","sources":["../../src/test/generateTests.ts"],"names":[],"mappings":"AAgCA;;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;CACtB;AAuBD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4J7E"}
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, catalog), force);
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, catalog), force);
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, catalog), force);
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, catalog), force);
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 catalog - Compiled catalog for mock data generation
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[], catalog: CatalogForMocks): string;
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 catalog - Compiled catalog for mock data generation
41
+ * @param mocks - Pre-computed mock data map
37
42
  */
38
- export declare function emitRoutesTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], catalog: CatalogForMocks): string;
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 catalog - Compiled catalog for mock data generation
50
+ * @param mocks - Pre-computed mock data map
46
51
  */
47
- export declare function emitErrorsTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], catalog: CatalogForMocks): string;
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[], catalog: CatalogForMocks): string;
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,CAiBzC;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,OAAO,EAAE,eAAe,GACvB,MAAM,CAiDR;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;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,OAAO,EAAE,eAAe,GACvB,MAAM,CA8CR;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,OAAO,EAAE,eAAe,GACvB,MAAM,CAkIR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,OAAO,EAAE,eAAe,GACvB,MAAM,CAwER;AAED;;GAEG;AACH,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,CAyCf"}
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"}
@@ -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 { generateAllOperationMocks } from "./mockData.js";
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 { dirname } from "node:path";
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 catalog - Compiled catalog for mock data generation
46
+ * @param mocks - Pre-computed mock data map
46
47
  */
47
- export function emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations, catalog) {
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 catalog - Compiled catalog for mock data generation
144
+ * @param mocks - Pre-computed mock data map
145
145
  */
146
- export function emitRoutesTest(testDir, importsMode, operations, catalog) {
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 catalog - Compiled catalog for mock data generation
194
+ * @param mocks - Pre-computed mock data map
195
195
  */
196
- export function emitErrorsTest(testDir, importsMode, operations, catalog) {
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
- export function emitEnvelopeTest(testDir, importsMode, operations, catalog) {
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 wrappers = detectArrayWrappersFromCatalog(catalog);
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
- }
@@ -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":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE;QACL,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;CACJ;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAiCjG;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,CAoCzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,GACvB,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,CAkBtF"}
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"}
@@ -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
- if (!childTypes || Object.keys(childTypes).length === 0) {
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
- const request = op.inputTypeName
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.5",
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",