@dudousxd/nestjs-codegen 0.15.0 → 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.
@@ -2272,7 +2272,7 @@ var import_chokidar = __toESM(require("chokidar"), 1);
2272
2272
  // src/discovery/contracts-fast.ts
2273
2273
  var import_node_path14 = require("path");
2274
2274
  var import_fast_glob2 = __toESM(require("fast-glob"), 1);
2275
- var import_ts_morph9 = require("ts-morph");
2275
+ var import_ts_morph10 = require("ts-morph");
2276
2276
 
2277
2277
  // src/discovery/dto-type-resolver.ts
2278
2278
  var import_ts_morph7 = require("ts-morph");
@@ -3251,6 +3251,12 @@ function toFilterFieldType(name, r) {
3251
3251
  }
3252
3252
 
3253
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
+ }
3254
3260
  function classifyFilterForHint(typeInit) {
3255
3261
  if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
3256
3262
  switch (typeInit.getLiteralValue()) {
@@ -3324,7 +3330,9 @@ function extractFilterForHints(classDecl, project) {
3324
3330
  }
3325
3331
  return hints;
3326
3332
  }
3327
- function extractApplyFilterInfo(method, sourceFile, project) {
3333
+ function extractApplyFilterInfo(method, sourceFile, project, mixin) {
3334
+ const declFile = method.getSourceFile();
3335
+ const mixinEntity = resolveMixinEntityClass(mixin, project);
3328
3336
  for (const param of method.getParameters()) {
3329
3337
  const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
3330
3338
  if (!filterDecorator) continue;
@@ -3344,12 +3352,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
3344
3352
  }
3345
3353
  }
3346
3354
  const filterClassName = filterClassArg.getText();
3347
- const resolved = findType(filterClassName, sourceFile, project);
3348
- if (resolved && resolved.kind === "class") {
3349
- const classDecl = resolved.decl;
3355
+ const resolved = findType(filterClassName, declFile, project);
3356
+ const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
3357
+ if (classDecl) {
3350
3358
  let fieldTypes = extractClassPropertyTypes(classDecl, project);
3351
3359
  if (fieldTypes.length === 0) {
3352
- fieldTypes = extractFilterableEntityFields(classDecl, project);
3360
+ fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
3353
3361
  }
3354
3362
  const filterForHints = extractFilterForHints(classDecl, project);
3355
3363
  if (filterForHints.size > 0) {
@@ -3434,22 +3442,29 @@ function extractClassPropertyTypes(classDecl, project) {
3434
3442
  }
3435
3443
  return fields;
3436
3444
  }
3437
- 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) {
3438
3460
  const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
3439
3461
  if (!filterableDecorator) return [];
3440
3462
  const args = filterableDecorator.getArguments();
3441
3463
  if (args.length === 0) return [];
3442
3464
  const optionsArg = args[0];
3443
3465
  if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
3444
- const entityProp = optionsArg.getProperty("entity");
3445
- if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return [];
3446
- const entityInit = entityProp.getInitializer();
3447
- if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
3448
- const entityName = entityInit.getText();
3449
- const filterSourceFile = filterClass.getSourceFile();
3450
- const resolvedEntity = findType(entityName, filterSourceFile, project);
3451
- if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
3452
- const entityDecl = resolvedEntity.decl;
3466
+ const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
3467
+ if (!entityDecl) return [];
3453
3468
  const fields = collectEntityFields(
3454
3469
  entityDecl,
3455
3470
  entityDecl.getSourceFile(),
@@ -3874,9 +3889,9 @@ function unwrapNamedContainer(node, names) {
3874
3889
  }
3875
3890
  return node;
3876
3891
  }
3877
- function extractDtoContract(method, sourceFile, project) {
3892
+ function extractDtoContract(method, sourceFile, project, mixin) {
3878
3893
  let body = extractBodyType(method, sourceFile, project);
3879
- const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
3894
+ const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
3880
3895
  const query = extractQueryType(method, sourceFile, project);
3881
3896
  const uploads = extractUploadedFiles(method);
3882
3897
  const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
@@ -3986,12 +4001,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
3986
4001
  return null;
3987
4002
  }
3988
4003
 
3989
- // src/discovery/zod-ast-to-ts.ts
4004
+ // src/discovery/heritage.ts
3990
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");
3991
4088
  function zodAstToTs(node) {
3992
- if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
4089
+ if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
3993
4090
  const expr = node.getExpression();
3994
- if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
4091
+ if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
3995
4092
  const methodName = expr.getName();
3996
4093
  const receiver = expr.getExpression();
3997
4094
  if (methodName === "optional") {
@@ -4015,17 +4112,17 @@ function zodAstToTs(node) {
4015
4112
  case "literal": {
4016
4113
  const lit = args[0];
4017
4114
  if (!lit) return "unknown";
4018
- if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
4019
- if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
4020
- if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
4021
- 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";
4022
4119
  return "unknown";
4023
4120
  }
4024
4121
  case "enum": {
4025
4122
  const arrArg = args[0];
4026
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4123
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4027
4124
  const members = arrArg.getElements().map(
4028
- (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"
4029
4126
  );
4030
4127
  return members.join(" | ");
4031
4128
  }
@@ -4036,10 +4133,10 @@ function zodAstToTs(node) {
4036
4133
  }
4037
4134
  case "object": {
4038
4135
  const objArg = args[0];
4039
- if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
4136
+ if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
4040
4137
  const lines = [];
4041
4138
  for (const prop of objArg.getProperties()) {
4042
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
4139
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
4043
4140
  const key = prop.getName();
4044
4141
  const valNode = prop.getInitializer();
4045
4142
  if (!valNode) continue;
@@ -4051,7 +4148,7 @@ function zodAstToTs(node) {
4051
4148
  }
4052
4149
  case "union": {
4053
4150
  const arrArg = args[0];
4054
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4151
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4055
4152
  return arrArg.getElements().map(zodAstToTs).join(" | ");
4056
4153
  }
4057
4154
  case "record": {
@@ -4061,7 +4158,7 @@ function zodAstToTs(node) {
4061
4158
  }
4062
4159
  case "tuple": {
4063
4160
  const arrArg = args[0];
4064
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4161
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
4065
4162
  return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
4066
4163
  }
4067
4164
  default:
@@ -4071,18 +4168,18 @@ function zodAstToTs(node) {
4071
4168
  return "unknown";
4072
4169
  }
4073
4170
  function isOptionalChain(node) {
4074
- if (!import_ts_morph8.Node.isCallExpression(node)) return false;
4171
+ if (!import_ts_morph9.Node.isCallExpression(node)) return false;
4075
4172
  const expr = node.getExpression();
4076
- return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
4173
+ return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
4077
4174
  }
4078
4175
  function parseDefineContractCall(callExpr) {
4079
- if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
4176
+ if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
4080
4177
  const callee = callExpr.getExpression();
4081
- 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() : "";
4082
4179
  if (calleeName !== "defineContract") return null;
4083
4180
  const args = callExpr.getArguments();
4084
4181
  const optsArg = args[0];
4085
- if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
4182
+ if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
4086
4183
  let query = null;
4087
4184
  let body = null;
4088
4185
  let response = "unknown";
@@ -4090,7 +4187,7 @@ function parseDefineContractCall(callExpr) {
4090
4187
  let bodyZodText = null;
4091
4188
  let queryZodText = null;
4092
4189
  for (const prop of optsArg.getProperties()) {
4093
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
4190
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
4094
4191
  const propName = prop.getName();
4095
4192
  const val = prop.getInitializer();
4096
4193
  if (!val) continue;
@@ -4115,14 +4212,14 @@ function resolveTsconfigPath(cwd, tsconfig) {
4115
4212
  }
4116
4213
  function createDiscoveryProject(tsconfigPath) {
4117
4214
  try {
4118
- return new import_ts_morph9.Project({
4215
+ return new import_ts_morph10.Project({
4119
4216
  tsConfigFilePath: tsconfigPath,
4120
4217
  skipAddingFilesFromTsConfig: true,
4121
4218
  skipLoadingLibFiles: true,
4122
4219
  skipFileDependencyResolution: true
4123
4220
  });
4124
4221
  } catch {
4125
- return new import_ts_morph9.Project({
4222
+ return new import_ts_morph10.Project({
4126
4223
  skipAddingFilesFromTsConfig: true,
4127
4224
  skipLoadingLibFiles: true,
4128
4225
  skipFileDependencyResolution: true,
@@ -4222,15 +4319,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
4222
4319
  runExtraction() {
4223
4320
  clearTypeResolutionCaches(this.project);
4224
4321
  clearEnumCache(this.project);
4322
+ clearMixinTypeProject();
4225
4323
  return extractRoutesFrom(this.project, this.controllerPaths);
4226
4324
  }
4227
4325
  };
4228
4326
  function decoratorStringArg(decoratorExpr) {
4229
4327
  if (!decoratorExpr) return void 0;
4230
- if (import_ts_morph9.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
4231
- 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)) {
4232
4330
  const first = decoratorExpr.getElements()[0];
4233
- if (first && import_ts_morph9.Node.isStringLiteral(first)) return first.getLiteralValue();
4331
+ if (first && import_ts_morph10.Node.isStringLiteral(first)) return first.getLiteralValue();
4234
4332
  }
4235
4333
  return void 0;
4236
4334
  }
@@ -4252,7 +4350,8 @@ function joinPaths(prefix, suffix) {
4252
4350
  if (!prefix && !suffix) return "/";
4253
4351
  if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
4254
4352
  if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
4255
- 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;
4256
4355
  const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
4257
4356
  const combined = p + s;
4258
4357
  return combined === "" ? "/" : combined;
@@ -4306,7 +4405,8 @@ function buildRoute(args) {
4306
4405
  methodAs,
4307
4406
  sourceFile,
4308
4407
  seenNames,
4309
- contractSource
4408
+ contractSource,
4409
+ mixin
4310
4410
  } = args;
4311
4411
  const routeName = resolveRouteName(className, methodName, classAs, methodAs);
4312
4412
  const qualifiedRef = `${className}.${methodName}`;
@@ -4322,7 +4422,12 @@ function buildRoute(args) {
4322
4422
  path: combinedPath,
4323
4423
  name: routeName,
4324
4424
  params: extractParams(combinedPath),
4325
- controllerRef: { className, methodName, filePath: sourceFile.getFilePath() },
4425
+ controllerRef: {
4426
+ className,
4427
+ methodName,
4428
+ filePath: sourceFile.getFilePath(),
4429
+ ...mixin ? { mixin } : {}
4430
+ },
4326
4431
  contract: { contractSource }
4327
4432
  };
4328
4433
  }
@@ -4336,16 +4441,17 @@ function extractContractRoute(args) {
4336
4441
  className,
4337
4442
  sourceFile,
4338
4443
  project,
4339
- seenNames
4444
+ seenNames,
4445
+ mixin
4340
4446
  } = args;
4341
4447
  const firstDecoratorArg = applyContractDecorator.getArguments()[0];
4342
4448
  if (!firstDecoratorArg) return null;
4343
4449
  let contractDef = null;
4344
4450
  let bodyZodRef = null;
4345
4451
  let queryZodRef = null;
4346
- if (import_ts_morph9.Node.isCallExpression(firstDecoratorArg)) {
4452
+ if (import_ts_morph10.Node.isCallExpression(firstDecoratorArg)) {
4347
4453
  contractDef = parseDefineContractCall(firstDecoratorArg);
4348
- } else if (import_ts_morph9.Node.isIdentifier(firstDecoratorArg)) {
4454
+ } else if (import_ts_morph10.Node.isIdentifier(firstDecoratorArg)) {
4349
4455
  const identName = firstDecoratorArg.getText();
4350
4456
  const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
4351
4457
  if (!resolvedVar) {
@@ -4388,6 +4494,7 @@ function extractContractRoute(args) {
4388
4494
  methodAs,
4389
4495
  sourceFile,
4390
4496
  seenNames,
4497
+ mixin,
4391
4498
  contractSource: {
4392
4499
  query: contractDef.query,
4393
4500
  body: contractDef.body,
@@ -4404,13 +4511,14 @@ function extractContractRoute(args) {
4404
4511
  });
4405
4512
  }
4406
4513
  function extractDtoRoute(args) {
4407
- const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
4514
+ const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
4408
4515
  if (!verb) return null;
4409
4516
  const combined = joinPaths(prefix, verb.handlerPath);
4410
4517
  const methodName = method.getName();
4411
4518
  const classAs = readAsDecorator(cls, `class ${className}`);
4412
4519
  const methodAs = readAsDecorator(method, `${className}.${methodName}`);
4413
- const dtoContract = extractDtoContract(method, sourceFile, project);
4520
+ const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
4521
+ const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
4414
4522
  return buildRoute({
4415
4523
  className,
4416
4524
  methodName,
@@ -4420,10 +4528,11 @@ function extractDtoRoute(args) {
4420
4528
  methodAs,
4421
4529
  sourceFile,
4422
4530
  seenNames,
4531
+ mixin,
4423
4532
  contractSource: {
4424
4533
  query: dtoContract?.query ?? null,
4425
4534
  body: dtoContract?.body ?? null,
4426
- response: dtoContract?.response ?? "unknown",
4535
+ response: mixinResponse ?? dtoContract?.response ?? "unknown",
4427
4536
  error: dtoContract?.error ?? null,
4428
4537
  queryRef: dtoContract?.queryRef ?? null,
4429
4538
  bodyRef: dtoContract?.bodyRef ?? null,
@@ -4452,7 +4561,17 @@ function extractFromSourceFile(sourceFile, project) {
4452
4561
  const firstArg2 = controllerDecorator.getArguments()[0];
4453
4562
  const prefix = decoratorStringArg(firstArg2) ?? "";
4454
4563
  const className = cls.getName() ?? "Unknown";
4455
- 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) {
4456
4575
  const verb = resolveVerb(method);
4457
4576
  const applyContractDecorator = method.getDecorator("ApplyContract");
4458
4577
  const route = applyContractDecorator ? extractContractRoute({
@@ -4464,7 +4583,8 @@ function extractFromSourceFile(sourceFile, project) {
4464
4583
  className,
4465
4584
  sourceFile,
4466
4585
  project,
4467
- seenNames
4586
+ seenNames,
4587
+ mixin
4468
4588
  }) : extractDtoRoute({
4469
4589
  cls,
4470
4590
  method,
@@ -4473,7 +4593,8 @@ function extractFromSourceFile(sourceFile, project) {
4473
4593
  className,
4474
4594
  sourceFile,
4475
4595
  project,
4476
- seenNames
4596
+ seenNames,
4597
+ mixin
4477
4598
  });
4478
4599
  if (route) routes.push(route);
4479
4600
  }
@@ -4674,7 +4795,7 @@ async function watch(config, onChange, options = {}) {
4674
4795
  }
4675
4796
 
4676
4797
  // src/index.ts
4677
- var VERSION = "0.15.0";
4798
+ var VERSION = "0.16.0";
4678
4799
 
4679
4800
  // src/generate-manifest.ts
4680
4801
  var MANIFEST_FILE = ".codegen-manifest.json";