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.mjs CHANGED
@@ -3,6 +3,53 @@ import path from "path";
3
3
  //#region src/transformer.fixtures.ts
4
4
  const constants = {
5
5
  TS_CONFIG_DEFAULT_PATH: path.resolve(process.cwd(), "tsconfig.json"),
6
+ JS_KEYWORDS: new Set([
7
+ "constructor",
8
+ "static",
9
+ "get",
10
+ "set",
11
+ "async",
12
+ "return",
13
+ "if",
14
+ "else",
15
+ "for",
16
+ "while",
17
+ "switch",
18
+ "case",
19
+ "break",
20
+ "throw",
21
+ "new",
22
+ "delete",
23
+ "typeof",
24
+ "void",
25
+ "try",
26
+ "catch",
27
+ "finally",
28
+ "this",
29
+ "super",
30
+ "class",
31
+ "const",
32
+ "let",
33
+ "var",
34
+ "function",
35
+ "yield",
36
+ "await",
37
+ "import",
38
+ "export",
39
+ "default",
40
+ "extends",
41
+ "implements",
42
+ "in",
43
+ "of",
44
+ "do",
45
+ "with",
46
+ "continue",
47
+ "instanceof",
48
+ "true",
49
+ "false",
50
+ "null",
51
+ "undefined"
52
+ ]),
6
53
  jsPrimitives: {
7
54
  String: {
8
55
  type: "String",
@@ -165,6 +212,7 @@ var SchemaTransformer = class SchemaTransformer {
165
212
  autoCleanup;
166
213
  classFileIndex = /* @__PURE__ */ new Map();
167
214
  transformCallIndex = /* @__PURE__ */ new Map();
215
+ nonGenericTransformCalls = /* @__PURE__ */ new Set();
168
216
  constructor(tsConfigPath = constants.TS_CONFIG_DEFAULT_PATH, options = {}) {
169
217
  this.maxCacheSize = options.maxCacheSize ?? 100;
170
218
  this.autoCleanup = options.autoCleanup ?? true;
@@ -185,14 +233,19 @@ var SchemaTransformer = class SchemaTransformer {
185
233
  else {
186
234
  const heritageClause = classNode.heritageClauses[0];
187
235
  if (heritageClause && heritageClause.token === ts.SyntaxKind.ExtendsKeyword) {
188
- var _symbol$declarations;
189
236
  const type = heritageClause.types[0];
190
237
  let properties = [];
191
238
  let baseProperties = [];
192
239
  if (!type) return [];
193
240
  const symbol = this.checker.getSymbolAtLocation(type.expression);
194
241
  if (!symbol) return [];
195
- const declaration = (_symbol$declarations = symbol.declarations) === null || _symbol$declarations === void 0 ? void 0 : _symbol$declarations[0];
242
+ const realSymbol = symbol && symbol.flags & ts.SymbolFlags.Alias ? this.checker.getAliasedSymbol(symbol) : symbol;
243
+ let declaration;
244
+ if (realSymbol && realSymbol.links && realSymbol.links.type && realSymbol.links.type.symbol && realSymbol.links.type.symbol.declarations) declaration = realSymbol.links.type.symbol.declarations[0];
245
+ else {
246
+ var _realSymbol$declarati;
247
+ declaration = realSymbol === null || realSymbol === void 0 || (_realSymbol$declarati = realSymbol.declarations) === null || _realSymbol$declarati === void 0 ? void 0 : _realSymbol$declarati.find(ts.isClassDeclaration);
248
+ }
196
249
  if (declaration && ts.isClassDeclaration(declaration)) {
197
250
  const newGenericTypeMap = /* @__PURE__ */ new Map();
198
251
  if (declaration.typeParameters && type.typeArguments) declaration.typeParameters.forEach((param, index) => {
@@ -211,78 +264,77 @@ var SchemaTransformer = class SchemaTransformer {
211
264
  }
212
265
  getPropertiesByClassMembers(members, parentClassNode, genericTypeMap = /* @__PURE__ */ new Map()) {
213
266
  const properties = [];
214
- for (const member of members) {
215
- var _member$name;
216
- if (ts.isPropertyDeclaration(member) && member.name && ((_member$name = member.name) === null || _member$name === void 0 ? void 0 : _member$name.text)) {
217
- var _typeArguments;
218
- if (member.modifiers) {
219
- if (member.modifiers.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword)) continue;
220
- }
221
- const propertyName = member.name.text;
222
- const type = this.getPropertyType(member, genericTypeMap);
223
- const decorators = this.extractDecorators(member);
224
- const isOptional = !!member.questionToken;
225
- const isGeneric = this.isPropertyTypeGeneric(member);
226
- const isEnum = this.isEnum(member);
227
- const isPrimitive = this.isPrimitiveType(type) || isEnum;
228
- const isClassType = this.isClassType(member);
229
- const isArray = this.isArrayProperty(member);
230
- const isTypeLiteral = this.isTypeLiteral(member);
231
- let genericClassReference = void 0;
232
- if (isGeneric && !isPrimitive) {
233
- const baseTypeName = type.replace(/\[\]$/, "").trim();
234
- if (!this.isPrimitiveType(baseTypeName)) {
235
- const matches = this.classFileIndex.get(baseTypeName);
236
- if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
237
- }
267
+ for (const member of members) if (ts.isPropertyDeclaration(member) && (ts.isIdentifier(member.name) || ts.isStringLiteral(member.name))) {
268
+ var _property$originalPro;
269
+ if (member.modifiers) {
270
+ if (member.modifiers.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword)) continue;
271
+ }
272
+ const propertyName = member.name.text;
273
+ const type = this.getPropertyType(member, genericTypeMap);
274
+ const decorators = this.extractDecorators(member);
275
+ const isOptional = !!member.questionToken;
276
+ const isGeneric = this.isPropertyTypeGeneric(member);
277
+ const isEnum = this.isEnum(member);
278
+ const isPrimitive = this.isPrimitiveType(type) || isEnum;
279
+ const isClassType = this.isClassType(member);
280
+ const isArray = this.isArrayProperty(member);
281
+ const isTypeLiteral = this.isTypeLiteral(member);
282
+ let genericClassReference = void 0;
283
+ if (isGeneric && !isPrimitive) {
284
+ const baseTypeName = type.replace(/\[\]$/, "").trim();
285
+ if (!this.isPrimitiveType(baseTypeName)) {
286
+ const matches = this.classFileIndex.get(baseTypeName);
287
+ if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
238
288
  }
239
- const property = {
240
- name: propertyName,
241
- type,
242
- decorators,
243
- isOptional,
244
- isGeneric,
245
- originalProperty: member,
246
- isPrimitive,
247
- isClassType,
248
- isArray,
249
- isEnum,
250
- isRef: false,
251
- isTypeLiteral,
252
- genericClassReference
253
- };
254
- if (property.isClassType) {
255
- const declaration = this.getDeclarationProperty(property);
256
- if (parentClassNode) {
257
- if (declaration && declaration.name && this.checker.getSymbolAtLocation(declaration.name) === this.checker.getSymbolAtLocation(parentClassNode.name)) property.isRef = true;
258
- }
289
+ }
290
+ const property = {
291
+ name: propertyName,
292
+ type,
293
+ decorators,
294
+ isOptional,
295
+ isGeneric,
296
+ originalProperty: member,
297
+ isPrimitive,
298
+ isClassType,
299
+ isArray,
300
+ isEnum,
301
+ isRef: false,
302
+ isTypeLiteral,
303
+ genericClassReference
304
+ };
305
+ if (property.isClassType) {
306
+ const declaration = this.getDeclarationProperty(property);
307
+ if (parentClassNode) {
308
+ if (declaration && declaration.name && this.checker.getSymbolAtLocation(declaration.name) === this.checker.getSymbolAtLocation(parentClassNode.name)) property.isRef = true;
259
309
  }
260
- if (property.isTypeLiteral && property.originalProperty.type && ((_typeArguments = property.originalProperty.type.typeArguments) === null || _typeArguments === void 0 ? void 0 : _typeArguments.length) === 1) {
261
- const typeArguments = property.originalProperty.type.typeArguments;
262
- if (typeArguments && typeArguments[0]) {
263
- const firstTypeArg = typeArguments[0];
264
- if (ts.isTypeReferenceNode(firstTypeArg)) {
265
- const symbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
266
- if (symbol && symbol.declarations) {
267
- const classDeclaration = symbol.declarations.find((decl) => ts.isClassDeclaration(decl));
268
- if (classDeclaration && ts.isClassDeclaration(classDeclaration)) property.typeLiteralClassReference = classDeclaration;
269
- }
310
+ }
311
+ if (property.isTypeLiteral && property.originalProperty.type !== void 0 && ts.isTypeReferenceNode(property.originalProperty.type) && ((_property$originalPro = property.originalProperty.type.typeArguments) === null || _property$originalPro === void 0 ? void 0 : _property$originalPro.length) === 1) {
312
+ const typeArguments = property.originalProperty.type.typeArguments;
313
+ if (typeArguments && typeArguments[0]) {
314
+ const firstTypeArg = typeArguments[0];
315
+ if (ts.isTypeReferenceNode(firstTypeArg)) {
316
+ const symbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
317
+ if (symbol && symbol.declarations) {
318
+ const classDeclaration = symbol.declarations.find((decl) => ts.isClassDeclaration(decl));
319
+ if (classDeclaration && ts.isClassDeclaration(classDeclaration)) property.typeLiteralClassReference = classDeclaration;
270
320
  }
271
321
  }
272
322
  }
273
- properties.push(property);
274
323
  }
324
+ properties.push(property);
275
325
  }
276
326
  return properties;
277
327
  }
278
328
  getPropertyType(property, genericTypeMap = /* @__PURE__ */ new Map()) {
279
329
  if (property.type) return this.getTypeNodeToString(property.type, genericTypeMap);
280
330
  const type = this.checker.getTypeAtLocation(property);
281
- return this.getStringFromType(type);
331
+ return this.checker.typeToString(type);
282
332
  }
283
333
  getTypeNodeToString(typeNode, genericTypeMap = /* @__PURE__ */ new Map()) {
284
- if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
285
- const typeName = typeNode.typeName.text;
334
+ if (ts.isTypeReferenceNode(typeNode)) {
335
+ let typeName;
336
+ if (ts.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
337
+ else typeName = typeNode.typeName.right.text;
286
338
  if (genericTypeMap.has(typeName)) return genericTypeMap.get(typeName);
287
339
  if (typeName.toLowerCase() === "uploadfile") return "UploadFile";
288
340
  if (typeName.toLowerCase() === "uploadfiledto") return "UploadFileDto";
@@ -294,7 +346,7 @@ var SchemaTransformer = class SchemaTransformer {
294
346
  }
295
347
  return this.resolveGenericType(typeNode);
296
348
  }
297
- return typeNode.typeName.text;
349
+ return typeName;
298
350
  }
299
351
  switch (typeNode.kind) {
300
352
  case ts.SyntaxKind.StringKeyword: return constants.jsPrimitives.String.type;
@@ -309,31 +361,31 @@ var SchemaTransformer = class SchemaTransformer {
309
361
  if (meaningfulTypes.length > 0 && meaningfulTypes[0]) return meaningfulTypes[0];
310
362
  if (types.length > 0 && types[0]) return types[0];
311
363
  return "object";
312
- default:
313
- if (ts.isIndexedAccessTypeNode(typeNode)) {
314
- const resolvedType = this.checker.getTypeAtLocation(typeNode);
315
- const resolved = this.checker.typeToString(resolvedType);
316
- if (this.isPrimitiveType(resolved)) return resolved;
317
- }
318
- const typeText = typeNode.getText();
319
- if (genericTypeMap && genericTypeMap.has(typeText)) return genericTypeMap.get(typeText);
320
- if (typeText.startsWith("Date")) return constants.jsPrimitives.Date.type;
321
- if (typeText.includes("Buffer") || typeText.includes("Uint8Array")) return constants.jsPrimitives.Buffer.type;
322
- return typeText;
364
+ default: {
365
+ const resolvedType = this.checker.getTypeAtLocation(typeNode);
366
+ const resolved = this.checker.typeToString(resolvedType);
367
+ if (genericTypeMap && genericTypeMap.has(resolved)) return genericTypeMap.get(resolved);
368
+ if (this.isPrimitiveType(resolved)) return resolved;
369
+ if (resolved === "Date") return constants.jsPrimitives.Date.type;
370
+ if (resolved === "Buffer") return constants.jsPrimitives.Buffer.type;
371
+ if (resolved === "Uint8Array") return constants.jsPrimitives.Uint8Array.type;
372
+ return resolved;
373
+ }
323
374
  }
324
375
  }
325
376
  resolveGenericType(typeNode) {
326
- const typeName = typeNode.typeName.text;
377
+ let typeName;
378
+ if (ts.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
379
+ else typeName = typeNode.typeName.right.text;
327
380
  const typeArguments = typeNode.typeArguments;
328
381
  if (!typeArguments || typeArguments.length === 0) return typeName;
329
382
  const type = this.checker.getTypeAtLocation(typeNode);
330
- const resolvedType = this.getStringFromType(type);
331
- if (resolvedType && resolvedType !== typeName && !resolvedType.includes("any")) return resolvedType;
383
+ if (!(type.flags & ts.TypeFlags.Any)) {
384
+ const resolvedType = this.checker.typeToString(type);
385
+ if (resolvedType && resolvedType !== typeName) return resolvedType;
386
+ }
332
387
  return typeName;
333
388
  }
334
- getStringFromType(type) {
335
- return this.checker.typeToString(type);
336
- }
337
389
  extractDecorators(member) {
338
390
  const decorators = [];
339
391
  if (member.modifiers) {
@@ -353,6 +405,7 @@ var SchemaTransformer = class SchemaTransformer {
353
405
  }
354
406
  getDecoratorName(callExpression) {
355
407
  if (ts.isIdentifier(callExpression.expression)) return callExpression.expression.text;
408
+ if (ts.isPropertyAccessExpression(callExpression.expression)) return callExpression.expression.name.text;
356
409
  return "unknown";
357
410
  }
358
411
  getDecoratorArguments(callExpression) {
@@ -365,7 +418,13 @@ var SchemaTransformer = class SchemaTransformer {
365
418
  });
366
419
  }
367
420
  getSafeDecoratorArgument(arg) {
368
- if (arg && typeof arg === "object" && "kind" in arg) return arg.getText();
421
+ if (arg && typeof arg === "object" && "kind" in arg) {
422
+ const node = arg;
423
+ const type = this.checker.getTypeAtLocation(node);
424
+ if (type.isNumberLiteral()) return type.value;
425
+ if (type.isStringLiteral()) return type.value;
426
+ return this.checker.typeToString(type);
427
+ }
369
428
  return arg;
370
429
  }
371
430
  isPropertyTypeGeneric(property) {
@@ -389,15 +448,18 @@ var SchemaTransformer = class SchemaTransformer {
389
448
  isGenericTypeFromSymbol(type) {
390
449
  if (this.isSimpleArrayType(type)) return false;
391
450
  if (type.aliasTypeArguments && type.aliasTypeArguments.length > 0) return true;
392
- if (type.typeArguments && type.typeArguments.length > 0 && type.typeArguments[0].symbol.getName() === "Array") {
393
- const symbol = type.getSymbol();
394
- if (symbol && symbol.getName() === "Array") {
395
- const elementType = type.typeArguments[0];
396
- if (elementType) return this.isUtilityTypeFromType(elementType);
397
- return false;
451
+ if (type.flags & ts.TypeFlags.Object && type.objectFlags & ts.ObjectFlags.Reference) {
452
+ var _typeArgs$;
453
+ const typeArgs = this.checker.getTypeArguments(type);
454
+ if (typeArgs.length > 0 && ((_typeArgs$ = typeArgs[0]) === null || _typeArgs$ === void 0 || (_typeArgs$ = _typeArgs$.getSymbol()) === null || _typeArgs$ === void 0 ? void 0 : _typeArgs$.getName()) === "Array") {
455
+ const symbol = type.getSymbol();
456
+ if (symbol && symbol.getName() === "Array") {
457
+ const elementType = typeArgs[0];
458
+ return elementType ? this.isUtilityTypeFromType(elementType) : false;
459
+ }
460
+ const elementType = typeArgs[0];
461
+ return elementType ? this.isUtilityTypeFromType(elementType) : false;
398
462
  }
399
- const elementType = type.typeArguments[0];
400
- return this.isUtilityTypeFromType(elementType);
401
463
  }
402
464
  if (type.flags & ts.TypeFlags.TypeParameter) return true;
403
465
  if (type.flags & ts.TypeFlags.Conditional) return true;
@@ -429,13 +491,18 @@ var SchemaTransformer = class SchemaTransformer {
429
491
  isSimpleArrayType(type) {
430
492
  const symbol = type.getSymbol();
431
493
  if (!symbol || symbol.getName() !== "Array") return false;
432
- if (type.typeArguments && type.typeArguments.length === 1) {
433
- const elementType = type.typeArguments[0];
434
- if (!elementType) return false;
435
- if (this.isUtilityTypeFromType(elementType)) return false;
436
- if (elementType.typeArguments && elementType.typeArguments.length > 0) return false;
437
- if (type.typeArguments && type.typeArguments[0].symbol && type.typeArguments[0].symbol.getName() !== "Array") return false;
438
- return true;
494
+ if (type.flags & ts.TypeFlags.Object && type.objectFlags & ts.ObjectFlags.Reference) {
495
+ const typeArgs = this.checker.getTypeArguments(type);
496
+ if (typeArgs.length === 1) {
497
+ const elementType = typeArgs[0];
498
+ if (this.isUtilityTypeFromType(elementType)) return false;
499
+ if (elementType.flags & ts.TypeFlags.Object && elementType.objectFlags & ts.ObjectFlags.Reference) {
500
+ if (this.checker.getTypeArguments(elementType).length > 0) return false;
501
+ }
502
+ const elementSymbol = elementType.getSymbol();
503
+ if (elementSymbol && elementSymbol.getName() !== "Array") return false;
504
+ return true;
505
+ }
439
506
  }
440
507
  return false;
441
508
  }
@@ -493,23 +560,40 @@ var SchemaTransformer = class SchemaTransformer {
493
560
  return matches[0];
494
561
  }
495
562
  findBestMatch(cls, matches) {
496
- let instance = {};
497
- try {
498
- instance = new cls();
499
- } catch {
500
- instance = {};
563
+ const runtimeProps = this.extractRuntimePropertyNames(cls);
564
+ let bestMatch;
565
+ let bestScore = -1;
566
+ for (const match of matches) {
567
+ let score = 0;
568
+ for (const member of match.node.members) if (ts.isPropertyDeclaration(member) && member.name && (ts.isIdentifier(member.name) || ts.isStringLiteral(member.name))) {
569
+ if (runtimeProps.has(member.name.text)) score++;
570
+ }
571
+ if (score > bestScore) {
572
+ bestScore = score;
573
+ bestMatch = match;
574
+ }
501
575
  }
502
- const instanceProperties = Object.keys(instance);
503
- let matchesMap = {};
504
- matches.forEach((match, index) => {
505
- let fountProperties = 0;
506
- match.node.members.map((member) => {
507
- if (member.name && instanceProperties.includes(member.name.getText())) fountProperties++;
508
- });
509
- matchesMap[index] = fountProperties;
510
- });
511
- const maxMatches = Math.max(...Object.values(matchesMap));
512
- return matches[Object.values(matchesMap).findIndex((value) => value === maxMatches)];
576
+ return bestMatch;
577
+ }
578
+ /**
579
+ * Safely extracts property names from a class constructor without instantiation.
580
+ * Parses the class source via Function.prototype.toString() and inspects
581
+ * the prototype for method names.
582
+ */
583
+ extractRuntimePropertyNames(cls) {
584
+ const names = /* @__PURE__ */ new Set();
585
+ try {
586
+ const source = Function.prototype.toString.call(cls);
587
+ const thisAssign = /this\.(\w+)\s*=/g;
588
+ let m;
589
+ while ((m = thisAssign.exec(source)) !== null) if (m[1]) names.add(m[1]);
590
+ const classField = /[;}](\w+)(?=[=;}\s])/g;
591
+ while ((m = classField.exec(source)) !== null) if (m[1] && !constants.JS_KEYWORDS.has(m[1])) names.add(m[1]);
592
+ } catch {}
593
+ try {
594
+ for (const name of Object.getOwnPropertyNames(cls.prototype)) if (name !== "constructor") names.add(name);
595
+ } catch {}
596
+ return names;
513
597
  }
514
598
  getFilteredSourceFiles(sourceOptions) {
515
599
  if (sourceOptions === null || sourceOptions === void 0 ? void 0 : sourceOptions.isExternal) return this.program.getSourceFiles().filter((sf) => {
@@ -524,77 +608,54 @@ var SchemaTransformer = class SchemaTransformer {
524
608
  if (!propertyDeclaration.type) return false;
525
609
  let typeNode = propertyDeclaration.type;
526
610
  if (ts.isArrayTypeNode(typeNode)) typeNode = typeNode.elementType;
527
- if (ts.isTypeReferenceNode(typeNode)) {
528
- const type = this.checker.getTypeAtLocation(typeNode);
529
- return !!(type.flags & ts.TypeFlags.Enum) || !!(type.flags & ts.TypeFlags.EnumLiteral);
611
+ if (ts.isUnionTypeNode(typeNode)) {
612
+ const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword && t.kind !== ts.SyntaxKind.UndefinedKeyword);
613
+ if (nonNullTypes.length === 1 && nonNullTypes[0]) typeNode = nonNullTypes[0];
530
614
  }
615
+ if (ts.isTypeReferenceNode(typeNode)) return !!(this.checker.getTypeAtLocation(typeNode).flags & ts.TypeFlags.EnumLike);
531
616
  return false;
532
617
  }
533
- isClassType(propertyDeclaration) {
534
- if (!propertyDeclaration.type) return false;
535
- if (this.isArrayProperty(propertyDeclaration)) {
536
- var _propertyDeclaration$;
537
- let elementType;
538
- if (ts.isArrayTypeNode(propertyDeclaration.type)) elementType = propertyDeclaration.type.elementType;
539
- else if (ts.isTypeReferenceNode(propertyDeclaration.type) && ((_propertyDeclaration$ = propertyDeclaration.type.typeArguments) === null || _propertyDeclaration$ === void 0 ? void 0 : _propertyDeclaration$[0])) elementType = propertyDeclaration.type.typeArguments[0];
540
- if (!elementType) return false;
541
- if (ts.isTypeReferenceNode(elementType) && elementType.typeArguments && elementType.typeArguments.length > 0) {
542
- const firstTypeArg = elementType.typeArguments[0];
543
- if (firstTypeArg) {
544
- const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
545
- if (argSymbol && argSymbol.declarations) {
546
- if (argSymbol.declarations.some((decl) => ts.isClassDeclaration(decl))) return true;
547
- }
548
- }
549
- }
550
- const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
551
- if (symbol && symbol.declarations) return symbol.declarations.some((decl) => ts.isClassDeclaration(decl));
552
- return false;
553
- } else {
554
- if (ts.isTypeReferenceNode(propertyDeclaration.type) && propertyDeclaration.type.typeArguments && propertyDeclaration.type.typeArguments.length > 0) {
555
- const firstTypeArg = propertyDeclaration.type.typeArguments[0];
556
- if (firstTypeArg) {
557
- const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
558
- if (argSymbol && argSymbol.declarations) {
559
- if (argSymbol.declarations.some((decl) => ts.isClassDeclaration(decl))) return true;
560
- }
561
- }
562
- }
563
- const symbol = this.checker.getTypeAtLocation(propertyDeclaration.type).getSymbol();
564
- if (symbol && symbol.declarations) return symbol.declarations.some((decl) => ts.isClassDeclaration(decl));
565
- return false;
566
- }
567
- }
568
- getDeclarationProperty(property) {
569
- if (!property.originalProperty.type) return;
570
- if (ts.isArrayTypeNode(property.originalProperty.type)) {
571
- const elementType = property.originalProperty.type.elementType;
572
- if (ts.isTypeReferenceNode(elementType) && elementType.typeArguments && elementType.typeArguments.length > 0) {
573
- const firstTypeArg = elementType.typeArguments[0];
574
- if (firstTypeArg) {
575
- const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
576
- if (argSymbol && argSymbol.declarations) {
577
- const classDecl = argSymbol.declarations.find((decl) => ts.isClassDeclaration(decl));
578
- if (classDecl) return classDecl;
579
- }
580
- }
581
- }
582
- const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
583
- if (symbol && symbol.declarations) return symbol.declarations.find((decl) => ts.isClassDeclaration(decl)) || symbol.declarations[0];
584
- return;
585
- }
586
- if (ts.isTypeReferenceNode(property.originalProperty.type) && property.originalProperty.type.typeArguments && property.originalProperty.type.typeArguments.length > 0) {
587
- const firstTypeArg = property.originalProperty.type.typeArguments[0];
618
+ /**
619
+ * Resolves a type node to its underlying symbol via the type-checker.
620
+ * For type references with type arguments (e.g., PayloadEntity<Person>),
621
+ * it checks the first type argument for a class declaration first.
622
+ * Returns the ts.Symbol or undefined.
623
+ */
624
+ resolveClassSymbolFromTypeNode(typeNode) {
625
+ if (ts.isTypeReferenceNode(typeNode) && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
626
+ const firstTypeArg = typeNode.typeArguments[0];
588
627
  if (firstTypeArg) {
589
628
  const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
590
629
  if (argSymbol && argSymbol.declarations) {
591
- const classDecl = argSymbol.declarations.find((decl) => ts.isClassDeclaration(decl));
592
- if (classDecl) return classDecl;
630
+ if (argSymbol.declarations.some((decl) => ts.isClassDeclaration(decl))) return argSymbol;
593
631
  }
594
632
  }
595
633
  }
596
- const symbol = this.checker.getTypeAtLocation(property.originalProperty.type).getSymbol();
597
- if (symbol && symbol.declarations) return symbol.declarations.find((decl) => ts.isClassDeclaration(decl)) || symbol.declarations[0];
634
+ return this.checker.getTypeAtLocation(typeNode).getSymbol() ?? void 0;
635
+ }
636
+ /**
637
+ * Resolves the element type node from an array property declaration.
638
+ * Handles both `T[]` and `Array<T>` syntax.
639
+ */
640
+ resolveArrayElementTypeNode(propertyDeclaration) {
641
+ var _propertyDeclaration$;
642
+ if (!propertyDeclaration.type) return void 0;
643
+ if (ts.isArrayTypeNode(propertyDeclaration.type)) return propertyDeclaration.type.elementType;
644
+ if (ts.isTypeReferenceNode(propertyDeclaration.type) && ((_propertyDeclaration$ = propertyDeclaration.type.typeArguments) === null || _propertyDeclaration$ === void 0 ? void 0 : _propertyDeclaration$[0])) return propertyDeclaration.type.typeArguments[0];
645
+ }
646
+ isClassType(propertyDeclaration) {
647
+ if (!propertyDeclaration.type) return false;
648
+ const typeNode = this.isArrayProperty(propertyDeclaration) ? this.resolveArrayElementTypeNode(propertyDeclaration) : propertyDeclaration.type;
649
+ if (!typeNode) return false;
650
+ const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
651
+ if (symbol && symbol.declarations) return symbol.declarations.some((decl) => ts.isClassDeclaration(decl));
652
+ return false;
653
+ }
654
+ getDeclarationProperty(property) {
655
+ if (!property.originalProperty.type) return;
656
+ const typeNode = ts.isArrayTypeNode(property.originalProperty.type) ? property.originalProperty.type.elementType : property.originalProperty.type;
657
+ const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
658
+ if (symbol && symbol.declarations) return symbol.declarations.find((decl) => ts.isClassDeclaration(decl)) ?? symbol.declarations[0];
598
659
  }
599
660
  isArrayProperty(propertyDeclaration) {
600
661
  if (!propertyDeclaration.type) return false;
@@ -641,16 +702,28 @@ var SchemaTransformer = class SchemaTransformer {
641
702
  transformedSchema,
642
703
  declaration: property.genericClassReference
643
704
  });
644
- else schema = {
645
- type: "object",
646
- properties: {},
647
- additionalProperties: true
648
- };
649
- else schema = {
650
- type: "object",
651
- properties: {},
652
- additionalProperties: true
653
- };
705
+ else {
706
+ const inner = {
707
+ type: "object",
708
+ properties: {},
709
+ additionalProperties: true
710
+ };
711
+ schema = property.isArray ? {
712
+ type: "array",
713
+ items: inner
714
+ } : inner;
715
+ }
716
+ else {
717
+ const inner = {
718
+ type: "object",
719
+ properties: {},
720
+ additionalProperties: true
721
+ };
722
+ schema = property.isArray ? {
723
+ type: "array",
724
+ items: inner
725
+ } : inner;
726
+ }
654
727
  this.applyDecorators(property, schema);
655
728
  return schema;
656
729
  }
@@ -746,7 +819,12 @@ var SchemaTransformer = class SchemaTransformer {
746
819
  if (enumSchema) return enumSchema;
747
820
  }
748
821
  const propertySchema = { type: "object" };
749
- const propertyType = property.type.toLowerCase().replace("[]", "").trim();
822
+ let baseTypeNode = property.originalProperty.type;
823
+ if (property.isArray && baseTypeNode) baseTypeNode = this.resolveArrayElementTypeNode(property.originalProperty) ?? baseTypeNode;
824
+ const resolvedType = this.checker.getTypeAtLocation(baseTypeNode ?? property.originalProperty);
825
+ let propertyType;
826
+ if (resolvedType.flags & ts.TypeFlags.TypeParameter) propertyType = property.type.toLowerCase().replace(/\[\]$/, "").trim();
827
+ else propertyType = this.checker.typeToString(resolvedType).toLowerCase();
750
828
  let isFile = false;
751
829
  switch (propertyType) {
752
830
  case constants.jsPrimitives.String.value:
@@ -783,17 +861,26 @@ var SchemaTransformer = class SchemaTransformer {
783
861
  propertySchema.type = constants.jsPrimitives.Symbol.value;
784
862
  break;
785
863
  case constants.jsPrimitives.Object.value:
864
+ case "unknown":
865
+ case "any":
786
866
  propertySchema.type = constants.jsPrimitives.Object.value;
867
+ propertySchema.additionalProperties = true;
787
868
  break;
788
869
  default: propertySchema.type = constants.jsPrimitives.String.value;
789
870
  }
790
871
  if (property.isArray) {
791
872
  delete propertySchema.format;
792
- propertySchema.type = `array`;
793
- propertySchema.items = {
794
- type: isFile ? constants.jsPrimitives.UploadFile.value : propertyType,
873
+ const resolvedItemType = propertySchema.type;
874
+ const itemSchema = {
875
+ type: isFile ? constants.jsPrimitives.UploadFile.value : resolvedItemType,
795
876
  format: isFile ? constants.jsPrimitives.UploadFile.format : propertySchema.format
796
877
  };
878
+ if (propertySchema.additionalProperties) {
879
+ itemSchema.additionalProperties = true;
880
+ delete propertySchema.additionalProperties;
881
+ }
882
+ propertySchema.type = `array`;
883
+ propertySchema.items = itemSchema;
797
884
  }
798
885
  return propertySchema;
799
886
  }
@@ -991,21 +1078,24 @@ var SchemaTransformer = class SchemaTransformer {
991
1078
  const callee = node.expression;
992
1079
  if (ts.isIdentifier(callee) && callee.text === "transform" || ts.isPropertyAccessExpression(callee) && callee.name.text === "transform") {
993
1080
  const firstArg = node.arguments[0];
994
- const typeArgs = firstArg.typeArguments;
995
- if (typeArgs && typeArgs.length > 0) {
996
- const baseExpr = firstArg.expression ?? firstArg;
997
- if (ts.isIdentifier(baseExpr)) {
1081
+ if (ts.isExpressionWithTypeArguments(firstArg) && firstArg.typeArguments && firstArg.typeArguments.length > 0) {
1082
+ const baseExpr = firstArg.expression;
1083
+ const className = ts.isIdentifier(baseExpr) ? baseExpr.text : ts.isPropertyAccessExpression(baseExpr) ? baseExpr.name.text : void 0;
1084
+ if (className) {
998
1085
  var _this$classFileIndex$;
999
- 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;
1086
+ 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;
1000
1087
  if (classNode === null || classNode === void 0 ? void 0 : classNode.typeParameters) {
1001
1088
  const typeMap = /* @__PURE__ */ new Map();
1002
1089
  classNode.typeParameters.forEach((param, i) => {
1003
- const typeArg = typeArgs[i];
1090
+ const typeArg = firstArg.typeArguments[i];
1004
1091
  if (typeArg) typeMap.set(param.name.text, this.getTypeNodeToString(typeArg, /* @__PURE__ */ new Map()));
1005
1092
  });
1006
- if (typeMap.size > 0) this.transformCallIndex.set(baseExpr.text, typeMap);
1093
+ if (typeMap.size > 0) this.transformCallIndex.set(className, typeMap);
1007
1094
  }
1008
1095
  }
1096
+ } else {
1097
+ const className = ts.isIdentifier(firstArg) ? firstArg.text : ts.isPropertyAccessExpression(firstArg) ? firstArg.name.text : void 0;
1098
+ if (className) this.nonGenericTransformCalls.add(className);
1009
1099
  }
1010
1100
  }
1011
1101
  }
@@ -1028,7 +1118,14 @@ var SchemaTransformer = class SchemaTransformer {
1028
1118
  }
1029
1119
  };
1030
1120
  }
1031
- const genericTypeMap = this.transformCallIndex.get(cls.name) ?? /* @__PURE__ */ new Map();
1121
+ const genericTypeMap = /* @__PURE__ */ new Map();
1122
+ if (result.node.typeParameters) {
1123
+ const indexedTypeMap = this.transformCallIndex.get(cls.name);
1124
+ const hasNonGenericCall = this.nonGenericTransformCalls.has(cls.name);
1125
+ const allHaveDefaults = result.node.typeParameters.every((p) => !!p.default);
1126
+ if (indexedTypeMap && !(hasNonGenericCall && allHaveDefaults)) for (const [key, value] of indexedTypeMap) genericTypeMap.set(key, value);
1127
+ else for (const param of result.node.typeParameters) if (param.default) genericTypeMap.set(param.name.text, this.getTypeNodeToString(param.default, /* @__PURE__ */ new Map()));
1128
+ }
1032
1129
  const hasGenericArgs = genericTypeMap.size > 0;
1033
1130
  if (!hasGenericArgs && this.classCache.has(cls)) return this.classCache.get(cls);
1034
1131
  let schema = {