@dudousxd/nestjs-codegen 0.14.2 → 0.16.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.
@@ -42,7 +42,10 @@ __export(nest_exports, {
42
42
  CODEGEN_MODULE_OPTIONS: () => CODEGEN_MODULE_OPTIONS,
43
43
  NestjsCodegenModule: () => NestjsCodegenModule,
44
44
  NestjsCodegenService: () => NestjsCodegenService,
45
- shouldRun: () => shouldRun
45
+ QueryList: () => QueryList,
46
+ resolveQueryList: () => resolveQueryList,
47
+ shouldRun: () => shouldRun,
48
+ toStringList: () => toStringList
46
49
  });
47
50
  module.exports = __toCommonJS(nest_exports);
48
51
 
@@ -738,6 +741,9 @@ function buildErrorType(c) {
738
741
  }
739
742
  return c.contractSource.error ?? "unknown";
740
743
  }
744
+ function filterFieldLiterals(fields) {
745
+ return fields?.length ? fields.map((f) => JSON.stringify(f)) : [];
746
+ }
741
747
  function emitRouterTypeBlock(tree, indent, outDir, serialization) {
742
748
  const pad = " ".repeat(indent);
743
749
  const lines = [];
@@ -764,7 +770,8 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
764
770
  const params = buildParamsType(c.params);
765
771
  const safeMethod = JSON.stringify(method);
766
772
  const safeUrl = JSON.stringify(c.path);
767
- const filterFields = c.contractSource.filterFields?.length ? c.contractSource.filterFields.map((f) => JSON.stringify(f)).join(" | ") : "never";
773
+ const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
774
+ const filterFields = filterLiterals.length ? filterLiterals.join(" | ") : "never";
768
775
  const stream = c.contractSource.stream ? "true" : "false";
769
776
  const binary = c.contractSource.binaryResponse ? "true" : "false";
770
777
  lines.push(
@@ -804,6 +811,7 @@ function buildRequestModel(c) {
804
811
  const TA = buildRouterTypeAccess(c.name);
805
812
  const withParams = hasPathParams(c.params);
806
813
  const { isGet, isQuery, hasBody, hasQuery } = requestShape(c.route);
814
+ const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
807
815
  const fields = [];
808
816
  if (withParams) fields.push(`params: ${TA}['params']`);
809
817
  if (hasQuery) fields.push(`query?: ${TA}['query']`);
@@ -833,7 +841,12 @@ function buildRequestModel(c) {
833
841
  // (`[name]` rather than `[name, undefined]`) so the bare `.queryKey()` is a
834
842
  // clean prefix that partial-matches every parametrized variant — making it
835
843
  // directly usable for `invalidateQueries`.
836
- queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)`
844
+ queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)`,
845
+ // Runtime counterpart to the type-level `filterFields` union: the same
846
+ // discovered field list, emitted as a literal `[...] as const` so apps can
847
+ // validate a dynamic/user-supplied field string with `isFilterField(...)`
848
+ // instead of casting. Omitted for routes with no filter.
849
+ ...filterLiterals.length ? { filterFieldsExpr: `[${filterLiterals.join(", ")}] as const` } : {}
837
850
  };
838
851
  }
839
852
  function renderFetcherRequest(req, binaryResponse) {
@@ -868,9 +881,24 @@ function emitReqHelper() {
868
881
  ""
869
882
  ];
870
883
  }
884
+ function emitFilterFieldGuard() {
885
+ return [
886
+ "/** Runtime guard: narrows `value` to one of the leaf's `filterFields` (a `readonly K[] as const`), so a dynamic field string can be passed to `.where()` without a cast. */",
887
+ "export function isFilterField<const K extends string>(",
888
+ " fields: readonly K[],",
889
+ " value: string,",
890
+ "): value is K {",
891
+ " return (fields as readonly string[]).includes(value);",
892
+ "}",
893
+ ""
894
+ ];
895
+ }
871
896
  function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
872
897
  const lines = [`${pad}${objKey}: (input?: ${req.inputType}) => ({`];
873
898
  lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
899
+ if (req.filterFieldsExpr) {
900
+ lines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
901
+ }
874
902
  if (streamExpr) {
875
903
  lines.push(`${pad} stream: () => ${streamExpr},`);
876
904
  }
@@ -1142,6 +1170,9 @@ function buildApiFile(routes, outDir, opts = {}) {
1142
1170
  lines.push("};");
1143
1171
  lines.push("");
1144
1172
  lines.push(...emitReqHelper());
1173
+ if (contracted.some((r) => r.contract?.contractSource.filterFields?.length)) {
1174
+ lines.push(...emitFilterFieldGuard());
1175
+ }
1145
1176
  lines.push("export function createApi(fetcher: Fetcher) {");
1146
1177
  lines.push(" return {");
1147
1178
  lines.push(
@@ -2241,7 +2272,7 @@ var import_chokidar = __toESM(require("chokidar"), 1);
2241
2272
  // src/discovery/contracts-fast.ts
2242
2273
  var import_node_path14 = require("path");
2243
2274
  var import_fast_glob2 = __toESM(require("fast-glob"), 1);
2244
- var import_ts_morph9 = require("ts-morph");
2275
+ var import_ts_morph10 = require("ts-morph");
2245
2276
 
2246
2277
  // src/discovery/dto-type-resolver.ts
2247
2278
  var import_ts_morph7 = require("ts-morph");
@@ -3220,6 +3251,12 @@ function toFilterFieldType(name, r) {
3220
3251
  }
3221
3252
 
3222
3253
  // src/discovery/filter-for.ts
3254
+ function resolveMixinEntityClass(mixin, project) {
3255
+ const entityArg = mixin?.classArgs[0];
3256
+ if (!entityArg) return void 0;
3257
+ const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
3258
+ return file?.getClass(entityArg.name);
3259
+ }
3223
3260
  function classifyFilterForHint(typeInit) {
3224
3261
  if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
3225
3262
  switch (typeInit.getLiteralValue()) {
@@ -3293,7 +3330,9 @@ function extractFilterForHints(classDecl, project) {
3293
3330
  }
3294
3331
  return hints;
3295
3332
  }
3296
- function extractApplyFilterInfo(method, sourceFile, project) {
3333
+ function extractApplyFilterInfo(method, sourceFile, project, mixin) {
3334
+ const declFile = method.getSourceFile();
3335
+ const mixinEntity = resolveMixinEntityClass(mixin, project);
3297
3336
  for (const param of method.getParameters()) {
3298
3337
  const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
3299
3338
  if (!filterDecorator) continue;
@@ -3313,12 +3352,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
3313
3352
  }
3314
3353
  }
3315
3354
  const filterClassName = filterClassArg.getText();
3316
- const resolved = findType(filterClassName, sourceFile, project);
3317
- if (resolved && resolved.kind === "class") {
3318
- const classDecl = resolved.decl;
3355
+ const resolved = findType(filterClassName, declFile, project);
3356
+ const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
3357
+ if (classDecl) {
3319
3358
  let fieldTypes = extractClassPropertyTypes(classDecl, project);
3320
3359
  if (fieldTypes.length === 0) {
3321
- fieldTypes = extractFilterableEntityFields(classDecl, project);
3360
+ fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
3322
3361
  }
3323
3362
  const filterForHints = extractFilterForHints(classDecl, project);
3324
3363
  if (filterForHints.size > 0) {
@@ -3403,22 +3442,29 @@ function extractClassPropertyTypes(classDecl, project) {
3403
3442
  }
3404
3443
  return fields;
3405
3444
  }
3406
- function extractFilterableEntityFields(filterClass, project) {
3445
+ function resolveLocalClassDeclaration(identifier) {
3446
+ if (!import_ts_morph6.Node.isIdentifier(identifier)) return void 0;
3447
+ return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
3448
+ }
3449
+ function resolveDeclaredEntity(optionsArg, filterClass, project) {
3450
+ if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
3451
+ const entityProp = optionsArg.getProperty("entity");
3452
+ if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return void 0;
3453
+ const entityInit = entityProp.getInitializer();
3454
+ if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return void 0;
3455
+ const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
3456
+ if (!resolved || resolved.kind !== "class") return void 0;
3457
+ return resolved.decl;
3458
+ }
3459
+ function extractFilterableEntityFields(filterClass, project, entityOverride) {
3407
3460
  const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
3408
3461
  if (!filterableDecorator) return [];
3409
3462
  const args = filterableDecorator.getArguments();
3410
3463
  if (args.length === 0) return [];
3411
3464
  const optionsArg = args[0];
3412
3465
  if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
3413
- const entityProp = optionsArg.getProperty("entity");
3414
- if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return [];
3415
- const entityInit = entityProp.getInitializer();
3416
- if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
3417
- const entityName = entityInit.getText();
3418
- const filterSourceFile = filterClass.getSourceFile();
3419
- const resolvedEntity = findType(entityName, filterSourceFile, project);
3420
- if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
3421
- const entityDecl = resolvedEntity.decl;
3466
+ const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
3467
+ if (!entityDecl) return [];
3422
3468
  const fields = collectEntityFields(
3423
3469
  entityDecl,
3424
3470
  entityDecl.getSourceFile(),
@@ -3843,9 +3889,9 @@ function unwrapNamedContainer(node, names) {
3843
3889
  }
3844
3890
  return node;
3845
3891
  }
3846
- function extractDtoContract(method, sourceFile, project) {
3892
+ function extractDtoContract(method, sourceFile, project, mixin) {
3847
3893
  let body = extractBodyType(method, sourceFile, project);
3848
- const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
3894
+ const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
3849
3895
  const query = extractQueryType(method, sourceFile, project);
3850
3896
  const uploads = extractUploadedFiles(method);
3851
3897
  const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
@@ -3955,12 +4001,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
3955
4001
  return null;
3956
4002
  }
3957
4003
 
3958
- // src/discovery/zod-ast-to-ts.ts
4004
+ // src/discovery/heritage.ts
3959
4005
  var import_ts_morph8 = require("ts-morph");
4006
+ function unwrapExpression(node) {
4007
+ let current = node;
4008
+ while (import_ts_morph8.Node.isAsExpression(current) || import_ts_morph8.Node.isSatisfiesExpression(current) || import_ts_morph8.Node.isParenthesizedExpression(current) || import_ts_morph8.Node.isTypeAssertion(current)) {
4009
+ current = current.getExpression();
4010
+ }
4011
+ return current;
4012
+ }
4013
+ function resolveReturnedClass(factoryDecl) {
4014
+ const body = factoryDecl.getBody();
4015
+ if (!body) return void 0;
4016
+ const returns = body.getDescendants().filter((n) => import_ts_morph8.Node.isReturnStatement(n));
4017
+ for (const ret of returns) {
4018
+ const expr = ret.getExpression();
4019
+ if (!expr) continue;
4020
+ const inner = unwrapExpression(expr);
4021
+ if (import_ts_morph8.Node.isClassExpression(inner)) {
4022
+ return inner;
4023
+ }
4024
+ if (import_ts_morph8.Node.isIdentifier(inner)) {
4025
+ const name = inner.getText();
4026
+ const decl = body.getDescendants().find((n) => import_ts_morph8.Node.isClassDeclaration(n) && n.getName() === name);
4027
+ if (decl) return decl;
4028
+ }
4029
+ }
4030
+ return void 0;
4031
+ }
4032
+ function resolveInheritedMethods(cls) {
4033
+ const expr = cls.getExtends()?.getExpression();
4034
+ if (!expr || !import_ts_morph8.Node.isCallExpression(expr)) return void 0;
4035
+ const callee = expr.getExpression();
4036
+ if (!import_ts_morph8.Node.isIdentifier(callee)) return void 0;
4037
+ const factoryDecl = callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isFunctionDeclaration(n));
4038
+ if (!factoryDecl) return void 0;
4039
+ const returnedClass = resolveReturnedClass(factoryDecl);
4040
+ if (!returnedClass) return void 0;
4041
+ const classArgs = [];
4042
+ for (const arg of expr.getArguments()) {
4043
+ if (!import_ts_morph8.Node.isIdentifier(arg)) continue;
4044
+ const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isClassDeclaration(n));
4045
+ if (decl) {
4046
+ classArgs.push({
4047
+ name: decl.getName() ?? arg.getText(),
4048
+ filePath: decl.getSourceFile().getFilePath()
4049
+ });
4050
+ }
4051
+ }
4052
+ return {
4053
+ methods: returnedClass.getMethods(),
4054
+ factoryName: callee.getText(),
4055
+ factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
4056
+ classArgs
4057
+ };
4058
+ }
4059
+ var mixinTypeProject;
4060
+ function getMixinTypeProject() {
4061
+ mixinTypeProject ??= new import_ts_morph8.Project({
4062
+ skipAddingFilesFromTsConfig: true,
4063
+ compilerOptions: { strict: true }
4064
+ });
4065
+ return mixinTypeProject;
4066
+ }
4067
+ function clearMixinTypeProject() {
4068
+ mixinTypeProject = void 0;
4069
+ }
4070
+ function resolveInstantiatedReturnType(cls, methodName) {
4071
+ const filePath = cls.getSourceFile().getFilePath();
4072
+ const className = cls.getName();
4073
+ if (!className) return void 0;
4074
+ const project = getMixinTypeProject();
4075
+ const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPathIfExists(filePath);
4076
+ const typedCls = sourceFile?.getClass(className);
4077
+ if (!typedCls) return void 0;
4078
+ const prop = typedCls.getType().getProperty(methodName);
4079
+ if (!prop) return void 0;
4080
+ const returnType = prop.getTypeAtLocation(typedCls).getCallSignatures()[0]?.getReturnType();
4081
+ if (!returnType) return void 0;
4082
+ const unwrapped = returnType.getSymbol()?.getName() === "Promise" ? returnType.getTypeArguments()[0] ?? returnType : returnType;
4083
+ return unwrapped.getText(typedCls);
4084
+ }
4085
+
4086
+ // src/discovery/zod-ast-to-ts.ts
4087
+ var import_ts_morph9 = require("ts-morph");
3960
4088
  function zodAstToTs(node) {
3961
- if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
4089
+ if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
3962
4090
  const expr = node.getExpression();
3963
- if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
4091
+ if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
3964
4092
  const methodName = expr.getName();
3965
4093
  const receiver = expr.getExpression();
3966
4094
  if (methodName === "optional") {
@@ -3984,17 +4112,17 @@ function zodAstToTs(node) {
3984
4112
  case "literal": {
3985
4113
  const lit = args[0];
3986
4114
  if (!lit) return "unknown";
3987
- if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
3988
- if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
3989
- if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
3990
- if (lit.getKind() === import_ts_morph8.SyntaxKind.FalseKeyword) return "false";
4115
+ if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
4116
+ if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
4117
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
4118
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
3991
4119
  return "unknown";
3992
4120
  }
3993
4121
  case "enum": {
3994
4122
  const arrArg = args[0];
3995
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4123
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
3996
4124
  const members = arrArg.getElements().map(
3997
- (el) => import_ts_morph8.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
4125
+ (el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
3998
4126
  );
3999
4127
  return members.join(" | ");
4000
4128
  }
@@ -4005,10 +4133,10 @@ function zodAstToTs(node) {
4005
4133
  }
4006
4134
  case "object": {
4007
4135
  const objArg = args[0];
4008
- if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
4136
+ if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
4009
4137
  const lines = [];
4010
4138
  for (const prop of objArg.getProperties()) {
4011
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
4139
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
4012
4140
  const key = prop.getName();
4013
4141
  const valNode = prop.getInitializer();
4014
4142
  if (!valNode) continue;
@@ -4020,7 +4148,7 @@ function zodAstToTs(node) {
4020
4148
  }
4021
4149
  case "union": {
4022
4150
  const arrArg = args[0];
4023
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4151
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4024
4152
  return arrArg.getElements().map(zodAstToTs).join(" | ");
4025
4153
  }
4026
4154
  case "record": {
@@ -4030,7 +4158,7 @@ function zodAstToTs(node) {
4030
4158
  }
4031
4159
  case "tuple": {
4032
4160
  const arrArg = args[0];
4033
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4161
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4034
4162
  return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
4035
4163
  }
4036
4164
  default:
@@ -4040,18 +4168,18 @@ function zodAstToTs(node) {
4040
4168
  return "unknown";
4041
4169
  }
4042
4170
  function isOptionalChain(node) {
4043
- if (!import_ts_morph8.Node.isCallExpression(node)) return false;
4171
+ if (!import_ts_morph9.Node.isCallExpression(node)) return false;
4044
4172
  const expr = node.getExpression();
4045
- return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
4173
+ return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
4046
4174
  }
4047
4175
  function parseDefineContractCall(callExpr) {
4048
- if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
4176
+ if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
4049
4177
  const callee = callExpr.getExpression();
4050
- const calleeName = import_ts_morph8.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph8.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
4178
+ const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
4051
4179
  if (calleeName !== "defineContract") return null;
4052
4180
  const args = callExpr.getArguments();
4053
4181
  const optsArg = args[0];
4054
- if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
4182
+ if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
4055
4183
  let query = null;
4056
4184
  let body = null;
4057
4185
  let response = "unknown";
@@ -4059,7 +4187,7 @@ function parseDefineContractCall(callExpr) {
4059
4187
  let bodyZodText = null;
4060
4188
  let queryZodText = null;
4061
4189
  for (const prop of optsArg.getProperties()) {
4062
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
4190
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
4063
4191
  const propName = prop.getName();
4064
4192
  const val = prop.getInitializer();
4065
4193
  if (!val) continue;
@@ -4084,14 +4212,14 @@ function resolveTsconfigPath(cwd, tsconfig) {
4084
4212
  }
4085
4213
  function createDiscoveryProject(tsconfigPath) {
4086
4214
  try {
4087
- return new import_ts_morph9.Project({
4215
+ return new import_ts_morph10.Project({
4088
4216
  tsConfigFilePath: tsconfigPath,
4089
4217
  skipAddingFilesFromTsConfig: true,
4090
4218
  skipLoadingLibFiles: true,
4091
4219
  skipFileDependencyResolution: true
4092
4220
  });
4093
4221
  } catch {
4094
- return new import_ts_morph9.Project({
4222
+ return new import_ts_morph10.Project({
4095
4223
  skipAddingFilesFromTsConfig: true,
4096
4224
  skipLoadingLibFiles: true,
4097
4225
  skipFileDependencyResolution: true,
@@ -4191,15 +4319,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
4191
4319
  runExtraction() {
4192
4320
  clearTypeResolutionCaches(this.project);
4193
4321
  clearEnumCache(this.project);
4322
+ clearMixinTypeProject();
4194
4323
  return extractRoutesFrom(this.project, this.controllerPaths);
4195
4324
  }
4196
4325
  };
4197
4326
  function decoratorStringArg(decoratorExpr) {
4198
4327
  if (!decoratorExpr) return void 0;
4199
- if (import_ts_morph9.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
4200
- if (import_ts_morph9.Node.isArrayLiteralExpression(decoratorExpr)) {
4328
+ if (import_ts_morph10.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
4329
+ if (import_ts_morph10.Node.isArrayLiteralExpression(decoratorExpr)) {
4201
4330
  const first = decoratorExpr.getElements()[0];
4202
- if (first && import_ts_morph9.Node.isStringLiteral(first)) return first.getLiteralValue();
4331
+ if (first && import_ts_morph10.Node.isStringLiteral(first)) return first.getLiteralValue();
4203
4332
  }
4204
4333
  return void 0;
4205
4334
  }
@@ -4221,7 +4350,8 @@ function joinPaths(prefix, suffix) {
4221
4350
  if (!prefix && !suffix) return "/";
4222
4351
  if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
4223
4352
  if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
4224
- const p = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
4353
+ const withLeadingSlash = prefix.startsWith("/") ? prefix : `/${prefix}`;
4354
+ const p = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
4225
4355
  const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
4226
4356
  const combined = p + s;
4227
4357
  return combined === "" ? "/" : combined;
@@ -4275,7 +4405,8 @@ function buildRoute(args) {
4275
4405
  methodAs,
4276
4406
  sourceFile,
4277
4407
  seenNames,
4278
- contractSource
4408
+ contractSource,
4409
+ mixin
4279
4410
  } = args;
4280
4411
  const routeName = resolveRouteName(className, methodName, classAs, methodAs);
4281
4412
  const qualifiedRef = `${className}.${methodName}`;
@@ -4291,7 +4422,12 @@ function buildRoute(args) {
4291
4422
  path: combinedPath,
4292
4423
  name: routeName,
4293
4424
  params: extractParams(combinedPath),
4294
- controllerRef: { className, methodName, filePath: sourceFile.getFilePath() },
4425
+ controllerRef: {
4426
+ className,
4427
+ methodName,
4428
+ filePath: sourceFile.getFilePath(),
4429
+ ...mixin ? { mixin } : {}
4430
+ },
4295
4431
  contract: { contractSource }
4296
4432
  };
4297
4433
  }
@@ -4305,16 +4441,17 @@ function extractContractRoute(args) {
4305
4441
  className,
4306
4442
  sourceFile,
4307
4443
  project,
4308
- seenNames
4444
+ seenNames,
4445
+ mixin
4309
4446
  } = args;
4310
4447
  const firstDecoratorArg = applyContractDecorator.getArguments()[0];
4311
4448
  if (!firstDecoratorArg) return null;
4312
4449
  let contractDef = null;
4313
4450
  let bodyZodRef = null;
4314
4451
  let queryZodRef = null;
4315
- if (import_ts_morph9.Node.isCallExpression(firstDecoratorArg)) {
4452
+ if (import_ts_morph10.Node.isCallExpression(firstDecoratorArg)) {
4316
4453
  contractDef = parseDefineContractCall(firstDecoratorArg);
4317
- } else if (import_ts_morph9.Node.isIdentifier(firstDecoratorArg)) {
4454
+ } else if (import_ts_morph10.Node.isIdentifier(firstDecoratorArg)) {
4318
4455
  const identName = firstDecoratorArg.getText();
4319
4456
  const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
4320
4457
  if (!resolvedVar) {
@@ -4357,6 +4494,7 @@ function extractContractRoute(args) {
4357
4494
  methodAs,
4358
4495
  sourceFile,
4359
4496
  seenNames,
4497
+ mixin,
4360
4498
  contractSource: {
4361
4499
  query: contractDef.query,
4362
4500
  body: contractDef.body,
@@ -4373,13 +4511,14 @@ function extractContractRoute(args) {
4373
4511
  });
4374
4512
  }
4375
4513
  function extractDtoRoute(args) {
4376
- const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
4514
+ const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
4377
4515
  if (!verb) return null;
4378
4516
  const combined = joinPaths(prefix, verb.handlerPath);
4379
4517
  const methodName = method.getName();
4380
4518
  const classAs = readAsDecorator(cls, `class ${className}`);
4381
4519
  const methodAs = readAsDecorator(method, `${className}.${methodName}`);
4382
- const dtoContract = extractDtoContract(method, sourceFile, project);
4520
+ const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
4521
+ const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
4383
4522
  return buildRoute({
4384
4523
  className,
4385
4524
  methodName,
@@ -4389,10 +4528,11 @@ function extractDtoRoute(args) {
4389
4528
  methodAs,
4390
4529
  sourceFile,
4391
4530
  seenNames,
4531
+ mixin,
4392
4532
  contractSource: {
4393
4533
  query: dtoContract?.query ?? null,
4394
4534
  body: dtoContract?.body ?? null,
4395
- response: dtoContract?.response ?? "unknown",
4535
+ response: mixinResponse ?? dtoContract?.response ?? "unknown",
4396
4536
  error: dtoContract?.error ?? null,
4397
4537
  queryRef: dtoContract?.queryRef ?? null,
4398
4538
  bodyRef: dtoContract?.bodyRef ?? null,
@@ -4421,7 +4561,17 @@ function extractFromSourceFile(sourceFile, project) {
4421
4561
  const firstArg2 = controllerDecorator.getArguments()[0];
4422
4562
  const prefix = decoratorStringArg(firstArg2) ?? "";
4423
4563
  const className = cls.getName() ?? "Unknown";
4424
- for (const method of cls.getMethods()) {
4564
+ const inherited = resolveInheritedMethods(cls);
4565
+ const mixinBinding = inherited ? {
4566
+ factoryName: inherited.factoryName,
4567
+ factoryFilePath: inherited.factoryFilePath,
4568
+ classArgs: inherited.classArgs
4569
+ } : void 0;
4570
+ const methods = [
4571
+ ...cls.getMethods().map((method) => ({ method })),
4572
+ ...(inherited?.methods ?? []).map((method) => ({ method, mixin: mixinBinding }))
4573
+ ];
4574
+ for (const { method, mixin } of methods) {
4425
4575
  const verb = resolveVerb(method);
4426
4576
  const applyContractDecorator = method.getDecorator("ApplyContract");
4427
4577
  const route = applyContractDecorator ? extractContractRoute({
@@ -4433,7 +4583,8 @@ function extractFromSourceFile(sourceFile, project) {
4433
4583
  className,
4434
4584
  sourceFile,
4435
4585
  project,
4436
- seenNames
4586
+ seenNames,
4587
+ mixin
4437
4588
  }) : extractDtoRoute({
4438
4589
  cls,
4439
4590
  method,
@@ -4442,7 +4593,8 @@ function extractFromSourceFile(sourceFile, project) {
4442
4593
  className,
4443
4594
  sourceFile,
4444
4595
  project,
4445
- seenNames
4596
+ seenNames,
4597
+ mixin
4446
4598
  });
4447
4599
  if (route) routes.push(route);
4448
4600
  }
@@ -4643,7 +4795,7 @@ async function watch(config, onChange, options = {}) {
4643
4795
  }
4644
4796
 
4645
4797
  // src/index.ts
4646
- var VERSION = "0.14.2";
4798
+ var VERSION = "0.16.0";
4647
4799
 
4648
4800
  // src/generate-manifest.ts
4649
4801
  var MANIFEST_FILE = ".codegen-manifest.json";
@@ -4843,11 +4995,27 @@ var NestjsCodegenModule = class {
4843
4995
  NestjsCodegenModule = __decorateClass([
4844
4996
  (0, import_common.Module)({})
4845
4997
  ], NestjsCodegenModule);
4998
+
4999
+ // src/nest/query-list.ts
5000
+ var import_common2 = require("@nestjs/common");
5001
+ function toStringList(raw) {
5002
+ if (raw === void 0 || raw === null) return [];
5003
+ const arr = Array.isArray(raw) ? raw : String(raw).split(",");
5004
+ return arr.map((entry) => String(entry).trim()).filter((entry) => entry.length > 0);
5005
+ }
5006
+ function resolveQueryList(key, ctx) {
5007
+ const request = ctx.switchToHttp().getRequest();
5008
+ return toStringList(key ? request.query?.[key] : void 0);
5009
+ }
5010
+ var QueryList = (0, import_common2.createParamDecorator)(resolveQueryList);
4846
5011
  // Annotate the CommonJS export names for ESM import in node:
4847
5012
  0 && (module.exports = {
4848
5013
  CODEGEN_MODULE_OPTIONS,
4849
5014
  NestjsCodegenModule,
4850
5015
  NestjsCodegenService,
4851
- shouldRun
5016
+ QueryList,
5017
+ resolveQueryList,
5018
+ shouldRun,
5019
+ toStringList
4852
5020
  });
4853
5021
  //# sourceMappingURL=index.cjs.map