@techspokes/typescript-wsdl-client 0.11.3 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"generateApp.d.ts","sourceRoot":"","sources":["../../src/app/generateApp.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAklBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDzE"}
1
+ {"version":3,"file":"generateApp.d.ts","sourceRoot":"","sources":["../../src/app/generateApp.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA0kBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDzE"}
@@ -25,7 +25,7 @@ import fs from "node:fs";
25
25
  import path from "node:path";
26
26
  import { deriveClientName } from "../util/tools.js";
27
27
  import { info, success } from "../util/cli.js";
28
- import { computeRelativeImport, getImportExtension } from "../util/imports.js";
28
+ import { computeRelativeImport } from "../util/imports.js";
29
29
  /**
30
30
  * Validates that all required files and directories exist
31
31
  *
@@ -67,15 +67,6 @@ function validateRequiredFiles(opts) {
67
67
  throw new Error(`Gateway plugin entrypoint does not exist in ${opts.gatewayDir} (tried plugin.ts, plugin.js, plugin)`);
68
68
  }
69
69
  }
70
- /**
71
- * Returns the file extension for the import mode
72
- *
73
- * @param {string} imports - Import mode (js, ts, or bare)
74
- * @returns {string} - File extension with leading dot or empty string for bare
75
- */
76
- function getExtension(imports) {
77
- return getImportExtension(imports);
78
- }
79
70
  /**
80
71
  * Returns the file extension for scaffold app files (server, config).
81
72
  * Always .ts — the scaffold is TypeScript for full type safety.
@@ -295,6 +286,7 @@ main().catch((err) => {
295
286
  function generateConfigFile(appDir, opts, defaultWsdlSource, force) {
296
287
  const ext = getAppFileExtension();
297
288
  const filePath = path.join(appDir, `config${ext}`);
289
+ // noinspection DuplicatedCode
298
290
  if (!shouldWriteScaffoldFile(filePath, force))
299
291
  return;
300
292
  const defaultHost = opts.host || "127.0.0.1";
@@ -384,6 +376,7 @@ export function loadConfig(): AppConfig {
384
376
  */
385
377
  function generateEnvExample(appDir, opts, defaultWsdlSource, force) {
386
378
  const filePath = path.join(appDir, ".env.example");
379
+ // noinspection DuplicatedCode
387
380
  if (!shouldWriteScaffoldFile(filePath, force))
388
381
  return;
389
382
  const defaultHost = opts.host || "127.0.0.1";
@@ -461,7 +454,7 @@ function generatePackageJson(appDir, force) {
461
454
  dependencies: {
462
455
  fastify: "^5.7.4",
463
456
  "fastify-plugin": "^5.1.0",
464
- soap: "^1.6.5",
457
+ soap: "^1.7.1",
465
458
  },
466
459
  devDependencies: {
467
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":"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,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,CA4mB1F"}
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,
@@ -183,7 +183,7 @@ export async function generateGateway(opts) {
183
183
  }
184
184
  // Step 7: Emit plugin.ts and type-check fixture (if enabled)
185
185
  if (emitPlugin) {
186
- emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, importsMode, operations);
186
+ emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, importsMode);
187
187
  emitTypeCheckFixture(outDir, clientMeta, importsMode);
188
188
  }
189
189
  }
@@ -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
@@ -118,9 +120,8 @@ export declare function emitRuntimeModule(outDir: string, versionSlug: string, s
118
120
  * @param {string} serviceSlug - Service slug for function naming
119
121
  * @param {ClientMeta} clientMeta - Client class metadata
120
122
  * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
121
- * @param {OperationMetadata[]} operations - Operation metadata for generating the operations interface
122
123
  */
123
- export declare function emitPluginModule(outDir: string, versionSlug: string, serviceSlug: string, clientMeta: ClientMeta, importsMode: "js" | "ts" | "bare", operations: OperationMetadata[]): void;
124
+ export declare function emitPluginModule(outDir: string, versionSlug: string, serviceSlug: string, clientMeta: ClientMeta, importsMode: "js" | "ts" | "bare"): void;
124
125
  /**
125
126
  * Emits a type-check fixture that verifies plugin-client type compatibility
126
127
  *
@@ -151,6 +152,7 @@ export declare function emitTypeCheckFixture(outDir: string, clientMeta: ClientM
151
152
  * @param {OperationMetadata[]} operations - Array of operation metadata
152
153
  * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
153
154
  * @param {ClientMeta} clientMeta - Client class metadata
155
+ * @param {any} [catalog] - Compiled catalog for detecting ArrayOf* wrapper types to unwrap
154
156
  */
155
157
  export declare function emitRouteFilesWithHandlers(outDir: string, routesDir: string, versionSlug: string, serviceSlug: string, operations: OperationMetadata[], importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta, catalog?: any): void;
156
158
  //# sourceMappingURL=generators.d.ts.map
@@ -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
  }
@@ -558,9 +586,8 @@ export function createGatewayErrorHandler_${vSlug}_${sSlug}() {
558
586
  * @param {string} serviceSlug - Service slug for function naming
559
587
  * @param {ClientMeta} clientMeta - Client class metadata
560
588
  * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
561
- * @param {OperationMetadata[]} operations - Operation metadata for generating the operations interface
562
589
  */
563
- export function emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, importsMode, operations) {
590
+ export function emitPluginModule(outDir, versionSlug, serviceSlug, clientMeta, importsMode) {
564
591
  const vSlug = slugName(versionSlug);
565
592
  const sSlug = slugName(serviceSlug);
566
593
  const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
@@ -704,6 +731,7 @@ void _assertClientCompatible;
704
731
  * @param {OperationMetadata[]} operations - Array of operation metadata
705
732
  * @param {"js"|"ts"|"bare"} importsMode - Import-extension mode
706
733
  * @param {ClientMeta} clientMeta - Client class metadata
734
+ * @param {any} [catalog] - Compiled catalog for detecting ArrayOf* wrapper types to unwrap
707
735
  */
708
736
  export function emitRouteFilesWithHandlers(outDir, routesDir, versionSlug, serviceSlug, operations, importsMode, clientMeta, catalog) {
709
737
  fs.mkdirSync(routesDir, { recursive: true });
@@ -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":"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;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,KAAK,GAAE,MAAY,GAClB,MAAM,CAsCR"}
@@ -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":"AAUA,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,CAuER;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"}
@@ -1,3 +1,4 @@
1
+ // noinspection DuplicatedCode
1
2
  /**
2
3
  * Test File Emitters
3
4
  *
@@ -8,7 +9,7 @@
8
9
  * via computeRelativeImport() from src/util/imports.ts.
9
10
  */
10
11
  import { computeRelativeImport } from "../util/imports.js";
11
- import { generateAllOperationMocks } from "./mockData.js";
12
+ import { detectArrayWrappers, detectChildrenTypes } from "../util/catalogMeta.js";
12
13
  /**
13
14
  * Emits vitest.config.ts content.
14
15
  *
@@ -16,12 +17,13 @@ import { generateAllOperationMocks } from "./mockData.js";
16
17
  * config file location, not the working directory.
17
18
  */
18
19
  export function emitVitestConfig() {
19
- return `import { dirname } from "node:path";
20
+ return `import { defineProject } from "vitest/config";
21
+ import { dirname } from "node:path";
20
22
  import { fileURLToPath } from "node:url";
21
23
 
22
24
  const __dirname = dirname(fileURLToPath(import.meta.url));
23
25
 
24
- export default {
26
+ export default defineProject({
25
27
  test: {
26
28
  root: __dirname,
27
29
  include: [
@@ -30,7 +32,7 @@ export default {
30
32
  ],
31
33
  testTimeout: 30000,
32
34
  },
33
- };
35
+ });
34
36
  `;
35
37
  }
36
38
  /**
@@ -41,12 +43,11 @@ export default {
41
43
  * @param importsMode - Import extension mode
42
44
  * @param clientMeta - Client metadata
43
45
  * @param operations - Resolved operation metadata
44
- * @param catalog - Compiled catalog for mock data generation
46
+ * @param mocks - Pre-computed mock data map
45
47
  */
46
- export function emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations, catalog) {
48
+ export function emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations, mocks) {
47
49
  const helpersDir = `${testDir}/helpers`;
48
50
  const operationsImport = computeRelativeImport(helpersDir, `${clientDir}/operations`, importsMode);
49
- const mocks = generateAllOperationMocks(catalog);
50
51
  // Sort operations for deterministic output
51
52
  const sortedOps = [...operations].sort((a, b) => a.operationId.localeCompare(b.operationId));
52
53
  const methodEntries = sortedOps.map((op) => {
@@ -140,12 +141,12 @@ export async function createTestApp(
140
141
  * @param testDir - Absolute path to test output directory
141
142
  * @param importsMode - Import extension mode
142
143
  * @param operations - Resolved operation metadata
143
- * @param catalog - Compiled catalog for mock data generation
144
+ * @param mocks - Pre-computed mock data map
144
145
  */
145
- export function emitRoutesTest(testDir, importsMode, operations, catalog) {
146
+ // noinspection JSUnusedLocalSymbols
147
+ export function emitRoutesTest(testDir, importsMode, operations, mocks) {
146
148
  const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
147
149
  const testAppImport = `../helpers/test-app${suffix}`;
148
- const mocks = generateAllOperationMocks(catalog);
149
150
  // Sort operations for deterministic output
150
151
  const sortedOps = [...operations].sort((a, b) => a.operationId.localeCompare(b.operationId));
151
152
  const testCases = sortedOps.map((op) => {
@@ -190,9 +191,10 @@ ${testCases}
190
191
  * @param testDir - Absolute path to test output directory
191
192
  * @param importsMode - Import extension mode
192
193
  * @param operations - Resolved operation metadata (uses first operation for error tests)
193
- * @param catalog - Compiled catalog for mock data generation
194
+ * @param mocks - Pre-computed mock data map
194
195
  */
195
- export function emitErrorsTest(testDir, importsMode, operations, catalog) {
196
+ // noinspection JSUnusedLocalSymbols
197
+ export function emitErrorsTest(testDir, importsMode, operations, mocks) {
196
198
  const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
197
199
  const testAppImport = `../helpers/test-app${suffix}`;
198
200
  const mockClientImport = `../helpers/mock-client${suffix}`;
@@ -201,7 +203,6 @@ export function emitErrorsTest(testDir, importsMode, operations, catalog) {
201
203
  const op = sortedOps[0];
202
204
  if (!op)
203
205
  return "// No operations found\n";
204
- const mocks = generateAllOperationMocks(catalog);
205
206
  const mockData = mocks.get(op.operationId);
206
207
  const requestPayload = JSON.stringify(mockData?.request ?? {}, null, 4).replace(/\n/g, "\n ");
207
208
  return `/**
@@ -324,7 +325,9 @@ describe("gateway routes — error handling", () => {
324
325
  /**
325
326
  * Emits gateway/envelope.test.ts with SUCCESS/ERROR structure assertions.
326
327
  */
327
- export function emitEnvelopeTest(testDir, importsMode, operations, catalog) {
328
+ // noinspection JSUnusedLocalSymbols
329
+ export function emitEnvelopeTest(testDir, importsMode, operations, mocks) {
330
+ // noinspection DuplicatedCode
328
331
  const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
329
332
  const testAppImport = `../helpers/test-app${suffix}`;
330
333
  const mockClientImport = `../helpers/mock-client${suffix}`;
@@ -332,7 +335,6 @@ export function emitEnvelopeTest(testDir, importsMode, operations, catalog) {
332
335
  const op = sortedOps[0];
333
336
  if (!op)
334
337
  return "// No operations found\n";
335
- const mocks = generateAllOperationMocks(catalog);
336
338
  const mockData = mocks.get(op.operationId);
337
339
  const requestPayload = JSON.stringify(mockData?.request ?? {}, null, 4).replace(/\n/g, "\n ");
338
340
  return `/**
@@ -397,6 +399,7 @@ describe("gateway — envelope structure", () => {
397
399
  /**
398
400
  * Emits gateway/validation.test.ts with invalid payload tests per route.
399
401
  */
402
+ // noinspection JSUnusedLocalSymbols
400
403
  export function emitValidationTest(testDir, importsMode, operations) {
401
404
  const suffix = importsMode === "bare" ? "" : `.${importsMode}`;
402
405
  const testAppImport = `../helpers/test-app${suffix}`;
@@ -570,10 +573,14 @@ describe("buildErrorEnvelope", () => {
570
573
  * Returns null if no array wrappers exist in the catalog.
571
574
  */
572
575
  export function emitUnwrapTest(testDir, gatewayDir, importsMode, catalog) {
573
- // Detect array wrappers
574
- 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);
575
579
  if (Object.keys(wrappers).length === 0)
576
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);
577
584
  const runtimeDir = `${testDir}/runtime`;
578
585
  const runtimeImport = computeRelativeImport(runtimeDir, `${gatewayDir}/runtime`, importsMode);
579
586
  const wrapperTests = Object.entries(wrappers).map(([wrapperType, innerKey]) => {
@@ -583,6 +590,24 @@ export function emitUnwrapTest(testDir, gatewayDir, importsMode, catalog) {
583
590
  expect(result).toEqual([{ id: 1 }]);
584
591
  });`;
585
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
+ : "";
586
611
  return `/**
587
612
  * unwrapArrayWrappers() Unit Tests
588
613
  *
@@ -595,7 +620,7 @@ import { unwrapArrayWrappers } from "${runtimeImport}";
595
620
 
596
621
  describe("unwrapArrayWrappers", () => {
597
622
  ${wrapperTests}
598
-
623
+ ${childrenTestBlock}
599
624
  it("returns empty array when inner key is missing", () => {
600
625
  const result = unwrapArrayWrappers({}, "${Object.keys(wrappers)[0]}");
601
626
  expect(result).toEqual([]);
@@ -609,22 +634,3 @@ ${wrapperTests}
609
634
  });
610
635
  `;
611
636
  }
612
- /**
613
- * Detects ArrayOf* wrapper types from catalog (mirrors the logic in generators.ts).
614
- */
615
- function detectArrayWrappersFromCatalog(catalog) {
616
- const wrappers = {};
617
- const childTypes = catalog.meta?.childType ?? {};
618
- const propMeta = catalog.meta?.propMeta ?? {};
619
- for (const [typeName, children] of Object.entries(childTypes)) {
620
- const childEntries = Object.entries(children);
621
- if (childEntries.length !== 1)
622
- continue;
623
- const [propName] = childEntries[0];
624
- const meta = propMeta[typeName]?.[propName];
625
- if (meta?.max === "unbounded" || (typeof meta?.max === "number" && meta.max > 1)) {
626
- wrappers[typeName] = propName;
627
- }
628
- }
629
- return wrappers;
630
- }
@@ -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.3",
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",