ng-openapi 0.2.22-pr-91-bugfix-nested-objects-parameter-fb5e988.0 → 0.2.22-pr-92-feature-single-request-parameter-0b340a2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,6 +113,7 @@ ng-openapi -i ./swagger.json -o ./src/api --date-type string
113
113
  - `customHeaders` - Headers to add to all HTTP requests
114
114
  - `responseTypeMapping` - Map content types to Angular HttpClient response types
115
115
  - `customizeMethodName` - Function to customize generated method names
116
+ - `useSingleRequestParameter` - Generate one request object parameter per method instead of positional parameters (default: `false`)
116
117
  - `compilerOptions` - TypeScript compiler options for code generation
117
118
 
118
119
  ## Generated Files Structure
package/cli.cjs CHANGED
@@ -41,7 +41,7 @@ __name(isUrl, "isUrl");
41
41
  // src/lib/cli.ts
42
42
  var import_commander = require("commander");
43
43
  var fs4 = __toESM(require("fs"));
44
- var path12 = __toESM(require("path"));
44
+ var path13 = __toESM(require("path"));
45
45
 
46
46
  // package.json
47
47
  var version = "0.2.21";
@@ -51,11 +51,11 @@ var import_ts_morph7 = require("ts-morph");
51
51
 
52
52
  // ../shared/src/utils/string.utils.ts
53
53
  function camelCase(str) {
54
- return str.replace(/[-_.\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
54
+ return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
55
55
  }
56
56
  __name(camelCase, "camelCase");
57
57
  function pascalCase(str) {
58
- return str.replace(/[-_.\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
58
+ return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
59
59
  }
60
60
  __name(pascalCase, "pascalCase");
61
61
  function pascalCaseForEnums(str) {
@@ -166,12 +166,12 @@ function extractPaths(swaggerPaths = {}, methods = [
166
166
  "head"
167
167
  ]) {
168
168
  const paths = [];
169
- Object.entries(swaggerPaths).forEach(([path13, pathItem]) => {
169
+ Object.entries(swaggerPaths).forEach(([path14, pathItem]) => {
170
170
  methods.forEach((method) => {
171
171
  if (pathItem[method]) {
172
172
  const operation = pathItem[method];
173
173
  paths.push({
174
- path: path13,
174
+ path: path14,
175
175
  method: method.toUpperCase(),
176
176
  operationId: operation.operationId,
177
177
  summary: operation.summary,
@@ -395,6 +395,10 @@ var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) =
395
395
  * Do not edit this file manually
396
396
  */
397
397
  `, "SERVICE_GENERATOR_HEADER_COMMENT");
398
+ var REQUEST_PARAMS_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated request parameter interfaces
399
+ * Do not edit this file manually
400
+ */
401
+ `;
398
402
  var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
399
403
  * Do not edit this file manually
400
404
  */
@@ -2045,7 +2049,7 @@ return httpParams.append(key, value);`
2045
2049
 
2046
2050
  // src/lib/generators/service/service.generator.ts
2047
2051
  var import_ts_morph6 = require("ts-morph");
2048
- var path9 = __toESM(require("path"));
2052
+ var path10 = __toESM(require("path"));
2049
2053
 
2050
2054
  // src/lib/generators/service/service-method/service-method-body.generator.ts
2051
2055
  var ServiceMethodBodyGenerator = class {
@@ -2299,6 +2303,73 @@ return this.httpClient.${httpMethod}(url, requestOptions)${parseResponse};`;
2299
2303
  }
2300
2304
  };
2301
2305
 
2306
+ // src/lib/generators/service/service-method/service-method-request-object.generator.ts
2307
+ var ServiceMethodRequestObjectGenerator = class {
2308
+ static {
2309
+ __name(this, "ServiceMethodRequestObjectGenerator");
2310
+ }
2311
+ /** First-occurrence-wins dedupe, shared by the flat and single-request parameter builders. */
2312
+ static dedupe(params) {
2313
+ const seen = /* @__PURE__ */ new Set();
2314
+ return params.filter((param) => {
2315
+ if (seen.has(param.name)) {
2316
+ return false;
2317
+ }
2318
+ seen.add(param.name);
2319
+ return true;
2320
+ });
2321
+ }
2322
+ static createEntry(interfaceName, parameters) {
2323
+ return {
2324
+ interfaceName,
2325
+ parameters,
2326
+ varName: this.resolveVarName(parameters),
2327
+ isOptional: parameters.every((param) => param.hasQuestionToken)
2328
+ };
2329
+ }
2330
+ static toRequestParameter(entry) {
2331
+ return {
2332
+ name: entry.varName,
2333
+ type: entry.interfaceName,
2334
+ hasQuestionToken: entry.isOptional
2335
+ };
2336
+ }
2337
+ static toInterfaceProperties(entry) {
2338
+ return entry.parameters.map((param) => ({
2339
+ name: param.name,
2340
+ type: param.type,
2341
+ hasQuestionToken: param.hasQuestionToken
2342
+ }));
2343
+ }
2344
+ /**
2345
+ * The destructuring statement placed at the top of the method body so the
2346
+ * existing body templates keep referencing plain local identifiers.
2347
+ */
2348
+ static toDestructureStatement(entry) {
2349
+ const names = entry.parameters.map((param) => param.name).join(", ");
2350
+ const source = entry.isOptional ? `${entry.varName} ?? {}` : entry.varName;
2351
+ return `const { ${names} } = ${source};`;
2352
+ }
2353
+ /** Destructured properties share the method scope, so the request variable must not collide with them. */
2354
+ static resolveVarName(parameters) {
2355
+ const used = new Set(parameters.map((param) => param.name));
2356
+ for (const candidate of [
2357
+ "request",
2358
+ "requestParams",
2359
+ "requestParameters"
2360
+ ]) {
2361
+ if (!used.has(candidate)) {
2362
+ return candidate;
2363
+ }
2364
+ }
2365
+ let suffix = 2;
2366
+ while (used.has(`requestParameters${suffix}`)) {
2367
+ suffix++;
2368
+ }
2369
+ return `requestParameters${suffix}`;
2370
+ }
2371
+ };
2372
+
2302
2373
  // src/lib/generators/service/service-method/service-method-params.generator.ts
2303
2374
  var ServiceMethodParamsGenerator = class {
2304
2375
  static {
@@ -2313,19 +2384,10 @@ var ServiceMethodParamsGenerator = class {
2313
2384
  generateMethodParameters(operation) {
2314
2385
  const params = this.generateApiParameters(operation);
2315
2386
  const optionsParam = this.addOptionsParameter(params);
2316
- const combined = [
2387
+ return ServiceMethodRequestObjectGenerator.dedupe([
2317
2388
  ...params,
2318
2389
  ...optionsParam
2319
- ];
2320
- const seen = /* @__PURE__ */ new Set();
2321
- const uniqueParams = [];
2322
- for (const param of combined) {
2323
- if (!seen.has(param.name)) {
2324
- seen.add(param.name);
2325
- uniqueParams.push(param);
2326
- }
2327
- }
2328
- return uniqueParams;
2390
+ ]);
2329
2391
  }
2330
2392
  generateApiParameters(operation) {
2331
2393
  const params = [];
@@ -2432,7 +2494,7 @@ var ServiceMethodOverloadsGenerator = class {
2432
2494
  this.config = config;
2433
2495
  this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2434
2496
  }
2435
- generateMethodOverloads(operation) {
2497
+ generateMethodOverloads(operation, requestObject) {
2436
2498
  const observeTypes = [
2437
2499
  "body",
2438
2500
  "response",
@@ -2441,38 +2503,35 @@ var ServiceMethodOverloadsGenerator = class {
2441
2503
  const overloads = [];
2442
2504
  const responseType = this.determineResponseTypeForOperation(operation);
2443
2505
  observeTypes.forEach((observe) => {
2444
- const overload = this.generateMethodOverload(operation, observe, responseType);
2506
+ const overload = this.generateMethodOverload(operation, observe, responseType, requestObject);
2445
2507
  if (overload) {
2446
2508
  overloads.push(overload);
2447
2509
  }
2448
2510
  });
2449
2511
  return overloads;
2450
2512
  }
2451
- generateMethodOverload(operation, observe, responseType) {
2513
+ generateMethodOverload(operation, observe, responseType, requestObject) {
2452
2514
  this.responseDataType = this.generateOverloadResponseType(operation);
2453
- const params = this.generateOverloadParameters(operation, observe, responseType);
2515
+ const params = requestObject ? this.generateSingleRequestOverloadParameters(requestObject, observe, responseType) : this.generateOverloadParameters(operation, observe, responseType);
2454
2516
  const returnType = this.generateOverloadReturnType(observe);
2455
2517
  return {
2456
2518
  parameters: params,
2457
2519
  returnType
2458
2520
  };
2459
2521
  }
2522
+ generateSingleRequestOverloadParameters(requestObject, observe, responseType) {
2523
+ return [
2524
+ ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
2525
+ ...this.addOverloadOptionsParameter(requestObject.parameters, observe, responseType)
2526
+ ];
2527
+ }
2460
2528
  generateOverloadParameters(operation, observe, responseType) {
2461
2529
  const params = this.paramsGenerator.generateApiParameters(operation);
2462
2530
  const optionsParam = this.addOverloadOptionsParameter(params, observe, responseType);
2463
- const combined = [
2531
+ return ServiceMethodRequestObjectGenerator.dedupe([
2464
2532
  ...params,
2465
2533
  ...optionsParam
2466
- ];
2467
- const seen = /* @__PURE__ */ new Set();
2468
- const uniqueParams = [];
2469
- for (const param of combined) {
2470
- if (!seen.has(param.name)) {
2471
- seen.add(param.name);
2472
- uniqueParams.push(param);
2473
- }
2474
- }
2475
- return uniqueParams;
2534
+ ]);
2476
2535
  }
2477
2536
  addOverloadOptionsParameter(params, observe, responseType) {
2478
2537
  return [
@@ -2550,12 +2609,16 @@ var ServiceMethodGenerator = class {
2550
2609
  this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config, parser);
2551
2610
  this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2552
2611
  }
2553
- addServiceMethod(serviceClass, operation) {
2612
+ addServiceMethod(serviceClass, operation, requestObject) {
2554
2613
  const methodName = this.generateMethodName(operation);
2555
- const parameters = this.paramsGenerator.generateMethodParameters(operation);
2614
+ const parameters = requestObject ? this.generateSingleRequestParameters(requestObject) : this.paramsGenerator.generateMethodParameters(operation);
2556
2615
  const returnType = this.generateReturnType();
2557
- const methodBody = this.bodyGenerator.generateMethodBody(operation);
2558
- const methodOverLoads = this.overloadsGenerator.generateMethodOverloads(operation);
2616
+ let methodBody = this.bodyGenerator.generateMethodBody(operation);
2617
+ if (requestObject) {
2618
+ methodBody = `${ServiceMethodRequestObjectGenerator.toDestructureStatement(requestObject)}
2619
+ ${methodBody}`;
2620
+ }
2621
+ const methodOverLoads = this.overloadsGenerator.generateMethodOverloads(operation, requestObject);
2559
2622
  serviceClass.addMethod({
2560
2623
  name: methodName,
2561
2624
  parameters,
@@ -2567,6 +2630,12 @@ var ServiceMethodGenerator = class {
2567
2630
  ] : void 0
2568
2631
  });
2569
2632
  }
2633
+ generateSingleRequestParameters(requestObject) {
2634
+ return [
2635
+ ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
2636
+ ...this.paramsGenerator.addOptionsParameter(requestObject.parameters)
2637
+ ];
2638
+ }
2570
2639
  generateMethodName(operation) {
2571
2640
  if (this.config.options.customizeMethodName) {
2572
2641
  if (operation.operationId == null) {
@@ -2593,6 +2662,97 @@ var ServiceMethodGenerator = class {
2593
2662
  }
2594
2663
  };
2595
2664
 
2665
+ // src/lib/generators/service/request-params.generator.ts
2666
+ var path9 = __toESM(require("path"));
2667
+ var RequestParamsGenerator = class {
2668
+ static {
2669
+ __name(this, "RequestParamsGenerator");
2670
+ }
2671
+ project;
2672
+ paramsGenerator;
2673
+ registry = /* @__PURE__ */ new Map();
2674
+ usedInterfaceNames = /* @__PURE__ */ new Set();
2675
+ constructor(parser, project, config) {
2676
+ this.project = project;
2677
+ this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2678
+ }
2679
+ buildRegistry(controllerGroups, getMethodName) {
2680
+ Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
2681
+ operations.forEach((operation) => {
2682
+ const parameters = ServiceMethodRequestObjectGenerator.dedupe(this.paramsGenerator.generateApiParameters(operation));
2683
+ if (parameters.length === 0) {
2684
+ return;
2685
+ }
2686
+ const reserved = parameters.find((param) => param.name === "observe" || param.name === "options");
2687
+ if (reserved) {
2688
+ throw new Error(`Parameter name '${reserved.name}' conflicts with the reserved '${reserved.name}' method parameter when useSingleRequestParameter is enabled: (${operation.method}) ${operation.path}`);
2689
+ }
2690
+ const interfaceName = this.reserveInterfaceName(controllerName, getMethodName(operation));
2691
+ this.registry.set(operation, ServiceMethodRequestObjectGenerator.createEntry(interfaceName, parameters));
2692
+ });
2693
+ });
2694
+ return this.registry;
2695
+ }
2696
+ generate(outputRoot) {
2697
+ if (this.registry.size === 0) {
2698
+ return;
2699
+ }
2700
+ const filePath = path9.join(outputRoot, "models", "request-params.ts");
2701
+ const sourceFile = this.project.createSourceFile(filePath, "", {
2702
+ overwrite: true
2703
+ });
2704
+ this.registry.forEach((entry, operation) => {
2705
+ sourceFile.addInterface({
2706
+ name: entry.interfaceName,
2707
+ isExported: true,
2708
+ properties: ServiceMethodRequestObjectGenerator.toInterfaceProperties(entry),
2709
+ docs: operation.description ? [
2710
+ operation.description
2711
+ ] : void 0
2712
+ });
2713
+ });
2714
+ sourceFile.fixMissingImports().formatText();
2715
+ sourceFile.insertText(0, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT);
2716
+ sourceFile.saveSync();
2717
+ this.addModelsBarrelExport(outputRoot);
2718
+ }
2719
+ /**
2720
+ * Method names are only unique per service class, so interfaces sharing the
2721
+ * global request-params file fall back to a controller-prefixed name on collision.
2722
+ */
2723
+ reserveInterfaceName(controllerName, methodName) {
2724
+ const base = `${pascalCase(methodName)}Params`;
2725
+ const candidates = [
2726
+ base,
2727
+ `${pascalCase(controllerName)}${base}`
2728
+ ];
2729
+ for (const candidate of candidates) {
2730
+ if (!this.usedInterfaceNames.has(candidate)) {
2731
+ this.usedInterfaceNames.add(candidate);
2732
+ return candidate;
2733
+ }
2734
+ }
2735
+ let suffix = 2;
2736
+ while (this.usedInterfaceNames.has(`${candidates[1]}${suffix}`)) {
2737
+ suffix++;
2738
+ }
2739
+ const name = `${candidates[1]}${suffix}`;
2740
+ this.usedInterfaceNames.add(name);
2741
+ return name;
2742
+ }
2743
+ addModelsBarrelExport(outputRoot) {
2744
+ const modelsIndex = this.project.getSourceFile(path9.join(outputRoot, "models", "index.ts"));
2745
+ if (!modelsIndex) {
2746
+ return;
2747
+ }
2748
+ modelsIndex.addExportDeclaration({
2749
+ moduleSpecifier: "./request-params"
2750
+ });
2751
+ modelsIndex.formatText();
2752
+ modelsIndex.saveSync();
2753
+ }
2754
+ };
2755
+
2596
2756
  // src/lib/generators/service/service.generator.ts
2597
2757
  var ServiceGenerator = class {
2598
2758
  static {
@@ -2603,6 +2763,7 @@ var ServiceGenerator = class {
2603
2763
  spec;
2604
2764
  config;
2605
2765
  methodGenerator;
2766
+ requestObjects;
2606
2767
  constructor(parser, project, config) {
2607
2768
  this.config = config;
2608
2769
  this.project = project;
@@ -2615,23 +2776,28 @@ var ServiceGenerator = class {
2615
2776
  this.methodGenerator = new ServiceMethodGenerator(config, parser);
2616
2777
  }
2617
2778
  async generate(outputRoot) {
2618
- const outputDir = path9.join(outputRoot, "services");
2779
+ const outputDir = path10.join(outputRoot, "services");
2619
2780
  const paths = extractPaths(this.spec.paths);
2620
2781
  if (paths.length === 0) {
2621
2782
  console.warn("No API paths found in the specification");
2622
2783
  return;
2623
2784
  }
2624
2785
  const controllerGroups = this.groupPathsByController(paths);
2786
+ if (this.config.options.useSingleRequestParameter) {
2787
+ const requestParamsGenerator = new RequestParamsGenerator(this.parser, this.project, this.config);
2788
+ this.requestObjects = requestParamsGenerator.buildRegistry(controllerGroups, (operation) => this.methodGenerator.generateMethodName(operation));
2789
+ requestParamsGenerator.generate(outputRoot);
2790
+ }
2625
2791
  await Promise.all(Object.entries(controllerGroups).map(([controllerName, operations]) => this.generateServiceFile(controllerName, operations, outputDir)));
2626
2792
  }
2627
2793
  groupPathsByController(paths) {
2628
2794
  const groups = {};
2629
- paths.forEach((path13) => {
2795
+ paths.forEach((path14) => {
2630
2796
  let controllerName = "Default";
2631
- if (path13.tags && path13.tags.length > 0) {
2632
- controllerName = path13.tags[0];
2797
+ if (path14.tags && path14.tags.length > 0) {
2798
+ controllerName = path14.tags[0];
2633
2799
  } else {
2634
- const pathParts = path13.path.split("/").filter((p) => p && !p.startsWith("{"));
2800
+ const pathParts = path14.path.split("/").filter((p) => p && !p.startsWith("{"));
2635
2801
  if (pathParts.length > 1) {
2636
2802
  controllerName = pascalCase(pathParts[1]);
2637
2803
  }
@@ -2640,13 +2806,13 @@ var ServiceGenerator = class {
2640
2806
  if (!groups[controllerName]) {
2641
2807
  groups[controllerName] = [];
2642
2808
  }
2643
- groups[controllerName].push(path13);
2809
+ groups[controllerName].push(path14);
2644
2810
  });
2645
2811
  return groups;
2646
2812
  }
2647
2813
  async generateServiceFile(controllerName, operations, outputDir) {
2648
2814
  const fileName = `${camelCase(controllerName)}.service.ts`;
2649
- const filePath = path9.join(outputDir, fileName);
2815
+ const filePath = path10.join(outputDir, fileName);
2650
2816
  const sourceFile = this.project.createSourceFile(filePath, "", {
2651
2817
  overwrite: true
2652
2818
  });
@@ -2747,7 +2913,7 @@ var ServiceGenerator = class {
2747
2913
  return context.set(this.clientContextToken, '${this.config.clientName || "default"}');`
2748
2914
  });
2749
2915
  operations.forEach((operation) => {
2750
- this.methodGenerator.addServiceMethod(serviceClass, operation);
2916
+ this.methodGenerator.addServiceMethod(serviceClass, operation, this.requestObjects?.get(operation));
2751
2917
  });
2752
2918
  if (hasDuplicateFunctionNames(serviceClass.getMethods())) {
2753
2919
  throw new Error(`Duplicate method names found in service class ${className}. Please ensure unique method names for each operation.`);
@@ -2757,7 +2923,7 @@ return context.set(this.clientContextToken, '${this.config.clientName || "defaul
2757
2923
 
2758
2924
  // src/lib/generators/service/service-index.generator.ts
2759
2925
  var fs2 = __toESM(require("fs"));
2760
- var path10 = __toESM(require("path"));
2926
+ var path11 = __toESM(require("path"));
2761
2927
  var ServiceIndexGenerator = class {
2762
2928
  static {
2763
2929
  __name(this, "ServiceIndexGenerator");
@@ -2767,8 +2933,8 @@ var ServiceIndexGenerator = class {
2767
2933
  this.project = project;
2768
2934
  }
2769
2935
  generateIndex(outputRoot) {
2770
- const servicesDir = path10.join(outputRoot, "services");
2771
- const indexPath = path10.join(servicesDir, "index.ts");
2936
+ const servicesDir = path11.join(outputRoot, "services");
2937
+ const indexPath = path11.join(servicesDir, "index.ts");
2772
2938
  const sourceFile = this.project.createSourceFile(indexPath, "", {
2773
2939
  overwrite: true
2774
2940
  });
@@ -2789,7 +2955,7 @@ var ServiceIndexGenerator = class {
2789
2955
 
2790
2956
  // src/lib/core/generator.ts
2791
2957
  var fs3 = __toESM(require("fs"));
2792
- var path11 = __toESM(require("path"));
2958
+ var path12 = __toESM(require("path"));
2793
2959
  function validateInput(inputPath) {
2794
2960
  if (isUrl(inputPath)) {
2795
2961
  return;
@@ -2797,7 +2963,7 @@ function validateInput(inputPath) {
2797
2963
  if (!fs3.existsSync(inputPath)) {
2798
2964
  throw new Error(`Input file not found: ${inputPath}`);
2799
2965
  }
2800
- const extension = path11.extname(inputPath).toLowerCase();
2966
+ const extension = path12.extname(inputPath).toLowerCase();
2801
2967
  const supportedExtensions = [
2802
2968
  ".json",
2803
2969
  ".yaml",
@@ -2888,7 +3054,7 @@ __name(generateFromConfig, "generateFromConfig");
2888
3054
  // src/lib/cli.ts
2889
3055
  var program = new import_commander.Command();
2890
3056
  async function loadConfigFile(configPath) {
2891
- const resolvedPath = path12.resolve(configPath);
3057
+ const resolvedPath = path13.resolve(configPath);
2892
3058
  if (!fs4.existsSync(resolvedPath)) {
2893
3059
  throw new Error(`Configuration file not found: ${resolvedPath}`);
2894
3060
  }
@@ -2902,12 +3068,12 @@ async function loadConfigFile(configPath) {
2902
3068
  if (!config.input || !config.output) {
2903
3069
  throw new Error('Configuration must include "input" and "output" properties');
2904
3070
  }
2905
- const configDir = path12.dirname(resolvedPath);
2906
- if (!isUrl(config.input) && !path12.isAbsolute(config.input)) {
2907
- config.input = path12.resolve(configDir, config.input);
3071
+ const configDir = path13.dirname(resolvedPath);
3072
+ if (!isUrl(config.input) && !path13.isAbsolute(config.input)) {
3073
+ config.input = path13.resolve(configDir, config.input);
2908
3074
  }
2909
- if (!path12.isAbsolute(config.output)) {
2910
- config.output = path12.resolve(configDir, config.output);
3075
+ if (!path13.isAbsolute(config.output)) {
3076
+ config.output = path13.resolve(configDir, config.output);
2911
3077
  }
2912
3078
  return config;
2913
3079
  } catch (error) {
package/index.d.ts CHANGED
@@ -204,6 +204,7 @@ interface GeneratorConfig {
204
204
  [contentType: string]: "json" | "blob" | "arraybuffer" | "text";
205
205
  };
206
206
  customizeMethodName?: (operationId: string) => string;
207
+ useSingleRequestParameter?: boolean;
207
208
  };
208
209
  compilerOptions?: {
209
210
  declaration?: boolean;
@@ -272,6 +273,7 @@ declare const CONTENT_TYPES: {
272
273
  declare const TYPE_GENERATOR_HEADER_COMMENT: string;
273
274
  declare const SERVICE_INDEX_GENERATOR_HEADER_COMMENT: string;
274
275
  declare const SERVICE_GENERATOR_HEADER_COMMENT: (controllerName: string) => string;
276
+ declare const REQUEST_PARAMS_GENERATOR_HEADER_COMMENT: string;
275
277
  declare const MAIN_INDEX_GENERATOR_HEADER_COMMENT: string;
276
278
  declare const PROVIDER_GENERATOR_HEADER_COMMENT: string;
277
279
  declare const BASE_INTERCEPTOR_HEADER_COMMENT: (clientName: string) => string;
@@ -288,4 +290,4 @@ declare function validateInput(inputPath: string): void;
288
290
  */
289
291
  declare function generateFromConfig(config: GeneratorConfig): Promise<void>;
290
292
 
291
- export { BASE_INTERCEPTOR_HEADER_COMMENT, CONTENT_TYPES, type EnumValueObject, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type IPluginGenerator, type IPluginGeneratorClass, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenerationContext, type NgOpenapiClientConfig, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, type RequestBody, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeSchema, ZOD_PLUGIN_GENERATOR_HEADER_COMMENT, ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT, camelCase, escapeString, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getInterceptorsTokenName, getRequestBodyType, getResponseType, getResponseTypeFromResponse, getTypeScriptType, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, kebabCase, nullableType, pascalCase, pascalCaseForEnums, type placeHolder, screamingSnakeCase, validateInput };
293
+ export { BASE_INTERCEPTOR_HEADER_COMMENT, CONTENT_TYPES, type EnumValueObject, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type IPluginGenerator, type IPluginGeneratorClass, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenerationContext, type NgOpenapiClientConfig, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT, type RequestBody, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeSchema, ZOD_PLUGIN_GENERATOR_HEADER_COMMENT, ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT, camelCase, escapeString, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getInterceptorsTokenName, getRequestBodyType, getResponseType, getResponseTypeFromResponse, getTypeScriptType, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, kebabCase, nullableType, pascalCase, pascalCaseForEnums, type placeHolder, screamingSnakeCase, validateInput };
package/index.js CHANGED
@@ -78,6 +78,7 @@ __export(index_exports, {
78
78
  HTTP_RESOURCE_GENERATOR_HEADER_COMMENT: () => HTTP_RESOURCE_GENERATOR_HEADER_COMMENT,
79
79
  MAIN_INDEX_GENERATOR_HEADER_COMMENT: () => MAIN_INDEX_GENERATOR_HEADER_COMMENT,
80
80
  PROVIDER_GENERATOR_HEADER_COMMENT: () => PROVIDER_GENERATOR_HEADER_COMMENT,
81
+ REQUEST_PARAMS_GENERATOR_HEADER_COMMENT: () => REQUEST_PARAMS_GENERATOR_HEADER_COMMENT,
81
82
  SERVICE_GENERATOR_HEADER_COMMENT: () => SERVICE_GENERATOR_HEADER_COMMENT,
82
83
  SERVICE_INDEX_GENERATOR_HEADER_COMMENT: () => SERVICE_INDEX_GENERATOR_HEADER_COMMENT,
83
84
  SwaggerParser: () => SwaggerParser,
@@ -114,7 +115,7 @@ var import_ts_morph7 = require("ts-morph");
114
115
 
115
116
  // ../shared/src/utils/string.utils.ts
116
117
  function camelCase(str) {
117
- return str.replace(/[-_.\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
118
+ return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
118
119
  }
119
120
  __name(camelCase, "camelCase");
120
121
  function kebabCase(str) {
@@ -122,7 +123,7 @@ function kebabCase(str) {
122
123
  }
123
124
  __name(kebabCase, "kebabCase");
124
125
  function pascalCase(str) {
125
- return str.replace(/[-_.\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
126
+ return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
126
127
  }
127
128
  __name(pascalCase, "pascalCase");
128
129
  function screamingSnakeCase(str) {
@@ -237,12 +238,12 @@ function extractPaths(swaggerPaths = {}, methods = [
237
238
  "head"
238
239
  ]) {
239
240
  const paths = [];
240
- Object.entries(swaggerPaths).forEach(([path12, pathItem]) => {
241
+ Object.entries(swaggerPaths).forEach(([path13, pathItem]) => {
241
242
  methods.forEach((method) => {
242
243
  if (pathItem[method]) {
243
244
  const operation = pathItem[method];
244
245
  paths.push({
245
- path: path12,
246
+ path: path13,
246
247
  method: method.toUpperCase(),
247
248
  operationId: operation.operationId,
248
249
  summary: operation.summary,
@@ -480,6 +481,10 @@ var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) =
480
481
  * Do not edit this file manually
481
482
  */
482
483
  `, "SERVICE_GENERATOR_HEADER_COMMENT");
484
+ var REQUEST_PARAMS_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated request parameter interfaces
485
+ * Do not edit this file manually
486
+ */
487
+ `;
483
488
  var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
484
489
  * Do not edit this file manually
485
490
  */
@@ -2169,7 +2174,7 @@ var HttpParamsBuilderGenerator = _HttpParamsBuilderGenerator;
2169
2174
 
2170
2175
  // src/lib/generators/service/service.generator.ts
2171
2176
  var import_ts_morph6 = require("ts-morph");
2172
- var path9 = __toESM(require("path"));
2177
+ var path10 = __toESM(require("path"));
2173
2178
 
2174
2179
  // src/lib/generators/service/service-method/service-method-body.generator.ts
2175
2180
  var _ServiceMethodBodyGenerator = class _ServiceMethodBodyGenerator {
@@ -2431,6 +2436,72 @@ return this.httpClient.${httpMethod}(url, requestOptions)${parseResponse};`;
2431
2436
  __name(_ServiceMethodBodyGenerator, "ServiceMethodBodyGenerator");
2432
2437
  var ServiceMethodBodyGenerator = _ServiceMethodBodyGenerator;
2433
2438
 
2439
+ // src/lib/generators/service/service-method/service-method-request-object.generator.ts
2440
+ var _ServiceMethodRequestObjectGenerator = class _ServiceMethodRequestObjectGenerator {
2441
+ /** First-occurrence-wins dedupe, shared by the flat and single-request parameter builders. */
2442
+ static dedupe(params) {
2443
+ const seen = /* @__PURE__ */ new Set();
2444
+ return params.filter((param) => {
2445
+ if (seen.has(param.name)) {
2446
+ return false;
2447
+ }
2448
+ seen.add(param.name);
2449
+ return true;
2450
+ });
2451
+ }
2452
+ static createEntry(interfaceName, parameters) {
2453
+ return {
2454
+ interfaceName,
2455
+ parameters,
2456
+ varName: this.resolveVarName(parameters),
2457
+ isOptional: parameters.every((param) => param.hasQuestionToken)
2458
+ };
2459
+ }
2460
+ static toRequestParameter(entry) {
2461
+ return {
2462
+ name: entry.varName,
2463
+ type: entry.interfaceName,
2464
+ hasQuestionToken: entry.isOptional
2465
+ };
2466
+ }
2467
+ static toInterfaceProperties(entry) {
2468
+ return entry.parameters.map((param) => ({
2469
+ name: param.name,
2470
+ type: param.type,
2471
+ hasQuestionToken: param.hasQuestionToken
2472
+ }));
2473
+ }
2474
+ /**
2475
+ * The destructuring statement placed at the top of the method body so the
2476
+ * existing body templates keep referencing plain local identifiers.
2477
+ */
2478
+ static toDestructureStatement(entry) {
2479
+ const names = entry.parameters.map((param) => param.name).join(", ");
2480
+ const source = entry.isOptional ? `${entry.varName} ?? {}` : entry.varName;
2481
+ return `const { ${names} } = ${source};`;
2482
+ }
2483
+ /** Destructured properties share the method scope, so the request variable must not collide with them. */
2484
+ static resolveVarName(parameters) {
2485
+ const used = new Set(parameters.map((param) => param.name));
2486
+ for (const candidate of [
2487
+ "request",
2488
+ "requestParams",
2489
+ "requestParameters"
2490
+ ]) {
2491
+ if (!used.has(candidate)) {
2492
+ return candidate;
2493
+ }
2494
+ }
2495
+ let suffix = 2;
2496
+ while (used.has(`requestParameters${suffix}`)) {
2497
+ suffix++;
2498
+ }
2499
+ return `requestParameters${suffix}`;
2500
+ }
2501
+ };
2502
+ __name(_ServiceMethodRequestObjectGenerator, "ServiceMethodRequestObjectGenerator");
2503
+ var ServiceMethodRequestObjectGenerator = _ServiceMethodRequestObjectGenerator;
2504
+
2434
2505
  // src/lib/generators/service/service-method/service-method-params.generator.ts
2435
2506
  var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
2436
2507
  constructor(config, parser) {
@@ -2442,19 +2513,10 @@ var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
2442
2513
  generateMethodParameters(operation) {
2443
2514
  const params = this.generateApiParameters(operation);
2444
2515
  const optionsParam = this.addOptionsParameter(params);
2445
- const combined = [
2516
+ return ServiceMethodRequestObjectGenerator.dedupe([
2446
2517
  ...params,
2447
2518
  ...optionsParam
2448
- ];
2449
- const seen = /* @__PURE__ */ new Set();
2450
- const uniqueParams = [];
2451
- for (const param of combined) {
2452
- if (!seen.has(param.name)) {
2453
- seen.add(param.name);
2454
- uniqueParams.push(param);
2455
- }
2456
- }
2457
- return uniqueParams;
2519
+ ]);
2458
2520
  }
2459
2521
  generateApiParameters(operation) {
2460
2522
  var _a, _b, _c, _d, _e;
@@ -2564,7 +2626,7 @@ var _ServiceMethodOverloadsGenerator = class _ServiceMethodOverloadsGenerator {
2564
2626
  this.config = config;
2565
2627
  this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2566
2628
  }
2567
- generateMethodOverloads(operation) {
2629
+ generateMethodOverloads(operation, requestObject) {
2568
2630
  const observeTypes = [
2569
2631
  "body",
2570
2632
  "response",
@@ -2573,38 +2635,35 @@ var _ServiceMethodOverloadsGenerator = class _ServiceMethodOverloadsGenerator {
2573
2635
  const overloads = [];
2574
2636
  const responseType = this.determineResponseTypeForOperation(operation);
2575
2637
  observeTypes.forEach((observe) => {
2576
- const overload = this.generateMethodOverload(operation, observe, responseType);
2638
+ const overload = this.generateMethodOverload(operation, observe, responseType, requestObject);
2577
2639
  if (overload) {
2578
2640
  overloads.push(overload);
2579
2641
  }
2580
2642
  });
2581
2643
  return overloads;
2582
2644
  }
2583
- generateMethodOverload(operation, observe, responseType) {
2645
+ generateMethodOverload(operation, observe, responseType, requestObject) {
2584
2646
  this.responseDataType = this.generateOverloadResponseType(operation);
2585
- const params = this.generateOverloadParameters(operation, observe, responseType);
2647
+ const params = requestObject ? this.generateSingleRequestOverloadParameters(requestObject, observe, responseType) : this.generateOverloadParameters(operation, observe, responseType);
2586
2648
  const returnType = this.generateOverloadReturnType(observe);
2587
2649
  return {
2588
2650
  parameters: params,
2589
2651
  returnType
2590
2652
  };
2591
2653
  }
2654
+ generateSingleRequestOverloadParameters(requestObject, observe, responseType) {
2655
+ return [
2656
+ ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
2657
+ ...this.addOverloadOptionsParameter(requestObject.parameters, observe, responseType)
2658
+ ];
2659
+ }
2592
2660
  generateOverloadParameters(operation, observe, responseType) {
2593
2661
  const params = this.paramsGenerator.generateApiParameters(operation);
2594
2662
  const optionsParam = this.addOverloadOptionsParameter(params, observe, responseType);
2595
- const combined = [
2663
+ return ServiceMethodRequestObjectGenerator.dedupe([
2596
2664
  ...params,
2597
2665
  ...optionsParam
2598
- ];
2599
- const seen = /* @__PURE__ */ new Set();
2600
- const uniqueParams = [];
2601
- for (const param of combined) {
2602
- if (!seen.has(param.name)) {
2603
- seen.add(param.name);
2604
- uniqueParams.push(param);
2605
- }
2606
- }
2607
- return uniqueParams;
2666
+ ]);
2608
2667
  }
2609
2668
  addOverloadOptionsParameter(params, observe, responseType) {
2610
2669
  return [
@@ -2684,12 +2743,16 @@ var _ServiceMethodGenerator = class _ServiceMethodGenerator {
2684
2743
  this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config, parser);
2685
2744
  this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2686
2745
  }
2687
- addServiceMethod(serviceClass, operation) {
2746
+ addServiceMethod(serviceClass, operation, requestObject) {
2688
2747
  const methodName = this.generateMethodName(operation);
2689
- const parameters = this.paramsGenerator.generateMethodParameters(operation);
2748
+ const parameters = requestObject ? this.generateSingleRequestParameters(requestObject) : this.paramsGenerator.generateMethodParameters(operation);
2690
2749
  const returnType = this.generateReturnType();
2691
- const methodBody = this.bodyGenerator.generateMethodBody(operation);
2692
- const methodOverLoads = this.overloadsGenerator.generateMethodOverloads(operation);
2750
+ let methodBody = this.bodyGenerator.generateMethodBody(operation);
2751
+ if (requestObject) {
2752
+ methodBody = `${ServiceMethodRequestObjectGenerator.toDestructureStatement(requestObject)}
2753
+ ${methodBody}`;
2754
+ }
2755
+ const methodOverLoads = this.overloadsGenerator.generateMethodOverloads(operation, requestObject);
2693
2756
  serviceClass.addMethod({
2694
2757
  name: methodName,
2695
2758
  parameters,
@@ -2701,6 +2764,12 @@ var _ServiceMethodGenerator = class _ServiceMethodGenerator {
2701
2764
  ] : void 0
2702
2765
  });
2703
2766
  }
2767
+ generateSingleRequestParameters(requestObject) {
2768
+ return [
2769
+ ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
2770
+ ...this.paramsGenerator.addOptionsParameter(requestObject.parameters)
2771
+ ];
2772
+ }
2704
2773
  generateMethodName(operation) {
2705
2774
  if (this.config.options.customizeMethodName) {
2706
2775
  if (operation.operationId == null) {
@@ -2729,6 +2798,96 @@ var _ServiceMethodGenerator = class _ServiceMethodGenerator {
2729
2798
  __name(_ServiceMethodGenerator, "ServiceMethodGenerator");
2730
2799
  var ServiceMethodGenerator = _ServiceMethodGenerator;
2731
2800
 
2801
+ // src/lib/generators/service/request-params.generator.ts
2802
+ var path9 = __toESM(require("path"));
2803
+ var _RequestParamsGenerator = class _RequestParamsGenerator {
2804
+ constructor(parser, project, config) {
2805
+ __publicField(this, "project");
2806
+ __publicField(this, "paramsGenerator");
2807
+ __publicField(this, "registry", /* @__PURE__ */ new Map());
2808
+ __publicField(this, "usedInterfaceNames", /* @__PURE__ */ new Set());
2809
+ this.project = project;
2810
+ this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
2811
+ }
2812
+ buildRegistry(controllerGroups, getMethodName) {
2813
+ Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
2814
+ operations.forEach((operation) => {
2815
+ const parameters = ServiceMethodRequestObjectGenerator.dedupe(this.paramsGenerator.generateApiParameters(operation));
2816
+ if (parameters.length === 0) {
2817
+ return;
2818
+ }
2819
+ const reserved = parameters.find((param) => param.name === "observe" || param.name === "options");
2820
+ if (reserved) {
2821
+ throw new Error(`Parameter name '${reserved.name}' conflicts with the reserved '${reserved.name}' method parameter when useSingleRequestParameter is enabled: (${operation.method}) ${operation.path}`);
2822
+ }
2823
+ const interfaceName = this.reserveInterfaceName(controllerName, getMethodName(operation));
2824
+ this.registry.set(operation, ServiceMethodRequestObjectGenerator.createEntry(interfaceName, parameters));
2825
+ });
2826
+ });
2827
+ return this.registry;
2828
+ }
2829
+ generate(outputRoot) {
2830
+ if (this.registry.size === 0) {
2831
+ return;
2832
+ }
2833
+ const filePath = path9.join(outputRoot, "models", "request-params.ts");
2834
+ const sourceFile = this.project.createSourceFile(filePath, "", {
2835
+ overwrite: true
2836
+ });
2837
+ this.registry.forEach((entry, operation) => {
2838
+ sourceFile.addInterface({
2839
+ name: entry.interfaceName,
2840
+ isExported: true,
2841
+ properties: ServiceMethodRequestObjectGenerator.toInterfaceProperties(entry),
2842
+ docs: operation.description ? [
2843
+ operation.description
2844
+ ] : void 0
2845
+ });
2846
+ });
2847
+ sourceFile.fixMissingImports().formatText();
2848
+ sourceFile.insertText(0, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT);
2849
+ sourceFile.saveSync();
2850
+ this.addModelsBarrelExport(outputRoot);
2851
+ }
2852
+ /**
2853
+ * Method names are only unique per service class, so interfaces sharing the
2854
+ * global request-params file fall back to a controller-prefixed name on collision.
2855
+ */
2856
+ reserveInterfaceName(controllerName, methodName) {
2857
+ const base = `${pascalCase(methodName)}Params`;
2858
+ const candidates = [
2859
+ base,
2860
+ `${pascalCase(controllerName)}${base}`
2861
+ ];
2862
+ for (const candidate of candidates) {
2863
+ if (!this.usedInterfaceNames.has(candidate)) {
2864
+ this.usedInterfaceNames.add(candidate);
2865
+ return candidate;
2866
+ }
2867
+ }
2868
+ let suffix = 2;
2869
+ while (this.usedInterfaceNames.has(`${candidates[1]}${suffix}`)) {
2870
+ suffix++;
2871
+ }
2872
+ const name = `${candidates[1]}${suffix}`;
2873
+ this.usedInterfaceNames.add(name);
2874
+ return name;
2875
+ }
2876
+ addModelsBarrelExport(outputRoot) {
2877
+ const modelsIndex = this.project.getSourceFile(path9.join(outputRoot, "models", "index.ts"));
2878
+ if (!modelsIndex) {
2879
+ return;
2880
+ }
2881
+ modelsIndex.addExportDeclaration({
2882
+ moduleSpecifier: "./request-params"
2883
+ });
2884
+ modelsIndex.formatText();
2885
+ modelsIndex.saveSync();
2886
+ }
2887
+ };
2888
+ __name(_RequestParamsGenerator, "RequestParamsGenerator");
2889
+ var RequestParamsGenerator = _RequestParamsGenerator;
2890
+
2732
2891
  // src/lib/generators/service/service.generator.ts
2733
2892
  var _ServiceGenerator = class _ServiceGenerator {
2734
2893
  constructor(parser, project, config) {
@@ -2737,6 +2896,7 @@ var _ServiceGenerator = class _ServiceGenerator {
2737
2896
  __publicField(this, "spec");
2738
2897
  __publicField(this, "config");
2739
2898
  __publicField(this, "methodGenerator");
2899
+ __publicField(this, "requestObjects");
2740
2900
  this.config = config;
2741
2901
  this.project = project;
2742
2902
  this.parser = parser;
@@ -2749,24 +2909,29 @@ var _ServiceGenerator = class _ServiceGenerator {
2749
2909
  }
2750
2910
  generate(outputRoot) {
2751
2911
  return __async(this, null, function* () {
2752
- const outputDir = path9.join(outputRoot, "services");
2912
+ const outputDir = path10.join(outputRoot, "services");
2753
2913
  const paths = extractPaths(this.spec.paths);
2754
2914
  if (paths.length === 0) {
2755
2915
  console.warn("No API paths found in the specification");
2756
2916
  return;
2757
2917
  }
2758
2918
  const controllerGroups = this.groupPathsByController(paths);
2919
+ if (this.config.options.useSingleRequestParameter) {
2920
+ const requestParamsGenerator = new RequestParamsGenerator(this.parser, this.project, this.config);
2921
+ this.requestObjects = requestParamsGenerator.buildRegistry(controllerGroups, (operation) => this.methodGenerator.generateMethodName(operation));
2922
+ requestParamsGenerator.generate(outputRoot);
2923
+ }
2759
2924
  yield Promise.all(Object.entries(controllerGroups).map(([controllerName, operations]) => this.generateServiceFile(controllerName, operations, outputDir)));
2760
2925
  });
2761
2926
  }
2762
2927
  groupPathsByController(paths) {
2763
2928
  const groups = {};
2764
- paths.forEach((path12) => {
2929
+ paths.forEach((path13) => {
2765
2930
  let controllerName = "Default";
2766
- if (path12.tags && path12.tags.length > 0) {
2767
- controllerName = path12.tags[0];
2931
+ if (path13.tags && path13.tags.length > 0) {
2932
+ controllerName = path13.tags[0];
2768
2933
  } else {
2769
- const pathParts = path12.path.split("/").filter((p) => p && !p.startsWith("{"));
2934
+ const pathParts = path13.path.split("/").filter((p) => p && !p.startsWith("{"));
2770
2935
  if (pathParts.length > 1) {
2771
2936
  controllerName = pascalCase(pathParts[1]);
2772
2937
  }
@@ -2775,14 +2940,14 @@ var _ServiceGenerator = class _ServiceGenerator {
2775
2940
  if (!groups[controllerName]) {
2776
2941
  groups[controllerName] = [];
2777
2942
  }
2778
- groups[controllerName].push(path12);
2943
+ groups[controllerName].push(path13);
2779
2944
  });
2780
2945
  return groups;
2781
2946
  }
2782
2947
  generateServiceFile(controllerName, operations, outputDir) {
2783
2948
  return __async(this, null, function* () {
2784
2949
  const fileName = `${camelCase(controllerName)}.service.ts`;
2785
- const filePath = path9.join(outputDir, fileName);
2950
+ const filePath = path10.join(outputDir, fileName);
2786
2951
  const sourceFile = this.project.createSourceFile(filePath, "", {
2787
2952
  overwrite: true
2788
2953
  });
@@ -2884,7 +3049,8 @@ var _ServiceGenerator = class _ServiceGenerator {
2884
3049
  return context.set(this.clientContextToken, '${this.config.clientName || "default"}');`
2885
3050
  });
2886
3051
  operations.forEach((operation) => {
2887
- this.methodGenerator.addServiceMethod(serviceClass, operation);
3052
+ var _a;
3053
+ this.methodGenerator.addServiceMethod(serviceClass, operation, (_a = this.requestObjects) == null ? void 0 : _a.get(operation));
2888
3054
  });
2889
3055
  if (hasDuplicateFunctionNames(serviceClass.getMethods())) {
2890
3056
  throw new Error(`Duplicate method names found in service class ${className}. Please ensure unique method names for each operation.`);
@@ -2896,15 +3062,15 @@ var ServiceGenerator = _ServiceGenerator;
2896
3062
 
2897
3063
  // src/lib/generators/service/service-index.generator.ts
2898
3064
  var fs2 = __toESM(require("fs"));
2899
- var path10 = __toESM(require("path"));
3065
+ var path11 = __toESM(require("path"));
2900
3066
  var _ServiceIndexGenerator = class _ServiceIndexGenerator {
2901
3067
  constructor(project) {
2902
3068
  __publicField(this, "project");
2903
3069
  this.project = project;
2904
3070
  }
2905
3071
  generateIndex(outputRoot) {
2906
- const servicesDir = path10.join(outputRoot, "services");
2907
- const indexPath = path10.join(servicesDir, "index.ts");
3072
+ const servicesDir = path11.join(outputRoot, "services");
3073
+ const indexPath = path11.join(servicesDir, "index.ts");
2908
3074
  const sourceFile = this.project.createSourceFile(indexPath, "", {
2909
3075
  overwrite: true
2910
3076
  });
@@ -2927,7 +3093,7 @@ var ServiceIndexGenerator = _ServiceIndexGenerator;
2927
3093
 
2928
3094
  // src/lib/core/generator.ts
2929
3095
  var fs3 = __toESM(require("fs"));
2930
- var path11 = __toESM(require("path"));
3096
+ var path12 = __toESM(require("path"));
2931
3097
  function validateInput(inputPath) {
2932
3098
  if (isUrl(inputPath)) {
2933
3099
  return;
@@ -2935,7 +3101,7 @@ function validateInput(inputPath) {
2935
3101
  if (!fs3.existsSync(inputPath)) {
2936
3102
  throw new Error(`Input file not found: ${inputPath}`);
2937
3103
  }
2938
- const extension = path11.extname(inputPath).toLowerCase();
3104
+ const extension = path12.extname(inputPath).toLowerCase();
2939
3105
  const supportedExtensions = [
2940
3106
  ".json",
2941
3107
  ".yaml",
@@ -3031,6 +3197,7 @@ __name(generateFromConfig, "generateFromConfig");
3031
3197
  HTTP_RESOURCE_GENERATOR_HEADER_COMMENT,
3032
3198
  MAIN_INDEX_GENERATOR_HEADER_COMMENT,
3033
3199
  PROVIDER_GENERATOR_HEADER_COMMENT,
3200
+ REQUEST_PARAMS_GENERATOR_HEADER_COMMENT,
3034
3201
  SERVICE_GENERATOR_HEADER_COMMENT,
3035
3202
  SERVICE_INDEX_GENERATOR_HEADER_COMMENT,
3036
3203
  SwaggerParser,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-openapi",
3
- "version": "0.2.22-pr-91-bugfix-nested-objects-parameter-fb5e988.0",
3
+ "version": "0.2.22-pr-92-feature-single-request-parameter-0b340a2.0",
4
4
  "description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
5
5
  "keywords": [
6
6
  "ng-openapi",