ts-class-to-openapi 1.4.1 → 1.4.3

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.
Files changed (3) hide show
  1. package/dist/index.cjs +291 -194
  2. package/dist/index.mjs +291 -194
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -28,6 +28,53 @@ path = __toESM(path);
28
28
  //#region src/transformer.fixtures.ts
29
29
  const constants = {
30
30
  TS_CONFIG_DEFAULT_PATH: path.default.resolve(process.cwd(), "tsconfig.json"),
31
+ JS_KEYWORDS: new Set([
32
+ "constructor",
33
+ "static",
34
+ "get",
35
+ "set",
36
+ "async",
37
+ "return",
38
+ "if",
39
+ "else",
40
+ "for",
41
+ "while",
42
+ "switch",
43
+ "case",
44
+ "break",
45
+ "throw",
46
+ "new",
47
+ "delete",
48
+ "typeof",
49
+ "void",
50
+ "try",
51
+ "catch",
52
+ "finally",
53
+ "this",
54
+ "super",
55
+ "class",
56
+ "const",
57
+ "let",
58
+ "var",
59
+ "function",
60
+ "yield",
61
+ "await",
62
+ "import",
63
+ "export",
64
+ "default",
65
+ "extends",
66
+ "implements",
67
+ "in",
68
+ "of",
69
+ "do",
70
+ "with",
71
+ "continue",
72
+ "instanceof",
73
+ "true",
74
+ "false",
75
+ "null",
76
+ "undefined"
77
+ ]),
31
78
  jsPrimitives: {
32
79
  String: {
33
80
  type: "String",
@@ -190,6 +237,7 @@ var SchemaTransformer = class SchemaTransformer {
190
237
  autoCleanup;
191
238
  classFileIndex = /* @__PURE__ */ new Map();
192
239
  transformCallIndex = /* @__PURE__ */ new Map();
240
+ nonGenericTransformCalls = /* @__PURE__ */ new Set();
193
241
  constructor(tsConfigPath = constants.TS_CONFIG_DEFAULT_PATH, options = {}) {
194
242
  this.maxCacheSize = options.maxCacheSize ?? 100;
195
243
  this.autoCleanup = options.autoCleanup ?? true;
@@ -210,14 +258,19 @@ var SchemaTransformer = class SchemaTransformer {
210
258
  else {
211
259
  const heritageClause = classNode.heritageClauses[0];
212
260
  if (heritageClause && heritageClause.token === typescript.default.SyntaxKind.ExtendsKeyword) {
213
- var _symbol$declarations;
214
261
  const type = heritageClause.types[0];
215
262
  let properties = [];
216
263
  let baseProperties = [];
217
264
  if (!type) return [];
218
265
  const symbol = this.checker.getSymbolAtLocation(type.expression);
219
266
  if (!symbol) return [];
220
- const declaration = (_symbol$declarations = symbol.declarations) === null || _symbol$declarations === void 0 ? void 0 : _symbol$declarations[0];
267
+ const realSymbol = symbol && symbol.flags & typescript.default.SymbolFlags.Alias ? this.checker.getAliasedSymbol(symbol) : symbol;
268
+ let declaration;
269
+ if (realSymbol && realSymbol.links && realSymbol.links.type && realSymbol.links.type.symbol && realSymbol.links.type.symbol.declarations) declaration = realSymbol.links.type.symbol.declarations[0];
270
+ else {
271
+ var _realSymbol$declarati;
272
+ declaration = realSymbol === null || realSymbol === void 0 || (_realSymbol$declarati = realSymbol.declarations) === null || _realSymbol$declarati === void 0 ? void 0 : _realSymbol$declarati.find(typescript.default.isClassDeclaration);
273
+ }
221
274
  if (declaration && typescript.default.isClassDeclaration(declaration)) {
222
275
  const newGenericTypeMap = /* @__PURE__ */ new Map();
223
276
  if (declaration.typeParameters && type.typeArguments) declaration.typeParameters.forEach((param, index) => {
@@ -236,78 +289,77 @@ var SchemaTransformer = class SchemaTransformer {
236
289
  }
237
290
  getPropertiesByClassMembers(members, parentClassNode, genericTypeMap = /* @__PURE__ */ new Map()) {
238
291
  const properties = [];
239
- for (const member of members) {
240
- var _member$name;
241
- if (typescript.default.isPropertyDeclaration(member) && member.name && ((_member$name = member.name) === null || _member$name === void 0 ? void 0 : _member$name.text)) {
242
- var _typeArguments;
243
- if (member.modifiers) {
244
- if (member.modifiers.some((m) => m.kind === typescript.default.SyntaxKind.PrivateKeyword || m.kind === typescript.default.SyntaxKind.ProtectedKeyword)) continue;
245
- }
246
- const propertyName = member.name.text;
247
- const type = this.getPropertyType(member, genericTypeMap);
248
- const decorators = this.extractDecorators(member);
249
- const isOptional = !!member.questionToken;
250
- const isGeneric = this.isPropertyTypeGeneric(member);
251
- const isEnum = this.isEnum(member);
252
- const isPrimitive = this.isPrimitiveType(type) || isEnum;
253
- const isClassType = this.isClassType(member);
254
- const isArray = this.isArrayProperty(member);
255
- const isTypeLiteral = this.isTypeLiteral(member);
256
- let genericClassReference = void 0;
257
- if (isGeneric && !isPrimitive) {
258
- const baseTypeName = type.replace(/\[\]$/, "").trim();
259
- if (!this.isPrimitiveType(baseTypeName)) {
260
- const matches = this.classFileIndex.get(baseTypeName);
261
- if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
262
- }
292
+ for (const member of members) if (typescript.default.isPropertyDeclaration(member) && (typescript.default.isIdentifier(member.name) || typescript.default.isStringLiteral(member.name))) {
293
+ var _property$originalPro;
294
+ if (member.modifiers) {
295
+ if (member.modifiers.some((m) => m.kind === typescript.default.SyntaxKind.PrivateKeyword || m.kind === typescript.default.SyntaxKind.ProtectedKeyword)) continue;
296
+ }
297
+ const propertyName = member.name.text;
298
+ const type = this.getPropertyType(member, genericTypeMap);
299
+ const decorators = this.extractDecorators(member);
300
+ const isOptional = !!member.questionToken;
301
+ const isGeneric = this.isPropertyTypeGeneric(member);
302
+ const isEnum = this.isEnum(member);
303
+ const isPrimitive = this.isPrimitiveType(type) || isEnum;
304
+ const isClassType = this.isClassType(member);
305
+ const isArray = this.isArrayProperty(member);
306
+ const isTypeLiteral = this.isTypeLiteral(member);
307
+ let genericClassReference = void 0;
308
+ if (isGeneric && !isPrimitive) {
309
+ const baseTypeName = type.replace(/\[\]$/, "").trim();
310
+ if (!this.isPrimitiveType(baseTypeName)) {
311
+ const matches = this.classFileIndex.get(baseTypeName);
312
+ if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
263
313
  }
264
- const property = {
265
- name: propertyName,
266
- type,
267
- decorators,
268
- isOptional,
269
- isGeneric,
270
- originalProperty: member,
271
- isPrimitive,
272
- isClassType,
273
- isArray,
274
- isEnum,
275
- isRef: false,
276
- isTypeLiteral,
277
- genericClassReference
278
- };
279
- if (property.isClassType) {
280
- const declaration = this.getDeclarationProperty(property);
281
- if (parentClassNode) {
282
- if (declaration && declaration.name && this.checker.getSymbolAtLocation(declaration.name) === this.checker.getSymbolAtLocation(parentClassNode.name)) property.isRef = true;
283
- }
314
+ }
315
+ const property = {
316
+ name: propertyName,
317
+ type,
318
+ decorators,
319
+ isOptional,
320
+ isGeneric,
321
+ originalProperty: member,
322
+ isPrimitive,
323
+ isClassType,
324
+ isArray,
325
+ isEnum,
326
+ isRef: false,
327
+ isTypeLiteral,
328
+ genericClassReference
329
+ };
330
+ if (property.isClassType) {
331
+ const declaration = this.getDeclarationProperty(property);
332
+ if (parentClassNode) {
333
+ if (declaration && declaration.name && this.checker.getSymbolAtLocation(declaration.name) === this.checker.getSymbolAtLocation(parentClassNode.name)) property.isRef = true;
284
334
  }
285
- if (property.isTypeLiteral && property.originalProperty.type && ((_typeArguments = property.originalProperty.type.typeArguments) === null || _typeArguments === void 0 ? void 0 : _typeArguments.length) === 1) {
286
- const typeArguments = property.originalProperty.type.typeArguments;
287
- if (typeArguments && typeArguments[0]) {
288
- const firstTypeArg = typeArguments[0];
289
- if (typescript.default.isTypeReferenceNode(firstTypeArg)) {
290
- const symbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
291
- if (symbol && symbol.declarations) {
292
- const classDeclaration = symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl));
293
- if (classDeclaration && typescript.default.isClassDeclaration(classDeclaration)) property.typeLiteralClassReference = classDeclaration;
294
- }
335
+ }
336
+ if (property.isTypeLiteral && property.originalProperty.type !== void 0 && typescript.default.isTypeReferenceNode(property.originalProperty.type) && ((_property$originalPro = property.originalProperty.type.typeArguments) === null || _property$originalPro === void 0 ? void 0 : _property$originalPro.length) === 1) {
337
+ const typeArguments = property.originalProperty.type.typeArguments;
338
+ if (typeArguments && typeArguments[0]) {
339
+ const firstTypeArg = typeArguments[0];
340
+ if (typescript.default.isTypeReferenceNode(firstTypeArg)) {
341
+ const symbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
342
+ if (symbol && symbol.declarations) {
343
+ const classDeclaration = symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl));
344
+ if (classDeclaration && typescript.default.isClassDeclaration(classDeclaration)) property.typeLiteralClassReference = classDeclaration;
295
345
  }
296
346
  }
297
347
  }
298
- properties.push(property);
299
348
  }
349
+ properties.push(property);
300
350
  }
301
351
  return properties;
302
352
  }
303
353
  getPropertyType(property, genericTypeMap = /* @__PURE__ */ new Map()) {
304
354
  if (property.type) return this.getTypeNodeToString(property.type, genericTypeMap);
305
355
  const type = this.checker.getTypeAtLocation(property);
306
- return this.getStringFromType(type);
356
+ return this.checker.typeToString(type);
307
357
  }
308
358
  getTypeNodeToString(typeNode, genericTypeMap = /* @__PURE__ */ new Map()) {
309
- if (typescript.default.isTypeReferenceNode(typeNode) && typescript.default.isIdentifier(typeNode.typeName)) {
310
- const typeName = typeNode.typeName.text;
359
+ if (typescript.default.isTypeReferenceNode(typeNode)) {
360
+ let typeName;
361
+ if (typescript.default.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
362
+ else typeName = typeNode.typeName.right.text;
311
363
  if (genericTypeMap.has(typeName)) return genericTypeMap.get(typeName);
312
364
  if (typeName.toLowerCase() === "uploadfile") return "UploadFile";
313
365
  if (typeName.toLowerCase() === "uploadfiledto") return "UploadFileDto";
@@ -319,7 +371,7 @@ var SchemaTransformer = class SchemaTransformer {
319
371
  }
320
372
  return this.resolveGenericType(typeNode);
321
373
  }
322
- return typeNode.typeName.text;
374
+ return typeName;
323
375
  }
324
376
  switch (typeNode.kind) {
325
377
  case typescript.default.SyntaxKind.StringKeyword: return constants.jsPrimitives.String.type;
@@ -334,31 +386,31 @@ var SchemaTransformer = class SchemaTransformer {
334
386
  if (meaningfulTypes.length > 0 && meaningfulTypes[0]) return meaningfulTypes[0];
335
387
  if (types.length > 0 && types[0]) return types[0];
336
388
  return "object";
337
- default:
338
- if (typescript.default.isIndexedAccessTypeNode(typeNode)) {
339
- const resolvedType = this.checker.getTypeAtLocation(typeNode);
340
- const resolved = this.checker.typeToString(resolvedType);
341
- if (this.isPrimitiveType(resolved)) return resolved;
342
- }
343
- const typeText = typeNode.getText();
344
- if (genericTypeMap && genericTypeMap.has(typeText)) return genericTypeMap.get(typeText);
345
- if (typeText.startsWith("Date")) return constants.jsPrimitives.Date.type;
346
- if (typeText.includes("Buffer") || typeText.includes("Uint8Array")) return constants.jsPrimitives.Buffer.type;
347
- return typeText;
389
+ default: {
390
+ const resolvedType = this.checker.getTypeAtLocation(typeNode);
391
+ const resolved = this.checker.typeToString(resolvedType);
392
+ if (genericTypeMap && genericTypeMap.has(resolved)) return genericTypeMap.get(resolved);
393
+ if (this.isPrimitiveType(resolved)) return resolved;
394
+ if (resolved === "Date") return constants.jsPrimitives.Date.type;
395
+ if (resolved === "Buffer") return constants.jsPrimitives.Buffer.type;
396
+ if (resolved === "Uint8Array") return constants.jsPrimitives.Uint8Array.type;
397
+ return resolved;
398
+ }
348
399
  }
349
400
  }
350
401
  resolveGenericType(typeNode) {
351
- const typeName = typeNode.typeName.text;
402
+ let typeName;
403
+ if (typescript.default.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
404
+ else typeName = typeNode.typeName.right.text;
352
405
  const typeArguments = typeNode.typeArguments;
353
406
  if (!typeArguments || typeArguments.length === 0) return typeName;
354
407
  const type = this.checker.getTypeAtLocation(typeNode);
355
- const resolvedType = this.getStringFromType(type);
356
- if (resolvedType && resolvedType !== typeName && !resolvedType.includes("any")) return resolvedType;
408
+ if (!(type.flags & typescript.default.TypeFlags.Any)) {
409
+ const resolvedType = this.checker.typeToString(type);
410
+ if (resolvedType && resolvedType !== typeName) return resolvedType;
411
+ }
357
412
  return typeName;
358
413
  }
359
- getStringFromType(type) {
360
- return this.checker.typeToString(type);
361
- }
362
414
  extractDecorators(member) {
363
415
  const decorators = [];
364
416
  if (member.modifiers) {
@@ -378,6 +430,7 @@ var SchemaTransformer = class SchemaTransformer {
378
430
  }
379
431
  getDecoratorName(callExpression) {
380
432
  if (typescript.default.isIdentifier(callExpression.expression)) return callExpression.expression.text;
433
+ if (typescript.default.isPropertyAccessExpression(callExpression.expression)) return callExpression.expression.name.text;
381
434
  return "unknown";
382
435
  }
383
436
  getDecoratorArguments(callExpression) {
@@ -390,7 +443,13 @@ var SchemaTransformer = class SchemaTransformer {
390
443
  });
391
444
  }
392
445
  getSafeDecoratorArgument(arg) {
393
- if (arg && typeof arg === "object" && "kind" in arg) return arg.getText();
446
+ if (arg && typeof arg === "object" && "kind" in arg) {
447
+ const node = arg;
448
+ const type = this.checker.getTypeAtLocation(node);
449
+ if (type.isNumberLiteral()) return type.value;
450
+ if (type.isStringLiteral()) return type.value;
451
+ return this.checker.typeToString(type);
452
+ }
394
453
  return arg;
395
454
  }
396
455
  isPropertyTypeGeneric(property) {
@@ -414,15 +473,18 @@ var SchemaTransformer = class SchemaTransformer {
414
473
  isGenericTypeFromSymbol(type) {
415
474
  if (this.isSimpleArrayType(type)) return false;
416
475
  if (type.aliasTypeArguments && type.aliasTypeArguments.length > 0) return true;
417
- if (type.typeArguments && type.typeArguments.length > 0 && type.typeArguments[0].symbol.getName() === "Array") {
418
- const symbol = type.getSymbol();
419
- if (symbol && symbol.getName() === "Array") {
420
- const elementType = type.typeArguments[0];
421
- if (elementType) return this.isUtilityTypeFromType(elementType);
422
- return false;
476
+ if (type.flags & typescript.default.TypeFlags.Object && type.objectFlags & typescript.default.ObjectFlags.Reference) {
477
+ var _typeArgs$;
478
+ const typeArgs = this.checker.getTypeArguments(type);
479
+ if (typeArgs.length > 0 && ((_typeArgs$ = typeArgs[0]) === null || _typeArgs$ === void 0 || (_typeArgs$ = _typeArgs$.getSymbol()) === null || _typeArgs$ === void 0 ? void 0 : _typeArgs$.getName()) === "Array") {
480
+ const symbol = type.getSymbol();
481
+ if (symbol && symbol.getName() === "Array") {
482
+ const elementType = typeArgs[0];
483
+ return elementType ? this.isUtilityTypeFromType(elementType) : false;
484
+ }
485
+ const elementType = typeArgs[0];
486
+ return elementType ? this.isUtilityTypeFromType(elementType) : false;
423
487
  }
424
- const elementType = type.typeArguments[0];
425
- return this.isUtilityTypeFromType(elementType);
426
488
  }
427
489
  if (type.flags & typescript.default.TypeFlags.TypeParameter) return true;
428
490
  if (type.flags & typescript.default.TypeFlags.Conditional) return true;
@@ -454,13 +516,18 @@ var SchemaTransformer = class SchemaTransformer {
454
516
  isSimpleArrayType(type) {
455
517
  const symbol = type.getSymbol();
456
518
  if (!symbol || symbol.getName() !== "Array") return false;
457
- if (type.typeArguments && type.typeArguments.length === 1) {
458
- const elementType = type.typeArguments[0];
459
- if (!elementType) return false;
460
- if (this.isUtilityTypeFromType(elementType)) return false;
461
- if (elementType.typeArguments && elementType.typeArguments.length > 0) return false;
462
- if (type.typeArguments && type.typeArguments[0].symbol && type.typeArguments[0].symbol.getName() !== "Array") return false;
463
- return true;
519
+ if (type.flags & typescript.default.TypeFlags.Object && type.objectFlags & typescript.default.ObjectFlags.Reference) {
520
+ const typeArgs = this.checker.getTypeArguments(type);
521
+ if (typeArgs.length === 1) {
522
+ const elementType = typeArgs[0];
523
+ if (this.isUtilityTypeFromType(elementType)) return false;
524
+ if (elementType.flags & typescript.default.TypeFlags.Object && elementType.objectFlags & typescript.default.ObjectFlags.Reference) {
525
+ if (this.checker.getTypeArguments(elementType).length > 0) return false;
526
+ }
527
+ const elementSymbol = elementType.getSymbol();
528
+ if (elementSymbol && elementSymbol.getName() !== "Array") return false;
529
+ return true;
530
+ }
464
531
  }
465
532
  return false;
466
533
  }
@@ -518,23 +585,40 @@ var SchemaTransformer = class SchemaTransformer {
518
585
  return matches[0];
519
586
  }
520
587
  findBestMatch(cls, matches) {
521
- let instance = {};
522
- try {
523
- instance = new cls();
524
- } catch {
525
- instance = {};
588
+ const runtimeProps = this.extractRuntimePropertyNames(cls);
589
+ let bestMatch;
590
+ let bestScore = -1;
591
+ for (const match of matches) {
592
+ let score = 0;
593
+ for (const member of match.node.members) if (typescript.default.isPropertyDeclaration(member) && member.name && (typescript.default.isIdentifier(member.name) || typescript.default.isStringLiteral(member.name))) {
594
+ if (runtimeProps.has(member.name.text)) score++;
595
+ }
596
+ if (score > bestScore) {
597
+ bestScore = score;
598
+ bestMatch = match;
599
+ }
526
600
  }
527
- const instanceProperties = Object.keys(instance);
528
- let matchesMap = {};
529
- matches.forEach((match, index) => {
530
- let fountProperties = 0;
531
- match.node.members.map((member) => {
532
- if (member.name && instanceProperties.includes(member.name.getText())) fountProperties++;
533
- });
534
- matchesMap[index] = fountProperties;
535
- });
536
- const maxMatches = Math.max(...Object.values(matchesMap));
537
- return matches[Object.values(matchesMap).findIndex((value) => value === maxMatches)];
601
+ return bestMatch;
602
+ }
603
+ /**
604
+ * Safely extracts property names from a class constructor without instantiation.
605
+ * Parses the class source via Function.prototype.toString() and inspects
606
+ * the prototype for method names.
607
+ */
608
+ extractRuntimePropertyNames(cls) {
609
+ const names = /* @__PURE__ */ new Set();
610
+ try {
611
+ const source = Function.prototype.toString.call(cls);
612
+ const thisAssign = /this\.(\w+)\s*=/g;
613
+ let m;
614
+ while ((m = thisAssign.exec(source)) !== null) if (m[1]) names.add(m[1]);
615
+ const classField = /[;}](\w+)(?=[=;}\s])/g;
616
+ while ((m = classField.exec(source)) !== null) if (m[1] && !constants.JS_KEYWORDS.has(m[1])) names.add(m[1]);
617
+ } catch {}
618
+ try {
619
+ for (const name of Object.getOwnPropertyNames(cls.prototype)) if (name !== "constructor") names.add(name);
620
+ } catch {}
621
+ return names;
538
622
  }
539
623
  getFilteredSourceFiles(sourceOptions) {
540
624
  if (sourceOptions === null || sourceOptions === void 0 ? void 0 : sourceOptions.isExternal) return this.program.getSourceFiles().filter((sf) => {
@@ -549,77 +633,54 @@ var SchemaTransformer = class SchemaTransformer {
549
633
  if (!propertyDeclaration.type) return false;
550
634
  let typeNode = propertyDeclaration.type;
551
635
  if (typescript.default.isArrayTypeNode(typeNode)) typeNode = typeNode.elementType;
552
- if (typescript.default.isTypeReferenceNode(typeNode)) {
553
- const type = this.checker.getTypeAtLocation(typeNode);
554
- return !!(type.flags & typescript.default.TypeFlags.Enum) || !!(type.flags & typescript.default.TypeFlags.EnumLiteral);
636
+ if (typescript.default.isUnionTypeNode(typeNode)) {
637
+ const nonNullTypes = typeNode.types.filter((t) => t.kind !== typescript.default.SyntaxKind.NullKeyword && t.kind !== typescript.default.SyntaxKind.UndefinedKeyword);
638
+ if (nonNullTypes.length === 1 && nonNullTypes[0]) typeNode = nonNullTypes[0];
555
639
  }
640
+ if (typescript.default.isTypeReferenceNode(typeNode)) return !!(this.checker.getTypeAtLocation(typeNode).flags & typescript.default.TypeFlags.EnumLike);
556
641
  return false;
557
642
  }
558
- isClassType(propertyDeclaration) {
559
- if (!propertyDeclaration.type) return false;
560
- if (this.isArrayProperty(propertyDeclaration)) {
561
- var _propertyDeclaration$;
562
- let elementType;
563
- if (typescript.default.isArrayTypeNode(propertyDeclaration.type)) elementType = propertyDeclaration.type.elementType;
564
- else if (typescript.default.isTypeReferenceNode(propertyDeclaration.type) && ((_propertyDeclaration$ = propertyDeclaration.type.typeArguments) === null || _propertyDeclaration$ === void 0 ? void 0 : _propertyDeclaration$[0])) elementType = propertyDeclaration.type.typeArguments[0];
565
- if (!elementType) return false;
566
- if (typescript.default.isTypeReferenceNode(elementType) && elementType.typeArguments && elementType.typeArguments.length > 0) {
567
- const firstTypeArg = elementType.typeArguments[0];
568
- if (firstTypeArg) {
569
- const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
570
- if (argSymbol && argSymbol.declarations) {
571
- if (argSymbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl))) return true;
572
- }
573
- }
574
- }
575
- const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
576
- if (symbol && symbol.declarations) return symbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl));
577
- return false;
578
- } else {
579
- if (typescript.default.isTypeReferenceNode(propertyDeclaration.type) && propertyDeclaration.type.typeArguments && propertyDeclaration.type.typeArguments.length > 0) {
580
- const firstTypeArg = propertyDeclaration.type.typeArguments[0];
581
- if (firstTypeArg) {
582
- const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
583
- if (argSymbol && argSymbol.declarations) {
584
- if (argSymbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl))) return true;
585
- }
586
- }
587
- }
588
- const symbol = this.checker.getTypeAtLocation(propertyDeclaration.type).getSymbol();
589
- if (symbol && symbol.declarations) return symbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl));
590
- return false;
591
- }
592
- }
593
- getDeclarationProperty(property) {
594
- if (!property.originalProperty.type) return;
595
- if (typescript.default.isArrayTypeNode(property.originalProperty.type)) {
596
- const elementType = property.originalProperty.type.elementType;
597
- if (typescript.default.isTypeReferenceNode(elementType) && elementType.typeArguments && elementType.typeArguments.length > 0) {
598
- const firstTypeArg = elementType.typeArguments[0];
599
- if (firstTypeArg) {
600
- const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
601
- if (argSymbol && argSymbol.declarations) {
602
- const classDecl = argSymbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl));
603
- if (classDecl) return classDecl;
604
- }
605
- }
606
- }
607
- const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
608
- if (symbol && symbol.declarations) return symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl)) || symbol.declarations[0];
609
- return;
610
- }
611
- if (typescript.default.isTypeReferenceNode(property.originalProperty.type) && property.originalProperty.type.typeArguments && property.originalProperty.type.typeArguments.length > 0) {
612
- const firstTypeArg = property.originalProperty.type.typeArguments[0];
643
+ /**
644
+ * Resolves a type node to its underlying symbol via the type-checker.
645
+ * For type references with type arguments (e.g., PayloadEntity<Person>),
646
+ * it checks the first type argument for a class declaration first.
647
+ * Returns the ts.Symbol or undefined.
648
+ */
649
+ resolveClassSymbolFromTypeNode(typeNode) {
650
+ if (typescript.default.isTypeReferenceNode(typeNode) && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
651
+ const firstTypeArg = typeNode.typeArguments[0];
613
652
  if (firstTypeArg) {
614
653
  const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
615
654
  if (argSymbol && argSymbol.declarations) {
616
- const classDecl = argSymbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl));
617
- if (classDecl) return classDecl;
655
+ if (argSymbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl))) return argSymbol;
618
656
  }
619
657
  }
620
658
  }
621
- const symbol = this.checker.getTypeAtLocation(property.originalProperty.type).getSymbol();
622
- if (symbol && symbol.declarations) return symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl)) || symbol.declarations[0];
659
+ return this.checker.getTypeAtLocation(typeNode).getSymbol() ?? void 0;
660
+ }
661
+ /**
662
+ * Resolves the element type node from an array property declaration.
663
+ * Handles both `T[]` and `Array<T>` syntax.
664
+ */
665
+ resolveArrayElementTypeNode(propertyDeclaration) {
666
+ var _propertyDeclaration$;
667
+ if (!propertyDeclaration.type) return void 0;
668
+ if (typescript.default.isArrayTypeNode(propertyDeclaration.type)) return propertyDeclaration.type.elementType;
669
+ if (typescript.default.isTypeReferenceNode(propertyDeclaration.type) && ((_propertyDeclaration$ = propertyDeclaration.type.typeArguments) === null || _propertyDeclaration$ === void 0 ? void 0 : _propertyDeclaration$[0])) return propertyDeclaration.type.typeArguments[0];
670
+ }
671
+ isClassType(propertyDeclaration) {
672
+ if (!propertyDeclaration.type) return false;
673
+ const typeNode = this.isArrayProperty(propertyDeclaration) ? this.resolveArrayElementTypeNode(propertyDeclaration) : propertyDeclaration.type;
674
+ if (!typeNode) return false;
675
+ const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
676
+ if (symbol && symbol.declarations) return symbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl));
677
+ return false;
678
+ }
679
+ getDeclarationProperty(property) {
680
+ if (!property.originalProperty.type) return;
681
+ const typeNode = typescript.default.isArrayTypeNode(property.originalProperty.type) ? property.originalProperty.type.elementType : property.originalProperty.type;
682
+ const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
683
+ if (symbol && symbol.declarations) return symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl)) ?? symbol.declarations[0];
623
684
  }
624
685
  isArrayProperty(propertyDeclaration) {
625
686
  if (!propertyDeclaration.type) return false;
@@ -666,16 +727,28 @@ var SchemaTransformer = class SchemaTransformer {
666
727
  transformedSchema,
667
728
  declaration: property.genericClassReference
668
729
  });
669
- else schema = {
670
- type: "object",
671
- properties: {},
672
- additionalProperties: true
673
- };
674
- else schema = {
675
- type: "object",
676
- properties: {},
677
- additionalProperties: true
678
- };
730
+ else {
731
+ const inner = {
732
+ type: "object",
733
+ properties: {},
734
+ additionalProperties: true
735
+ };
736
+ schema = property.isArray ? {
737
+ type: "array",
738
+ items: inner
739
+ } : inner;
740
+ }
741
+ else {
742
+ const inner = {
743
+ type: "object",
744
+ properties: {},
745
+ additionalProperties: true
746
+ };
747
+ schema = property.isArray ? {
748
+ type: "array",
749
+ items: inner
750
+ } : inner;
751
+ }
679
752
  this.applyDecorators(property, schema);
680
753
  return schema;
681
754
  }
@@ -771,7 +844,12 @@ var SchemaTransformer = class SchemaTransformer {
771
844
  if (enumSchema) return enumSchema;
772
845
  }
773
846
  const propertySchema = { type: "object" };
774
- const propertyType = property.type.toLowerCase().replace("[]", "").trim();
847
+ let baseTypeNode = property.originalProperty.type;
848
+ if (property.isArray && baseTypeNode) baseTypeNode = this.resolveArrayElementTypeNode(property.originalProperty) ?? baseTypeNode;
849
+ const resolvedType = this.checker.getTypeAtLocation(baseTypeNode ?? property.originalProperty);
850
+ let propertyType;
851
+ if (resolvedType.flags & typescript.default.TypeFlags.TypeParameter) propertyType = property.type.toLowerCase().replace(/\[\]$/, "").trim();
852
+ else propertyType = this.checker.typeToString(resolvedType).toLowerCase();
775
853
  let isFile = false;
776
854
  switch (propertyType) {
777
855
  case constants.jsPrimitives.String.value:
@@ -808,17 +886,26 @@ var SchemaTransformer = class SchemaTransformer {
808
886
  propertySchema.type = constants.jsPrimitives.Symbol.value;
809
887
  break;
810
888
  case constants.jsPrimitives.Object.value:
889
+ case "unknown":
890
+ case "any":
811
891
  propertySchema.type = constants.jsPrimitives.Object.value;
892
+ propertySchema.additionalProperties = true;
812
893
  break;
813
894
  default: propertySchema.type = constants.jsPrimitives.String.value;
814
895
  }
815
896
  if (property.isArray) {
816
897
  delete propertySchema.format;
817
- propertySchema.type = `array`;
818
- propertySchema.items = {
819
- type: isFile ? constants.jsPrimitives.UploadFile.value : propertyType,
898
+ const resolvedItemType = propertySchema.type;
899
+ const itemSchema = {
900
+ type: isFile ? constants.jsPrimitives.UploadFile.value : resolvedItemType,
820
901
  format: isFile ? constants.jsPrimitives.UploadFile.format : propertySchema.format
821
902
  };
903
+ if (propertySchema.additionalProperties) {
904
+ itemSchema.additionalProperties = true;
905
+ delete propertySchema.additionalProperties;
906
+ }
907
+ propertySchema.type = `array`;
908
+ propertySchema.items = itemSchema;
822
909
  }
823
910
  return propertySchema;
824
911
  }
@@ -1016,21 +1103,24 @@ var SchemaTransformer = class SchemaTransformer {
1016
1103
  const callee = node.expression;
1017
1104
  if (typescript.default.isIdentifier(callee) && callee.text === "transform" || typescript.default.isPropertyAccessExpression(callee) && callee.name.text === "transform") {
1018
1105
  const firstArg = node.arguments[0];
1019
- const typeArgs = firstArg.typeArguments;
1020
- if (typeArgs && typeArgs.length > 0) {
1021
- const baseExpr = firstArg.expression ?? firstArg;
1022
- if (typescript.default.isIdentifier(baseExpr)) {
1106
+ if (typescript.default.isExpressionWithTypeArguments(firstArg) && firstArg.typeArguments && firstArg.typeArguments.length > 0) {
1107
+ const baseExpr = firstArg.expression;
1108
+ const className = typescript.default.isIdentifier(baseExpr) ? baseExpr.text : typescript.default.isPropertyAccessExpression(baseExpr) ? baseExpr.name.text : void 0;
1109
+ if (className) {
1023
1110
  var _this$classFileIndex$;
1024
- const classNode = (_this$classFileIndex$ = this.classFileIndex.get(baseExpr.text)) === null || _this$classFileIndex$ === void 0 || (_this$classFileIndex$ = _this$classFileIndex$[0]) === null || _this$classFileIndex$ === void 0 ? void 0 : _this$classFileIndex$.node;
1111
+ const classNode = (_this$classFileIndex$ = this.classFileIndex.get(className)) === null || _this$classFileIndex$ === void 0 || (_this$classFileIndex$ = _this$classFileIndex$[0]) === null || _this$classFileIndex$ === void 0 ? void 0 : _this$classFileIndex$.node;
1025
1112
  if (classNode === null || classNode === void 0 ? void 0 : classNode.typeParameters) {
1026
1113
  const typeMap = /* @__PURE__ */ new Map();
1027
1114
  classNode.typeParameters.forEach((param, i) => {
1028
- const typeArg = typeArgs[i];
1115
+ const typeArg = firstArg.typeArguments[i];
1029
1116
  if (typeArg) typeMap.set(param.name.text, this.getTypeNodeToString(typeArg, /* @__PURE__ */ new Map()));
1030
1117
  });
1031
- if (typeMap.size > 0) this.transformCallIndex.set(baseExpr.text, typeMap);
1118
+ if (typeMap.size > 0) this.transformCallIndex.set(className, typeMap);
1032
1119
  }
1033
1120
  }
1121
+ } else {
1122
+ const className = typescript.default.isIdentifier(firstArg) ? firstArg.text : typescript.default.isPropertyAccessExpression(firstArg) ? firstArg.name.text : void 0;
1123
+ if (className) this.nonGenericTransformCalls.add(className);
1034
1124
  }
1035
1125
  }
1036
1126
  }
@@ -1053,7 +1143,14 @@ var SchemaTransformer = class SchemaTransformer {
1053
1143
  }
1054
1144
  };
1055
1145
  }
1056
- const genericTypeMap = this.transformCallIndex.get(cls.name) ?? /* @__PURE__ */ new Map();
1146
+ const genericTypeMap = /* @__PURE__ */ new Map();
1147
+ if (result.node.typeParameters) {
1148
+ const indexedTypeMap = this.transformCallIndex.get(cls.name);
1149
+ const hasNonGenericCall = this.nonGenericTransformCalls.has(cls.name);
1150
+ const allHaveDefaults = result.node.typeParameters.every((p) => !!p.default);
1151
+ if (indexedTypeMap && !(hasNonGenericCall && allHaveDefaults)) for (const [key, value] of indexedTypeMap) genericTypeMap.set(key, value);
1152
+ else for (const param of result.node.typeParameters) if (param.default) genericTypeMap.set(param.name.text, this.getTypeNodeToString(param.default, /* @__PURE__ */ new Map()));
1153
+ }
1057
1154
  const hasGenericArgs = genericTypeMap.size > 0;
1058
1155
  if (!hasGenericArgs && this.classCache.has(cls)) return this.classCache.get(cls);
1059
1156
  let schema = {