adorn-api 1.0.11 → 1.0.13

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 (81) hide show
  1. package/README.md +318 -620
  2. package/dist/adapter/express/auth.d.ts +5 -0
  3. package/dist/adapter/express/auth.d.ts.map +1 -0
  4. package/dist/adapter/express/bootstrap.d.ts.map +1 -1
  5. package/dist/adapter/express/coercion.d.ts +22 -0
  6. package/dist/adapter/express/coercion.d.ts.map +1 -0
  7. package/dist/adapter/express/index.d.ts +3 -50
  8. package/dist/adapter/express/index.d.ts.map +1 -1
  9. package/dist/adapter/express/merge.d.ts +0 -3
  10. package/dist/adapter/express/merge.d.ts.map +1 -1
  11. package/dist/adapter/express/openapi.d.ts +11 -0
  12. package/dist/adapter/express/openapi.d.ts.map +1 -0
  13. package/dist/adapter/express/router.d.ts +4 -0
  14. package/dist/adapter/express/router.d.ts.map +1 -0
  15. package/dist/adapter/express/swagger.d.ts +4 -0
  16. package/dist/adapter/express/swagger.d.ts.map +1 -0
  17. package/dist/adapter/express/types.d.ts +64 -0
  18. package/dist/adapter/express/types.d.ts.map +1 -0
  19. package/dist/adapter/express/validation.d.ts +10 -0
  20. package/dist/adapter/express/validation.d.ts.map +1 -0
  21. package/dist/cli.cjs +1003 -434
  22. package/dist/cli.cjs.map +1 -1
  23. package/dist/cli.js +1003 -434
  24. package/dist/cli.js.map +1 -1
  25. package/dist/compiler/analyze/scanControllers.d.ts +0 -1
  26. package/dist/compiler/analyze/scanControllers.d.ts.map +1 -1
  27. package/dist/compiler/cache/isStale.d.ts.map +1 -1
  28. package/dist/compiler/cache/writeCache.d.ts.map +1 -1
  29. package/dist/compiler/manifest/emit.d.ts.map +1 -1
  30. package/dist/compiler/manifest/format.d.ts +1 -1
  31. package/dist/compiler/manifest/format.d.ts.map +1 -1
  32. package/dist/compiler/schema/intersectionHandler.d.ts +7 -0
  33. package/dist/compiler/schema/intersectionHandler.d.ts.map +1 -0
  34. package/dist/compiler/schema/objectHandler.d.ts +20 -0
  35. package/dist/compiler/schema/objectHandler.d.ts.map +1 -0
  36. package/dist/compiler/schema/openapi.d.ts +1 -1
  37. package/dist/compiler/schema/openapi.d.ts.map +1 -1
  38. package/dist/compiler/schema/parameters.d.ts +18 -0
  39. package/dist/compiler/schema/parameters.d.ts.map +1 -0
  40. package/dist/compiler/schema/primitives.d.ts +10 -0
  41. package/dist/compiler/schema/primitives.d.ts.map +1 -0
  42. package/dist/compiler/schema/typeToJsonSchema.d.ts +3 -46
  43. package/dist/compiler/schema/typeToJsonSchema.d.ts.map +1 -1
  44. package/dist/compiler/schema/types.d.ts +54 -0
  45. package/dist/compiler/schema/types.d.ts.map +1 -0
  46. package/dist/compiler/schema/unionHandler.d.ts +10 -0
  47. package/dist/compiler/schema/unionHandler.d.ts.map +1 -0
  48. package/dist/decorators/index.d.ts +0 -1
  49. package/dist/decorators/index.d.ts.map +1 -1
  50. package/dist/express.cjs +522 -502
  51. package/dist/express.cjs.map +1 -1
  52. package/dist/express.js +522 -502
  53. package/dist/express.js.map +1 -1
  54. package/dist/http.d.ts +1 -10
  55. package/dist/http.d.ts.map +1 -1
  56. package/dist/index.cjs +3 -36
  57. package/dist/index.cjs.map +1 -1
  58. package/dist/index.d.ts +3 -4
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +2 -34
  61. package/dist/index.js.map +1 -1
  62. package/dist/metal/applyListQuery.d.ts +27 -0
  63. package/dist/metal/applyListQuery.d.ts.map +1 -0
  64. package/dist/metal/index.cjs +61 -2
  65. package/dist/metal/index.cjs.map +1 -1
  66. package/dist/metal/index.d.ts +4 -0
  67. package/dist/metal/index.d.ts.map +1 -1
  68. package/dist/metal/index.js +57 -2
  69. package/dist/metal/index.js.map +1 -1
  70. package/dist/metal/listQuery.d.ts +7 -0
  71. package/dist/metal/listQuery.d.ts.map +1 -0
  72. package/dist/metal/queryOptions.d.ts +8 -0
  73. package/dist/metal/queryOptions.d.ts.map +1 -0
  74. package/dist/metal/registerMetalEntities.d.ts.map +1 -1
  75. package/dist/runtime/metadata/types.d.ts +0 -3
  76. package/dist/runtime/metadata/types.d.ts.map +1 -1
  77. package/package.json +4 -1
  78. package/dist/compiler/analyze/extractQueryStyle.d.ts +0 -8
  79. package/dist/compiler/analyze/extractQueryStyle.d.ts.map +0 -1
  80. package/dist/decorators/Paginated.d.ts +0 -5
  81. package/dist/decorators/Paginated.d.ts.map +0 -1
package/dist/cli.cjs CHANGED
@@ -103,7 +103,7 @@ function analyzeClass(node, sourceFile, checker) {
103
103
  produces
104
104
  };
105
105
  }
106
- function extractClassConsumes(node, checker) {
106
+ function extractClassConsumes(node, _checker) {
107
107
  const decorator = findDecorator(node, "Consumes");
108
108
  if (!decorator) return void 0;
109
109
  const callExpr = decorator.expression;
@@ -119,7 +119,7 @@ function extractClassConsumes(node, checker) {
119
119
  }
120
120
  return void 0;
121
121
  }
122
- function extractClassProduces(node, checker) {
122
+ function extractClassProduces(node, _checker) {
123
123
  const decorator = findDecorator(node, "Produces");
124
124
  if (!decorator) return void 0;
125
125
  const callExpr = decorator.expression;
@@ -171,7 +171,7 @@ function analyzeMethod(node, className, checker) {
171
171
  }
172
172
  const pathParamNames = extractPathParams(path4);
173
173
  const pathParamIndices = matchPathParamsToIndices(pathParamNames, parameters);
174
- const { bodyParamIndex, queryParamIndices, queryObjectParamIndex, headerObjectParamIndex, cookieObjectParamIndex, paginationParamIndex, bodyContentType } = classifyParameters(parameters, httpMethod, pathParamIndices, checker);
174
+ const { bodyParamIndex, queryParamIndices, queryObjectParamIndex, headerObjectParamIndex, cookieObjectParamIndex, bodyContentType } = classifyParameters(parameters, httpMethod, pathParamIndices, checker);
175
175
  return {
176
176
  methodName,
177
177
  httpMethod,
@@ -187,7 +187,6 @@ function analyzeMethod(node, className, checker) {
187
187
  queryObjectParamIndex,
188
188
  headerObjectParamIndex,
189
189
  cookieObjectParamIndex,
190
- paginationParamIndex,
191
190
  bodyContentType
192
191
  };
193
192
  }
@@ -213,7 +212,6 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
213
212
  let queryObjectParamIndex = null;
214
213
  let headerObjectParamIndex = null;
215
214
  let cookieObjectParamIndex = null;
216
- let paginationParamIndex = null;
217
215
  const isBodyMethod = ["POST", "PUT", "PATCH"].includes(httpMethod);
218
216
  for (let i = 0; i < parameters.length; i++) {
219
217
  const param = parameters[i];
@@ -240,11 +238,6 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
240
238
  usedIndices.add(i);
241
239
  continue;
242
240
  }
243
- if (typeStr === "PaginationParams") {
244
- paginationParamIndex = i;
245
- usedIndices.add(i);
246
- continue;
247
- }
248
241
  if (isBodyMethod && bodyParamIndex === null) {
249
242
  bodyParamIndex = i;
250
243
  usedIndices.add(i);
@@ -265,19 +258,19 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
265
258
  queryObjectParamIndex,
266
259
  headerObjectParamIndex,
267
260
  cookieObjectParamIndex,
268
- paginationParamIndex,
269
261
  bodyContentType: void 0
270
262
  };
271
263
  }
272
264
  function isObjectType(type, checker) {
273
265
  const objectFlags = (type.flags & import_typescript2.default.TypeFlags.Object) !== 0;
274
- if (!objectFlags) return false;
266
+ const intersectionFlags = (type.flags & import_typescript2.default.TypeFlags.Intersection) !== 0;
267
+ if (!objectFlags && !intersectionFlags) return false;
275
268
  const symbol = type.getSymbol();
276
269
  if (symbol?.getName() === "__object") return true;
277
270
  const properties = checker.getPropertiesOfType(type);
278
271
  if (properties.length > 0) return true;
279
- const callSignatures = type.getCallSignatures();
280
- if (callSignatures && callSignatures.length > 0) return false;
272
+ const callSigs = type.getCallSignatures?.();
273
+ if (callSigs && callSigs.length > 0) return false;
281
274
  return true;
282
275
  }
283
276
  function getTypeName(type) {
@@ -308,7 +301,7 @@ function extractDecoratorStringArg(decorator) {
308
301
  }
309
302
  return null;
310
303
  }
311
- function unwrapPromise(type, checker) {
304
+ function unwrapPromise(type, _checker) {
312
305
  const symbol = type.getSymbol();
313
306
  if (symbol?.getName() === "Promise") {
314
307
  const typeArgs = type.typeArguments;
@@ -329,12 +322,15 @@ function unwrapPromiseTypeNode(typeNode) {
329
322
  }
330
323
 
331
324
  // src/compiler/schema/openapi.ts
332
- var import_typescript6 = __toESM(require("typescript"), 1);
325
+ var import_typescript9 = __toESM(require("typescript"), 1);
333
326
 
334
327
  // src/compiler/schema/typeToJsonSchema.ts
328
+ var import_typescript7 = __toESM(require("typescript"), 1);
329
+
330
+ // src/compiler/schema/primitives.ts
335
331
  var import_typescript3 = __toESM(require("typescript"), 1);
336
- function typeToJsonSchema(type, ctx, typeNode) {
337
- const { checker } = ctx;
332
+ function handlePrimitiveType(type, ctx, typeNode) {
333
+ const { checker, propertyName } = ctx;
338
334
  if (type.flags & import_typescript3.default.TypeFlags.Undefined) {
339
335
  return {};
340
336
  }
@@ -348,7 +344,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
348
344
  return { type: "string" };
349
345
  }
350
346
  if (type.flags & import_typescript3.default.TypeFlags.Number) {
351
- return { type: "number" };
347
+ return normalizeNumericType(type, checker, typeNode, propertyName);
352
348
  }
353
349
  if (type.flags & import_typescript3.default.TypeFlags.Boolean) {
354
350
  return { type: "boolean" };
@@ -372,26 +368,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
372
368
  const intrinsic = type.intrinsicName;
373
369
  return { type: "boolean", enum: [intrinsic === "true"] };
374
370
  }
375
- if (type.isUnion()) {
376
- return handleUnion(type, ctx, typeNode);
377
- }
378
- if (type.isIntersection()) {
379
- return handleIntersection(type, ctx, typeNode);
380
- }
381
- if (checker.isArrayType(type)) {
382
- const typeArgs = type.typeArguments;
383
- const itemType = typeArgs?.[0];
384
- const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
385
- return {
386
- type: "array",
387
- items,
388
- uniqueItems: isSetType(type, checker) ? true : void 0
389
- };
390
- }
391
- if (type.flags & import_typescript3.default.TypeFlags.Object) {
392
- return handleObjectType(type, ctx, typeNode);
393
- }
394
- return {};
371
+ return null;
395
372
  }
396
373
  function isDateType(type, checker) {
397
374
  const symbol = type.getSymbol();
@@ -406,53 +383,51 @@ function isDateType(type, checker) {
406
383
  }
407
384
  return symbol?.getName() === "Date";
408
385
  }
409
- function isSetType(type, checker) {
410
- const symbol = type.getSymbol();
411
- if (!symbol) return false;
412
- const name = symbol.getName();
413
- if (name === "Set") return true;
414
- return false;
415
- }
416
- function getSchemaName(type, typeNode) {
417
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
418
- const aliasName = aliasSymbol?.getName();
419
- if (aliasName && aliasName !== "__type") {
420
- return aliasName;
386
+ function normalizeNumericType(type, checker, typeNode, propertyName) {
387
+ const typeName = getExplicitTypeNameFromNode(typeNode) ?? null;
388
+ const symbol = getEffectiveSymbol(type, checker);
389
+ const symbolName = symbol?.getName() ?? null;
390
+ if (shouldBeIntegerType(typeName) || shouldBeIntegerType(symbolName) || shouldBeIntegerType(propertyName ?? null)) {
391
+ return { type: "integer" };
421
392
  }
422
- const symbol = type.getSymbol();
423
- const symbolName = symbol?.getName?.();
424
- if (symbolName && symbolName !== "__type") {
425
- return symbolName;
393
+ return { type: "number" };
394
+ }
395
+ function shouldBeIntegerType(typeName) {
396
+ if (!typeName) return false;
397
+ const lower = typeName.toLowerCase();
398
+ return lower === "id" || lower.endsWith("id") || lower === "primarykey" || lower === "pk" || lower === "page" || lower === "pagesize" || lower === "totalitems" || lower === "limit" || lower === "offset";
399
+ }
400
+ function getExplicitTypeNameFromNode(typeNode) {
401
+ if (!typeNode) return null;
402
+ if (import_typescript3.default.isTypeReferenceNode(typeNode)) {
403
+ if (import_typescript3.default.isIdentifier(typeNode.typeName)) {
404
+ return typeNode.typeName.text;
405
+ }
426
406
  }
427
- const nodeName = getExplicitTypeNameFromNode(typeNode);
428
- if (nodeName && nodeName !== "__type") {
429
- return nodeName;
407
+ if (import_typescript3.default.isTypeAliasDeclaration(typeNode.parent)) {
408
+ if (import_typescript3.default.isIdentifier(typeNode.parent.name)) {
409
+ return typeNode.parent.name.text;
410
+ }
430
411
  }
431
412
  return null;
432
413
  }
433
- function buildNamedSchema(type, ctx, typeNode, build) {
434
- const name = getSchemaName(type, typeNode);
435
- if (!name) {
436
- return build();
437
- }
438
- const { components, typeStack } = ctx;
439
- if (components.has(name) || typeStack.has(type)) {
440
- return { $ref: `#/components/schemas/${name}` };
441
- }
442
- typeStack.add(type);
443
- const schema = build();
444
- typeStack.delete(type);
445
- if (!components.has(name)) {
446
- components.set(name, schema);
414
+ function getEffectiveSymbol(type, checker) {
415
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
416
+ if (aliasSymbol && aliasSymbol.flags & import_typescript3.default.SymbolFlags.Alias) {
417
+ return checker.getAliasedSymbol(aliasSymbol);
447
418
  }
448
- return { $ref: `#/components/schemas/${name}` };
419
+ return type.getSymbol() ?? null;
449
420
  }
421
+
422
+ // src/compiler/schema/unionHandler.ts
423
+ var import_typescript4 = __toESM(require("typescript"), 1);
450
424
  function handleUnion(type, ctx, typeNode) {
451
425
  return buildNamedSchema(type, ctx, typeNode, () => {
452
426
  const types = type.types;
453
- const nullType = types.find((t) => t.flags & import_typescript3.default.TypeFlags.Null);
454
- const otherTypes = types.filter((t) => !(t.flags & import_typescript3.default.TypeFlags.Null) && !(t.flags & import_typescript3.default.TypeFlags.Undefined));
455
- const allStringLiterals = otherTypes.every((t) => t.flags & import_typescript3.default.TypeFlags.StringLiteral);
427
+ const nullType = types.find((t) => t.flags & import_typescript4.default.TypeFlags.Null);
428
+ const undefinedType = types.find((t) => t.flags & import_typescript4.default.TypeFlags.Undefined);
429
+ const otherTypes = types.filter((t) => !(t.flags & import_typescript4.default.TypeFlags.Null) && !(t.flags & import_typescript4.default.TypeFlags.Undefined));
430
+ const allStringLiterals = otherTypes.every((t) => t.flags & import_typescript4.default.TypeFlags.StringLiteral);
456
431
  if (allStringLiterals && otherTypes.length > 0) {
457
432
  const enumValues = otherTypes.map((t) => t.value);
458
433
  const schema = { type: "string", enum: enumValues };
@@ -461,6 +436,14 @@ function handleUnion(type, ctx, typeNode) {
461
436
  }
462
437
  return schema;
463
438
  }
439
+ const allBooleanLiterals = otherTypes.length > 0 && otherTypes.every((t) => t.flags & import_typescript4.default.TypeFlags.BooleanLiteral);
440
+ if (allBooleanLiterals) {
441
+ const schema = { type: "boolean" };
442
+ if (nullType || undefinedType) {
443
+ schema.type = ["boolean", "null"];
444
+ }
445
+ return schema;
446
+ }
464
447
  if (otherTypes.length === 1 && nullType) {
465
448
  const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
466
449
  if (typeof innerSchema.type === "string") {
@@ -490,49 +473,7 @@ function handleUnion(type, ctx, typeNode) {
490
473
  return {};
491
474
  });
492
475
  }
493
- function handleIntersection(type, ctx, typeNode) {
494
- return buildNamedSchema(type, ctx, typeNode, () => {
495
- const types = type.types;
496
- const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
497
- if (brandCollapsed) {
498
- return brandCollapsed;
499
- }
500
- const allOf = [];
501
- for (const t of types) {
502
- allOf.push(typeToJsonSchema(t, ctx));
503
- }
504
- return { allOf };
505
- });
506
- }
507
- function tryCollapseBrandedIntersection(types, ctx, typeNode) {
508
- const { checker } = ctx;
509
- const parts = [...types];
510
- const prim = parts.find(isPrimitiveLike);
511
- if (!prim) return null;
512
- const rest = parts.filter((p) => p !== prim);
513
- if (rest.every((r) => isBrandObject(checker, r, ctx))) {
514
- return typeToJsonSchema(prim, ctx);
515
- }
516
- return null;
517
- }
518
- function isPrimitiveLike(t) {
519
- return (t.flags & (import_typescript3.default.TypeFlags.String | import_typescript3.default.TypeFlags.Number | import_typescript3.default.TypeFlags.Boolean | import_typescript3.default.TypeFlags.BigInt)) !== 0 || (t.flags & import_typescript3.default.TypeFlags.StringLiteral) !== 0 || (t.flags & import_typescript3.default.TypeFlags.NumberLiteral) !== 0;
520
- }
521
- function isBrandObject(checker, t, ctx) {
522
- if (!(t.flags & import_typescript3.default.TypeFlags.Object)) return false;
523
- const props = t.getProperties();
524
- if (props.length === 0) return false;
525
- const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
526
- for (const p of props) {
527
- if (!allowed.has(p.getName())) return false;
528
- }
529
- const callSigs = t.getCallSignatures?.();
530
- if (callSigs && callSigs.length > 0) return false;
531
- const constructSigs = t.getConstructSignatures?.();
532
- if (constructSigs && constructSigs.length > 0) return false;
533
- return true;
534
- }
535
- function detectDiscriminatedUnion(types, ctx, branches) {
476
+ function detectDiscriminatedUnion(types, ctx, _branches) {
536
477
  if (types.length < 2) return null;
537
478
  const candidates = findCommonPropertyNames(ctx.checker, types);
538
479
  for (const propName of candidates) {
@@ -563,10 +504,10 @@ function findCommonPropertyNames(checker, types) {
563
504
  function isRequiredProperty(checker, type, propName) {
564
505
  const sym = checker.getPropertyOfType(type, propName);
565
506
  if (!sym) return false;
566
- if (sym.flags & import_typescript3.default.SymbolFlags.Optional) return false;
507
+ if (sym.flags & import_typescript4.default.SymbolFlags.Optional) return false;
567
508
  const propType = checker.getTypeOfSymbol(sym);
568
509
  if (propType.isUnion?.()) {
569
- const hasUndefined = propType.types.some((t) => (t.flags & import_typescript3.default.TypeFlags.Undefined) !== 0);
510
+ const hasUndefined = propType.types.some((t) => (t.flags & import_typescript4.default.TypeFlags.Undefined) !== 0);
570
511
  if (hasUndefined) return false;
571
512
  }
572
513
  return true;
@@ -609,11 +550,187 @@ function getBranchSchemaName(type, ctx) {
609
550
  }
610
551
  return `Anonymous_${ctx.typeNameStack.length}`;
611
552
  }
553
+ function buildNamedSchema(type, ctx, typeNode, build) {
554
+ const name = getSchemaName(type, typeNode);
555
+ if (!name) {
556
+ return build();
557
+ }
558
+ const { components, typeStack } = ctx;
559
+ if (components.has(name) || typeStack.has(type)) {
560
+ return { $ref: `#/components/schemas/${name}` };
561
+ }
562
+ typeStack.add(type);
563
+ const schema = build();
564
+ typeStack.delete(type);
565
+ if (!components.has(name)) {
566
+ components.set(name, schema);
567
+ }
568
+ return { $ref: `#/components/schemas/${name}` };
569
+ }
570
+ function getSchemaName(type, typeNode) {
571
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
572
+ const aliasName = aliasSymbol?.getName();
573
+ if (aliasName && aliasName !== "__type") {
574
+ return aliasName;
575
+ }
576
+ const symbol = type.getSymbol();
577
+ const symbolName = symbol?.getName?.();
578
+ if (symbolName && symbolName !== "__type") {
579
+ return symbolName;
580
+ }
581
+ const nodeName = getExplicitTypeNameFromNode2(typeNode);
582
+ if (nodeName && nodeName !== "__type") {
583
+ return nodeName;
584
+ }
585
+ return null;
586
+ }
587
+ function getExplicitTypeNameFromNode2(typeNode) {
588
+ if (!typeNode) return null;
589
+ if (import_typescript4.default.isTypeReferenceNode(typeNode)) {
590
+ if (import_typescript4.default.isIdentifier(typeNode.typeName)) {
591
+ return typeNode.typeName.text;
592
+ }
593
+ }
594
+ if (import_typescript4.default.isTypeAliasDeclaration(typeNode.parent)) {
595
+ if (import_typescript4.default.isIdentifier(typeNode.parent.name)) {
596
+ return typeNode.parent.name.text;
597
+ }
598
+ }
599
+ return null;
600
+ }
601
+
602
+ // src/compiler/schema/intersectionHandler.ts
603
+ var import_typescript5 = __toESM(require("typescript"), 1);
604
+ function handleIntersection(type, ctx, typeNode) {
605
+ return buildNamedSchema2(type, ctx, typeNode, () => {
606
+ const types = type.types;
607
+ const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
608
+ if (brandCollapsed) {
609
+ return brandCollapsed;
610
+ }
611
+ const allOf = [];
612
+ for (const t of types) {
613
+ const schema = typeToJsonSchema(t, ctx);
614
+ if (Object.keys(schema).length > 0) {
615
+ if (isEmptyObjectSchema(schema)) {
616
+ continue;
617
+ }
618
+ allOf.push(schema);
619
+ }
620
+ }
621
+ if (allOf.length === 0) {
622
+ return {};
623
+ }
624
+ if (allOf.length === 1) {
625
+ return allOf[0];
626
+ }
627
+ return { allOf };
628
+ });
629
+ }
630
+ function tryCollapseBrandedIntersection(types, ctx, _typeNode) {
631
+ const { checker } = ctx;
632
+ const parts = [...types];
633
+ const prim = parts.find(isPrimitiveLike);
634
+ if (!prim) return null;
635
+ const rest = parts.filter((p) => p !== prim);
636
+ if (rest.every((r) => isBrandObject(checker, r, ctx))) {
637
+ return typeToJsonSchema(prim, ctx);
638
+ }
639
+ return null;
640
+ }
641
+ function isPrimitiveLike(t) {
642
+ return (t.flags & (import_typescript5.default.TypeFlags.String | import_typescript5.default.TypeFlags.Number | import_typescript5.default.TypeFlags.Boolean | import_typescript5.default.TypeFlags.BigInt)) !== 0 || (t.flags & import_typescript5.default.TypeFlags.StringLiteral) !== 0 || (t.flags & import_typescript5.default.TypeFlags.NumberLiteral) !== 0;
643
+ }
644
+ function isBrandObject(checker, t, _ctx) {
645
+ if (!(t.flags & import_typescript5.default.TypeFlags.Object)) return false;
646
+ const props = t.getProperties();
647
+ if (props.length === 0) return false;
648
+ const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
649
+ for (const p of props) {
650
+ if (!allowed.has(p.getName())) return false;
651
+ }
652
+ const callSigs = t.getCallSignatures?.();
653
+ if (callSigs && callSigs.length > 0) return false;
654
+ const constructSigs = t.getConstructSignatures?.();
655
+ if (constructSigs && constructSigs.length > 0) return false;
656
+ return true;
657
+ }
658
+ function isEmptyObjectSchema(schema) {
659
+ if (schema.type !== "object") {
660
+ return false;
661
+ }
662
+ if (!schema.properties || Object.keys(schema.properties).length === 0) {
663
+ if (!schema.additionalProperties) {
664
+ return true;
665
+ }
666
+ }
667
+ return false;
668
+ }
669
+ function buildNamedSchema2(type, ctx, typeNode, build) {
670
+ const name = getSchemaName2(type, typeNode);
671
+ if (!name) {
672
+ return build();
673
+ }
674
+ const { components, typeStack } = ctx;
675
+ if (components.has(name) || typeStack.has(type)) {
676
+ return { $ref: `#/components/schemas/${name}` };
677
+ }
678
+ typeStack.add(type);
679
+ const schema = build();
680
+ typeStack.delete(type);
681
+ if (!components.has(name)) {
682
+ components.set(name, schema);
683
+ }
684
+ return { $ref: `#/components/schemas/${name}` };
685
+ }
686
+ function getSchemaName2(type, typeNode) {
687
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
688
+ const aliasName = aliasSymbol?.getName();
689
+ if (aliasName && aliasName !== "__type") {
690
+ return aliasName;
691
+ }
692
+ const symbol = type.getSymbol();
693
+ const symbolName = symbol?.getName?.();
694
+ if (symbolName && symbolName !== "__type") {
695
+ return symbolName;
696
+ }
697
+ const nodeName = getExplicitTypeNameFromNode3(typeNode);
698
+ if (nodeName && nodeName !== "__type") {
699
+ return nodeName;
700
+ }
701
+ return null;
702
+ }
703
+ function getExplicitTypeNameFromNode3(typeNode) {
704
+ if (!typeNode) return null;
705
+ if (import_typescript5.default.isTypeReferenceNode(typeNode)) {
706
+ if (import_typescript5.default.isIdentifier(typeNode.typeName)) {
707
+ return typeNode.typeName.text;
708
+ }
709
+ }
710
+ if (import_typescript5.default.isTypeAliasDeclaration(typeNode.parent)) {
711
+ if (import_typescript5.default.isIdentifier(typeNode.parent.name)) {
712
+ return typeNode.parent.name.text;
713
+ }
714
+ }
715
+ return null;
716
+ }
717
+
718
+ // src/compiler/schema/objectHandler.ts
719
+ var import_typescript6 = __toESM(require("typescript"), 1);
612
720
  function handleObjectType(type, ctx, typeNode) {
613
721
  const { checker, components, typeStack } = ctx;
614
722
  const symbol = type.getSymbol();
615
723
  const typeName = symbol?.getName?.() ?? getTypeNameFromNode(typeNode, ctx);
724
+ if (isMetalOrmWrapperType(type, checker)) {
725
+ return handleMetalOrmWrapper(type, ctx);
726
+ }
616
727
  if (typeName && typeName !== "__type") {
728
+ const isMetalOrmGeneric = METAL_ORM_WRAPPER_NAMES.some(
729
+ (name) => typeName === name || typeName.endsWith("Api")
730
+ );
731
+ if (isMetalOrmGeneric) {
732
+ return {};
733
+ }
617
734
  if (components.has(typeName)) {
618
735
  return { $ref: `#/components/schemas/${typeName}` };
619
736
  }
@@ -625,7 +742,8 @@ function handleObjectType(type, ctx, typeNode) {
625
742
  const schema = buildObjectSchema(type, ctx, typeNode);
626
743
  if (typeName && typeName !== "__type") {
627
744
  typeStack.delete(type);
628
- if (!components.has(typeName)) {
745
+ const existing = components.get(typeName);
746
+ if (!existing) {
629
747
  components.set(typeName, schema);
630
748
  }
631
749
  return { $ref: `#/components/schemas/${typeName}` };
@@ -633,36 +751,26 @@ function handleObjectType(type, ctx, typeNode) {
633
751
  typeStack.delete(type);
634
752
  return schema;
635
753
  }
636
- function getExplicitTypeNameFromNode(typeNode) {
637
- if (!typeNode) return null;
638
- if (import_typescript3.default.isTypeReferenceNode(typeNode)) {
639
- if (import_typescript3.default.isIdentifier(typeNode.typeName)) {
640
- return typeNode.typeName.text;
641
- }
642
- }
643
- if (import_typescript3.default.isTypeAliasDeclaration(typeNode.parent)) {
644
- if (import_typescript3.default.isIdentifier(typeNode.parent.name)) {
645
- return typeNode.parent.name.text;
646
- }
647
- }
648
- return null;
649
- }
650
- function getTypeNameFromNode(typeNode, ctx) {
651
- const explicitName = getExplicitTypeNameFromNode(typeNode);
652
- if (explicitName) return explicitName;
653
- return `Anonymous_${ctx.typeNameStack.length}`;
654
- }
655
- function buildObjectSchema(type, ctx, typeNode) {
656
- const { checker } = ctx;
754
+ function buildObjectSchema(type, ctx, _typeNode) {
755
+ const { checker, mode } = ctx;
657
756
  const properties = {};
658
757
  const required = [];
659
758
  const props = checker.getPropertiesOfType(type);
660
759
  for (const prop of props) {
661
760
  const propName = prop.getName();
761
+ if (isIteratorOrSymbolProperty(propName)) {
762
+ continue;
763
+ }
662
764
  const propType = checker.getTypeOfSymbol(prop);
663
- const isOptional = !!(prop.flags & import_typescript3.default.SymbolFlags.Optional);
664
- properties[propName] = typeToJsonSchema(propType, ctx);
665
- if (!isOptional) {
765
+ if (isMethodLike(propType)) {
766
+ continue;
767
+ }
768
+ const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
769
+ const isRelation = isMetalOrmWrapperType(propType, checker);
770
+ const propCtx = { ...ctx, propertyName: propName };
771
+ properties[propName] = typeToJsonSchema(propType, propCtx);
772
+ const shouldRequire = mode === "response" ? !isRelation && !isOptional : !isOptional;
773
+ if (shouldRequire) {
666
774
  required.push(propName);
667
775
  }
668
776
  }
@@ -681,14 +789,14 @@ function buildObjectSchema(type, ctx, typeNode) {
681
789
  }
682
790
  return schema;
683
791
  }
684
- function isRecordType(type, checker) {
792
+ function isRecordType(type, _checker) {
685
793
  const symbol = type.getSymbol();
686
794
  if (!symbol) return false;
687
795
  const name = symbol.getName();
688
796
  if (name === "Record") return true;
689
797
  return false;
690
798
  }
691
- function getRecordValueType(type, checker) {
799
+ function getRecordValueType(type, _checker) {
692
800
  const symbol = type.getSymbol();
693
801
  if (!symbol) return null;
694
802
  const name = symbol.getName();
@@ -701,24 +809,229 @@ function getRecordValueType(type, checker) {
701
809
  }
702
810
  return null;
703
811
  }
812
+ function isMetalOrmWrapperType(type, checker) {
813
+ return !!findMetalOrmWrapper(type, checker);
814
+ }
815
+ function isMethodLike(type) {
816
+ const callSigs = type.getCallSignatures?.();
817
+ return !!(callSigs && callSigs.length > 0);
818
+ }
819
+ function isIteratorOrSymbolProperty(propName) {
820
+ return propName.startsWith("__@") || propName.startsWith("[") || propName === Symbol.iterator.toString();
821
+ }
822
+ function getTypeNameFromNode(typeNode, _ctx) {
823
+ const explicitName = getExplicitTypeNameFromNode4(typeNode);
824
+ if (explicitName) return explicitName;
825
+ return "Anonymous_${ctx.typeNameStack.length}";
826
+ }
827
+ function getExplicitTypeNameFromNode4(typeNode) {
828
+ if (!typeNode) return null;
829
+ if (import_typescript6.default.isTypeReferenceNode(typeNode)) {
830
+ if (import_typescript6.default.isIdentifier(typeNode.typeName)) {
831
+ return typeNode.typeName.text;
832
+ }
833
+ }
834
+ if (import_typescript6.default.isTypeAliasDeclaration(typeNode.parent)) {
835
+ if (import_typescript6.default.isIdentifier(typeNode.parent.name)) {
836
+ return typeNode.parent.name.text;
837
+ }
838
+ }
839
+ return null;
840
+ }
841
+ var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
842
+ function findMetalOrmWrapper(type, checker) {
843
+ if (type.isIntersection()) {
844
+ let wrapperInfo = null;
845
+ let hasReadonlyArray = false;
846
+ for (const constituent of type.types) {
847
+ const result = findWrapperInType(constituent, checker);
848
+ if (result) {
849
+ wrapperInfo = result;
850
+ }
851
+ if (!(constituent.flags & import_typescript6.default.TypeFlags.Object)) continue;
852
+ const symbol = constituent.getSymbol();
853
+ if (symbol?.getName() === "ReadonlyArray") {
854
+ hasReadonlyArray = true;
855
+ }
856
+ }
857
+ if (wrapperInfo) {
858
+ return { ...wrapperInfo, isReadonlyArray: hasReadonlyArray };
859
+ }
860
+ return null;
861
+ }
862
+ return findWrapperInType(type, checker);
863
+ }
864
+ function findWrapperInType(type, checker) {
865
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
866
+ const symbol = type.getSymbol();
867
+ const effectiveSymbol = aliasSymbol && aliasSymbol.flags & import_typescript6.default.SymbolFlags.Alias ? checker.getAliasedSymbol(aliasSymbol) : symbol;
868
+ if (!effectiveSymbol) return null;
869
+ const name = effectiveSymbol.getName();
870
+ if (!METAL_ORM_WRAPPER_NAMES.includes(name)) return null;
871
+ const typeRef = type;
872
+ const typeArgs = typeRef.typeArguments || [];
873
+ return {
874
+ wrapperName: name,
875
+ targetTypeArgs: typeArgs,
876
+ isReadonlyArray: false
877
+ };
878
+ }
879
+ function getWrapperTypeName(type, _checker) {
880
+ const symbol = type.getSymbol();
881
+ if (!symbol) return null;
882
+ const name = symbol.getName();
883
+ return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
884
+ }
885
+ function handleMetalOrmWrapper(type, ctx) {
886
+ const typeRef = type;
887
+ const typeArgs = typeRef.typeArguments;
888
+ const targetType = typeArgs?.[0] ?? null;
889
+ const wrapperName = getWrapperTypeName(type, ctx.checker);
890
+ if (!wrapperName) return {};
891
+ const wrapperRel = { wrapper: wrapperName };
892
+ if (wrapperName === "HasManyCollection" || wrapperName === "ManyToManyCollection") {
893
+ const items = targetType ? typeToJsonSchema(targetType, ctx) : {};
894
+ if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
895
+ wrapperRel.pivot = typeArgs[1];
896
+ }
897
+ return {
898
+ type: "array",
899
+ items,
900
+ "x-metal-orm-rel": wrapperRel
901
+ };
902
+ }
903
+ if (!targetType) {
904
+ return { "x-metal-orm-rel": wrapperRel };
905
+ }
906
+ if (wrapperName === "BelongsToReference" || wrapperName === "HasOneReference") {
907
+ return handleBelongsToReference(targetType, ctx, wrapperRel);
908
+ }
909
+ const targetSchema = typeToJsonSchema(targetType, ctx);
910
+ return {
911
+ ...targetSchema,
912
+ "x-metal-orm-rel": wrapperRel
913
+ };
914
+ }
915
+ function handleBelongsToReference(targetType, ctx, wrapperRel) {
916
+ const { components } = ctx;
917
+ const targetSymbol = targetType.getSymbol();
918
+ const typeName = targetSymbol?.getName();
919
+ if (!typeName) {
920
+ return {
921
+ type: "object",
922
+ properties: {},
923
+ "x-metal-orm-rel": wrapperRel
924
+ };
925
+ }
926
+ const refSchemaName = `${typeName}Ref`;
927
+ if (components.has(refSchemaName)) {
928
+ return {
929
+ $ref: `#/components/schemas/${refSchemaName}`,
930
+ "x-metal-orm-rel": wrapperRel
931
+ };
932
+ }
933
+ const refSchema = buildRefSchema(targetType, ctx);
934
+ components.set(refSchemaName, refSchema);
935
+ return {
936
+ $ref: `#/components/schemas/${refSchemaName}`,
937
+ "x-metal-orm-rel": wrapperRel
938
+ };
939
+ }
940
+ function buildRefSchema(type, ctx) {
941
+ const { checker } = ctx;
942
+ if (!(type.flags & import_typescript6.default.TypeFlags.Object)) {
943
+ return { type: "object", properties: {} };
944
+ }
945
+ const objectType = type;
946
+ const properties = {};
947
+ const required = [];
948
+ const props = checker.getPropertiesOfType(objectType);
949
+ for (const prop of props) {
950
+ const propName = prop.getName();
951
+ if (isIteratorOrSymbolProperty(propName)) {
952
+ continue;
953
+ }
954
+ const propType = checker.getTypeOfSymbol(prop);
955
+ if (isMethodLike(propType)) {
956
+ continue;
957
+ }
958
+ const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
959
+ const isRelation = isMetalOrmWrapperType(propType, checker);
960
+ if (isRelation) {
961
+ continue;
962
+ }
963
+ const propCtx = { ...ctx, propertyName: propName };
964
+ properties[propName] = typeToJsonSchema(propType, propCtx);
965
+ if (!isOptional) {
966
+ required.push(propName);
967
+ }
968
+ }
969
+ const schema = {
970
+ type: "object",
971
+ properties
972
+ };
973
+ if (required.length > 0) {
974
+ schema.required = required;
975
+ }
976
+ return schema;
977
+ }
978
+
979
+ // src/compiler/schema/typeToJsonSchema.ts
980
+ function typeToJsonSchema(type, ctx, typeNode) {
981
+ const primitiveResult = handlePrimitiveType(type, ctx, typeNode);
982
+ if (primitiveResult) {
983
+ return primitiveResult;
984
+ }
985
+ if (type.isUnion()) {
986
+ return handleUnion(type, ctx, typeNode);
987
+ }
988
+ if (type.isIntersection()) {
989
+ return handleIntersection(type, ctx, typeNode);
990
+ }
991
+ if (ctx.checker.isArrayType(type)) {
992
+ const typeArgs = type.typeArguments;
993
+ const itemType = typeArgs?.[0];
994
+ const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
995
+ return {
996
+ type: "array",
997
+ items,
998
+ uniqueItems: isSetType(type, ctx.checker) ? true : void 0
999
+ };
1000
+ }
1001
+ if (type.flags & import_typescript7.default.TypeFlags.Object) {
1002
+ const objectType = type;
1003
+ if (isMetalOrmWrapperType(type, ctx.checker)) {
1004
+ return handleMetalOrmWrapper(objectType, ctx);
1005
+ }
1006
+ return handleObjectType(objectType, ctx, typeNode);
1007
+ }
1008
+ return {};
1009
+ }
1010
+ function isSetType(type, _checker) {
1011
+ const symbol = type.getSymbol();
1012
+ if (!symbol) return false;
1013
+ const name = symbol.getName();
1014
+ if (name === "Set") return true;
1015
+ return false;
1016
+ }
704
1017
 
705
1018
  // src/compiler/schema/extractAnnotations.ts
706
- var import_typescript4 = __toESM(require("typescript"), 1);
1019
+ var import_typescript8 = __toESM(require("typescript"), 1);
707
1020
  function extractPropertySchemaFragments(checker, prop) {
708
- if (!import_typescript4.default.canHaveDecorators(prop)) return [];
709
- const decs = import_typescript4.default.getDecorators(prop);
1021
+ if (!import_typescript8.default.canHaveDecorators(prop)) return [];
1022
+ const decs = import_typescript8.default.getDecorators(prop);
710
1023
  if (!decs || decs.length === 0) return [];
711
1024
  const frags = [];
712
1025
  for (const d of decs) {
713
1026
  const expr = d.expression;
714
1027
  let callee;
715
1028
  let args;
716
- if (import_typescript4.default.isCallExpression(expr)) {
1029
+ if (import_typescript8.default.isCallExpression(expr)) {
717
1030
  callee = expr.expression;
718
1031
  args = expr.arguments;
719
1032
  } else {
720
1033
  callee = expr;
721
- args = import_typescript4.default.factory.createNodeArray([]);
1034
+ args = import_typescript8.default.factory.createNodeArray([]);
722
1035
  }
723
1036
  const sym = checker.getSymbolAtLocation(callee);
724
1037
  if (!sym) continue;
@@ -727,7 +1040,7 @@ function extractPropertySchemaFragments(checker, prop) {
727
1040
  const name = resolved.name;
728
1041
  if (name === "Schema") {
729
1042
  const obj = args[0];
730
- if (obj && import_typescript4.default.isObjectLiteralExpression(obj)) {
1043
+ if (obj && import_typescript8.default.isObjectLiteralExpression(obj)) {
731
1044
  const frag = objectLiteralToJson(obj);
732
1045
  if (frag) frags.push(frag);
733
1046
  }
@@ -749,7 +1062,7 @@ function extractPropertySchemaFragments(checker, prop) {
749
1062
  frags.push({ format: args[0].text });
750
1063
  } else if (name === "Pattern") {
751
1064
  const arg = args[0];
752
- if (arg && import_typescript4.default.isRegularExpressionLiteral(arg)) {
1065
+ if (arg && import_typescript8.default.isRegularExpressionLiteral(arg)) {
753
1066
  frags.push({ pattern: extractRegexPattern(arg.text) });
754
1067
  } else if (isStringLiteral(arg)) {
755
1068
  frags.push({ pattern: arg.text });
@@ -766,11 +1079,11 @@ function extractPropertySchemaFragments(checker, prop) {
766
1079
  frags.push({ multipleOf: Number(args[0].text) });
767
1080
  } else if (name === "Example") {
768
1081
  frags.push({ example: literalToJson(args[0]) });
769
- } else if (name === "Examples" && import_typescript4.default.isArrayLiteralExpression(args[0])) {
1082
+ } else if (name === "Examples" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
770
1083
  frags.push({ examples: args[0].elements.map((e) => literalToJson(e)) });
771
1084
  } else if (name === "Description" && isStringLiteral(args[0])) {
772
1085
  frags.push({ description: args[0].text });
773
- } else if (name === "Enum" && import_typescript4.default.isArrayLiteralExpression(args[0])) {
1086
+ } else if (name === "Enum" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
774
1087
  frags.push({ enum: args[0].elements.map((e) => literalToJson(e)) });
775
1088
  } else if (name === "Const") {
776
1089
  frags.push({ const: literalToJson(args[0]) });
@@ -778,9 +1091,9 @@ function extractPropertySchemaFragments(checker, prop) {
778
1091
  frags.push({ default: literalToJson(args[0]) });
779
1092
  } else if (name === "AdditionalProperties") {
780
1093
  const arg = args[0];
781
- if (arg && (arg.kind === import_typescript4.default.SyntaxKind.FalseKeyword || arg.kind === import_typescript4.default.SyntaxKind.TrueKeyword)) {
782
- frags.push({ additionalProperties: arg.kind === import_typescript4.default.SyntaxKind.TrueKeyword });
783
- } else if (arg && import_typescript4.default.isObjectLiteralExpression(arg)) {
1094
+ if (arg && (arg.kind === import_typescript8.default.SyntaxKind.FalseKeyword || arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword)) {
1095
+ frags.push({ additionalProperties: arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword });
1096
+ } else if (arg && import_typescript8.default.isObjectLiteralExpression(arg)) {
784
1097
  const obj = objectLiteralToJson(arg);
785
1098
  if (obj) frags.push({ additionalProperties: obj });
786
1099
  }
@@ -793,7 +1106,7 @@ function extractPropertySchemaFragments(checker, prop) {
793
1106
  return frags;
794
1107
  }
795
1108
  function resolveImportedDecorator(checker, sym) {
796
- const target = sym.flags & import_typescript4.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
1109
+ const target = sym.flags & import_typescript8.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
797
1110
  const name = target.getName();
798
1111
  const decl = target.declarations?.[0];
799
1112
  if (!decl) return null;
@@ -801,111 +1114,320 @@ function resolveImportedDecorator(checker, sym) {
801
1114
  if (fileName.includes("/node_modules/adorn-api/") || fileName.includes("/src/schema/")) {
802
1115
  return { module: "adorn-api/schema", name };
803
1116
  }
804
- return null;
805
- }
806
- function isNumberLiteral(node) {
807
- return !!node && import_typescript4.default.isNumericLiteral(node);
1117
+ return null;
1118
+ }
1119
+ function isNumberLiteral(node) {
1120
+ return !!node && import_typescript8.default.isNumericLiteral(node);
1121
+ }
1122
+ function isStringLiteral(node) {
1123
+ return !!node && import_typescript8.default.isStringLiteral(node);
1124
+ }
1125
+ function extractRegexPattern(text) {
1126
+ const match = text.match(/^\/(.+)\/[gimsuy]*$/);
1127
+ return match ? match[1] : text;
1128
+ }
1129
+ function objectLiteralToJson(obj) {
1130
+ const out = {};
1131
+ for (const prop of obj.properties) {
1132
+ if (!import_typescript8.default.isPropertyAssignment(prop)) continue;
1133
+ const name = prop.name;
1134
+ let key;
1135
+ if (import_typescript8.default.isIdentifier(name)) {
1136
+ key = name.text;
1137
+ } else if (import_typescript8.default.isStringLiteral(name)) {
1138
+ key = name.text;
1139
+ } else {
1140
+ continue;
1141
+ }
1142
+ out[key] = literalToJson(prop.initializer);
1143
+ }
1144
+ return out;
1145
+ }
1146
+ function literalToJson(node) {
1147
+ if (import_typescript8.default.isStringLiteral(node)) return node.text;
1148
+ if (import_typescript8.default.isNumericLiteral(node)) return Number(node.text);
1149
+ if (node.kind === import_typescript8.default.SyntaxKind.TrueKeyword) return true;
1150
+ if (node.kind === import_typescript8.default.SyntaxKind.FalseKeyword) return false;
1151
+ if (node.kind === import_typescript8.default.SyntaxKind.NullKeyword) return null;
1152
+ if (import_typescript8.default.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
1153
+ if (import_typescript8.default.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
1154
+ return void 0;
1155
+ }
1156
+ function mergeFragments(base, ...frags) {
1157
+ const result = { ...base };
1158
+ for (const frag of frags) {
1159
+ Object.assign(result, frag);
1160
+ }
1161
+ return result;
1162
+ }
1163
+
1164
+ // src/compiler/schema/parameters.ts
1165
+ function buildPathParameters(operation, ctx, parameters) {
1166
+ for (const paramIndex of operation.pathParamIndices) {
1167
+ const param = operation.parameters[paramIndex];
1168
+ if (param) {
1169
+ let paramSchema = typeToJsonSchema(param.type, ctx);
1170
+ if (param.paramNode) {
1171
+ const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1172
+ if (frags.length > 0) {
1173
+ paramSchema = mergeFragments(paramSchema, ...frags);
1174
+ }
1175
+ }
1176
+ const schema = paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema;
1177
+ const paramName = param.name.toLowerCase();
1178
+ const isIdParam = paramName === "id" || paramName.endsWith("id");
1179
+ if (!schema.$ref && schema.type === "number" && isIdParam) {
1180
+ schema.type = "integer";
1181
+ if (!schema.minimum) {
1182
+ schema.minimum = 1;
1183
+ }
1184
+ }
1185
+ parameters.push({
1186
+ name: param.name,
1187
+ in: "path",
1188
+ required: !param.isOptional,
1189
+ schema
1190
+ });
1191
+ }
1192
+ }
1193
+ }
1194
+ function buildQueryParameters(operation, ctx, parameters) {
1195
+ if (operation.queryObjectParamIndex !== null) {
1196
+ const queryParam = operation.parameters[operation.queryObjectParamIndex];
1197
+ if (!queryParam) return;
1198
+ const querySchema = typeToJsonSchema(queryParam.type, ctx);
1199
+ const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
1200
+ for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1201
+ const isRequired = queryRequired.includes(propName);
1202
+ const isObjectLike = isObjectLikeSchema(propSchema, ctx);
1203
+ const serialization = determineQuerySerialization(propSchema.type);
1204
+ const exampleValue = generateExampleValue(propSchema, propName);
1205
+ if (isObjectLike) {
1206
+ const schemaRef = propSchema.$ref || "#/components/schemas/InlineQueryParam";
1207
+ parameters.push({
1208
+ name: propName,
1209
+ in: "query",
1210
+ required: isRequired,
1211
+ schema: { type: "string" },
1212
+ description: `JSON-encoded object. ${exampleValue}`,
1213
+ examples: {
1214
+ default: { value: parseExampleValue(exampleValue) }
1215
+ },
1216
+ "x-adorn-jsonSchemaRef": schemaRef
1217
+ });
1218
+ } else {
1219
+ const paramDef = {
1220
+ name: propName,
1221
+ in: "query",
1222
+ required: isRequired,
1223
+ schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1224
+ };
1225
+ if (propName === "page") {
1226
+ paramDef.schema = { type: "integer", default: 1, minimum: 1 };
1227
+ } else if (propName === "pageSize") {
1228
+ paramDef.schema = { type: "integer", default: 10, minimum: 1 };
1229
+ } else if (propName === "totalItems") {
1230
+ paramDef.schema = { type: "integer", minimum: 0 };
1231
+ } else if (propName === "sort") {
1232
+ paramDef.schema = {
1233
+ oneOf: [
1234
+ { type: "string" },
1235
+ { type: "array", items: { type: "string" } }
1236
+ ]
1237
+ };
1238
+ } else if (propName === "q") {
1239
+ paramDef.schema = { type: "string" };
1240
+ } else if (propName === "hasComments") {
1241
+ paramDef.schema = { type: "boolean" };
1242
+ }
1243
+ if (Object.keys(serialization).length > 0) {
1244
+ Object.assign(paramDef, serialization);
1245
+ }
1246
+ parameters.push(paramDef);
1247
+ }
1248
+ }
1249
+ }
1250
+ for (const paramIndex of operation.queryParamIndices) {
1251
+ const param = operation.parameters[paramIndex];
1252
+ if (param) {
1253
+ let paramSchema = typeToJsonSchema(param.type, ctx);
1254
+ if (param.paramNode) {
1255
+ const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1256
+ if (frags.length > 0) {
1257
+ paramSchema = mergeFragments(paramSchema, ...frags);
1258
+ }
1259
+ }
1260
+ const isObjectLike = isObjectLikeSchema(paramSchema, ctx);
1261
+ if (isObjectLike) {
1262
+ const schemaRef = paramSchema.$ref || "#/components/schemas/InlineQueryParam";
1263
+ const exampleValue = generateExampleValue(paramSchema, param.name);
1264
+ parameters.push({
1265
+ name: param.name,
1266
+ in: "query",
1267
+ required: !param.isOptional,
1268
+ schema: { type: "string" },
1269
+ description: `JSON-encoded object. ${exampleValue}`,
1270
+ examples: {
1271
+ default: { value: parseExampleValue(exampleValue) }
1272
+ },
1273
+ "x-adorn-jsonSchemaRef": schemaRef
1274
+ });
1275
+ } else {
1276
+ const serialization = determineQuerySerialization(paramSchema.type);
1277
+ parameters.push({
1278
+ name: param.name,
1279
+ in: "query",
1280
+ required: !param.isOptional,
1281
+ schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema,
1282
+ ...Object.keys(serialization).length > 0 ? serialization : {}
1283
+ });
1284
+ }
1285
+ }
1286
+ }
1287
+ }
1288
+ function buildHeaderParameters(operation, ctx, parameters) {
1289
+ if (operation.headerObjectParamIndex === null) return;
1290
+ const headerParam = operation.parameters[operation.headerObjectParamIndex];
1291
+ if (!headerParam) return;
1292
+ const headerSchema = typeToJsonSchema(headerParam.type, ctx);
1293
+ if (!headerSchema.properties) return;
1294
+ const headerObjProps = headerSchema.properties;
1295
+ for (const [propName, propSchema] of Object.entries(headerObjProps)) {
1296
+ const isRequired = headerSchema.required?.includes(propName) ?? false;
1297
+ parameters.push({
1298
+ name: propName,
1299
+ in: "header",
1300
+ required: isRequired,
1301
+ schema: propSchema
1302
+ });
1303
+ }
808
1304
  }
809
- function isStringLiteral(node) {
810
- return !!node && import_typescript4.default.isStringLiteral(node);
1305
+ function buildCookieParameters(operation, ctx, parameters) {
1306
+ if (operation.cookieObjectParamIndex === null) return;
1307
+ const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
1308
+ if (!cookieParam) return;
1309
+ const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
1310
+ if (!cookieSchema.properties) return;
1311
+ const cookieObjProps = cookieSchema.properties;
1312
+ for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
1313
+ const isRequired = cookieSchema.required?.includes(propName) ?? false;
1314
+ parameters.push({
1315
+ name: propName,
1316
+ in: "cookie",
1317
+ required: isRequired,
1318
+ schema: propSchema,
1319
+ style: "form",
1320
+ explode: true
1321
+ });
1322
+ }
811
1323
  }
812
- function extractRegexPattern(text) {
813
- const match = text.match(/^\/(.+)\/[gimsuy]*$/);
814
- return match ? match[1] : text;
1324
+ function determineQuerySerialization(schemaType) {
1325
+ const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
1326
+ const isArray = typeArray.includes("array");
1327
+ if (isArray) {
1328
+ return { style: "form", explode: true };
1329
+ }
1330
+ return {};
815
1331
  }
816
- function objectLiteralToJson(obj) {
817
- const out = {};
818
- for (const prop of obj.properties) {
819
- if (!import_typescript4.default.isPropertyAssignment(prop)) continue;
820
- const name = prop.name;
821
- let key;
822
- if (import_typescript4.default.isIdentifier(name)) {
823
- key = name.text;
824
- } else if (import_typescript4.default.isStringLiteral(name)) {
825
- key = name.text;
826
- } else {
827
- continue;
1332
+ function generateExampleValue(schema, propName) {
1333
+ const resolved = resolveSchemaRef(schema, /* @__PURE__ */ new Map());
1334
+ if (resolved.type === "object" && resolved.properties) {
1335
+ const example = {};
1336
+ for (const [key, prop] of Object.entries(resolved.properties)) {
1337
+ const propResolved = resolveSchemaRef(prop, /* @__PURE__ */ new Map());
1338
+ if (propResolved.type === "string") {
1339
+ example[key] = "value";
1340
+ } else if (propResolved.type === "number" || propResolved.type === "integer") {
1341
+ example[key] = 1;
1342
+ } else if (propResolved.type === "boolean") {
1343
+ example[key] = true;
1344
+ } else if (Array.isArray(propResolved.type) && propResolved.type.includes("null")) {
1345
+ example[key] = null;
1346
+ } else if (propResolved.enum) {
1347
+ example[key] = propResolved.enum[0];
1348
+ } else {
1349
+ example[key] = "value";
1350
+ }
828
1351
  }
829
- out[key] = literalToJson(prop.initializer);
1352
+ return `Example: ${propName}=${JSON.stringify(example)}`;
830
1353
  }
831
- return out;
832
- }
833
- function literalToJson(node) {
834
- if (import_typescript4.default.isStringLiteral(node)) return node.text;
835
- if (import_typescript4.default.isNumericLiteral(node)) return Number(node.text);
836
- if (node.kind === import_typescript4.default.SyntaxKind.TrueKeyword) return true;
837
- if (node.kind === import_typescript4.default.SyntaxKind.FalseKeyword) return false;
838
- if (node.kind === import_typescript4.default.SyntaxKind.NullKeyword) return null;
839
- if (import_typescript4.default.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
840
- if (import_typescript4.default.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
841
- return void 0;
1354
+ return `Example: ${propName}=${JSON.stringify({ key: "value" })}`;
842
1355
  }
843
- function mergeFragments(base, ...frags) {
844
- const result = { ...base };
845
- for (const frag of frags) {
846
- Object.assign(result, frag);
1356
+ function parseExampleValue(description) {
1357
+ const match = description.match(/Example:\s*\w+=(\{[^}]+\})/);
1358
+ if (match) {
1359
+ return match[1];
847
1360
  }
848
- return result;
1361
+ return JSON.stringify({ key: "value" });
849
1362
  }
850
-
851
- // src/compiler/analyze/extractQueryStyle.ts
852
- var import_typescript5 = __toESM(require("typescript"), 1);
853
- function extractQueryStyleOptions(checker, method) {
854
- if (!import_typescript5.default.canHaveDecorators(method)) return null;
855
- const decorators = import_typescript5.default.getDecorators(method);
856
- if (!decorators || decorators.length === 0) return null;
857
- for (const decorator of decorators) {
858
- const expr = decorator.expression;
859
- const isCall = import_typescript5.default.isCallExpression(expr);
860
- const callee = isCall ? expr.expression : expr;
861
- const args = isCall ? expr.arguments : import_typescript5.default.factory.createNodeArray([]);
862
- const sym = checker.getSymbolAtLocation(callee);
863
- if (!sym) continue;
864
- const resolved = sym.flags & import_typescript5.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
865
- const name = resolved.getName();
866
- if (name !== "QueryStyle") continue;
867
- const optsNode = args[0];
868
- if (!optsNode || !import_typescript5.default.isObjectLiteralExpression(optsNode)) {
869
- return {};
1363
+ function isObjectLikeSchema(schema, ctx) {
1364
+ const resolved = resolveSchemaRef(schema, ctx.components);
1365
+ if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1366
+ return true;
1367
+ }
1368
+ if (resolved.allOf) {
1369
+ for (const branch of resolved.allOf) {
1370
+ if (isObjectLikeSchema(branch, ctx)) {
1371
+ return true;
1372
+ }
870
1373
  }
871
- return parseQueryStyleOptions(optsNode);
872
1374
  }
873
- return null;
1375
+ if (resolved.type === "array" && resolved.items) {
1376
+ const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
1377
+ return isObjectLikeSchema(itemsSchema, ctx);
1378
+ }
1379
+ return false;
874
1380
  }
875
- function parseQueryStyleOptions(node) {
876
- const opts = {};
877
- for (const prop of node.properties) {
878
- if (!import_typescript5.default.isPropertyAssignment(prop)) continue;
879
- const name = getPropName(prop.name);
880
- if (!name) continue;
881
- if (name === "style" && import_typescript5.default.isStringLiteral(prop.initializer)) {
882
- const style = prop.initializer.text;
883
- opts.style = style;
884
- } else if (name === "explode" && isBooleanLiteral(prop.initializer)) {
885
- opts.explode = prop.initializer.kind === import_typescript5.default.SyntaxKind.TrueKeyword;
886
- } else if (name === "allowReserved" && isBooleanLiteral(prop.initializer)) {
887
- opts.allowReserved = prop.initializer.kind === import_typescript5.default.SyntaxKind.TrueKeyword;
888
- }
889
- }
890
- return opts;
891
- }
892
- function getPropName(name) {
893
- if (import_typescript5.default.isIdentifier(name)) return name.text;
894
- if (import_typescript5.default.isStringLiteral(name)) return name.text;
895
- return null;
1381
+ function resolveSchemaRef(schema, components) {
1382
+ const ref = schema.$ref;
1383
+ if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
1384
+ return schema;
1385
+ }
1386
+ const name = ref.replace("#/components/schemas/", "");
1387
+ const next = components.get(name);
1388
+ if (!next) return schema;
1389
+ return resolveSchemaRef(next, components);
896
1390
  }
897
- function isBooleanLiteral(node) {
898
- return node.kind === import_typescript5.default.SyntaxKind.TrueKeyword || node.kind === import_typescript5.default.SyntaxKind.FalseKeyword;
1391
+ function resolveAndCollectObjectProps(schema, components) {
1392
+ const resolved = resolveSchemaRef(schema, components);
1393
+ const properties = {};
1394
+ const required = [];
1395
+ const processSchema = (s) => {
1396
+ const current = resolveSchemaRef(s, components);
1397
+ if (current.properties) {
1398
+ for (const [key, val] of Object.entries(current.properties)) {
1399
+ if (!properties[key]) {
1400
+ properties[key] = val;
1401
+ }
1402
+ }
1403
+ }
1404
+ if (current.required) {
1405
+ for (const req of current.required) {
1406
+ if (!required.includes(req)) {
1407
+ required.push(req);
1408
+ }
1409
+ }
1410
+ }
1411
+ if (current.allOf) {
1412
+ for (const branch of current.allOf) {
1413
+ processSchema(branch);
1414
+ }
1415
+ }
1416
+ };
1417
+ processSchema(resolved);
1418
+ return { properties, required };
899
1419
  }
900
1420
 
901
1421
  // src/compiler/schema/openapi.ts
1422
+ var METAL_ORM_WRAPPER_NAMES2 = ["BelongsToReference", "HasOneReference", "HasManyCollection", "ManyToManyCollection"];
902
1423
  function generateOpenAPI(controllers, checker, options = {}) {
903
1424
  const components = /* @__PURE__ */ new Map();
904
1425
  const ctx = {
905
1426
  checker,
906
1427
  components,
907
1428
  typeStack: /* @__PURE__ */ new Set(),
908
- typeNameStack: []
1429
+ typeNameStack: [],
1430
+ mode: "response"
909
1431
  };
910
1432
  const paths = {};
911
1433
  for (const controller of controllers) {
@@ -918,6 +1440,8 @@ function generateOpenAPI(controllers, checker, options = {}) {
918
1440
  paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
919
1441
  }
920
1442
  }
1443
+ const schemas = Object.fromEntries(components);
1444
+ cleanupMetalOrmWrappers(schemas, paths);
921
1445
  return {
922
1446
  openapi: "3.1.0",
923
1447
  info: {
@@ -925,15 +1449,120 @@ function generateOpenAPI(controllers, checker, options = {}) {
925
1449
  version: options.version ?? "1.0.0"
926
1450
  },
927
1451
  components: {
928
- schemas: Object.fromEntries(components)
1452
+ schemas
929
1453
  },
930
1454
  paths
931
1455
  };
932
1456
  }
1457
+ function cleanupMetalOrmWrappers(schemas, paths) {
1458
+ const schemasToDelete = /* @__PURE__ */ new Set();
1459
+ for (const wrapperName of METAL_ORM_WRAPPER_NAMES2) {
1460
+ if (schemas[wrapperName]) {
1461
+ schemasToDelete.add(wrapperName);
1462
+ }
1463
+ if (schemas[`${wrapperName}Api`]) {
1464
+ schemasToDelete.add(`${wrapperName}Api`);
1465
+ }
1466
+ }
1467
+ for (const schema of Object.values(schemas)) {
1468
+ cleanupSchemaRefs(schema, schemasToDelete);
1469
+ }
1470
+ for (const pathItem of Object.values(paths)) {
1471
+ cleanupPathItemRefs(pathItem, schemasToDelete);
1472
+ }
1473
+ for (const schemaName of schemasToDelete) {
1474
+ delete schemas[schemaName];
1475
+ }
1476
+ }
1477
+ function cleanupSchemaRefs(schema, schemasToDelete) {
1478
+ if (typeof schema !== "object" || schema === null) {
1479
+ return;
1480
+ }
1481
+ if (schema.properties) {
1482
+ for (const propName of Object.keys(schema.properties)) {
1483
+ const propSchema = schema.properties[propName];
1484
+ if (propSchema.$ref && typeof propSchema.$ref === "string") {
1485
+ const refName = propSchema.$ref.replace("#/components/schemas/", "");
1486
+ if (schemasToDelete.has(refName)) {
1487
+ delete schema.properties[propName];
1488
+ if (schema.required && Array.isArray(schema.required)) {
1489
+ schema.required = schema.required.filter((r) => r !== propName);
1490
+ }
1491
+ }
1492
+ } else {
1493
+ cleanupSchemaRefs(propSchema, schemasToDelete);
1494
+ }
1495
+ }
1496
+ }
1497
+ if (schema.items) {
1498
+ cleanupSchemaRefs(schema.items, schemasToDelete);
1499
+ }
1500
+ if (schema.allOf) {
1501
+ for (const item of schema.allOf) {
1502
+ cleanupSchemaRefs(item, schemasToDelete);
1503
+ }
1504
+ }
1505
+ }
1506
+ function cleanupPathItemRefs(pathItem, schemasToDelete) {
1507
+ if (typeof pathItem !== "object" || pathItem === null) {
1508
+ return;
1509
+ }
1510
+ for (const method of Object.keys(pathItem)) {
1511
+ const operation = pathItem[method];
1512
+ if (typeof operation !== "object" || operation === null) continue;
1513
+ if (operation.requestBody) {
1514
+ cleanupRequestBodyRefs(operation.requestBody, schemasToDelete);
1515
+ }
1516
+ if (operation.responses) {
1517
+ const responses = Object.values(operation.responses);
1518
+ for (const response of responses) {
1519
+ if (response.content) {
1520
+ const contentTypes = Object.values(response.content);
1521
+ for (const contentType of contentTypes) {
1522
+ if (contentType.schema) {
1523
+ cleanupSchemaRefs(contentType.schema, schemasToDelete);
1524
+ }
1525
+ }
1526
+ }
1527
+ }
1528
+ }
1529
+ }
1530
+ }
1531
+ function cleanupRequestBodyRefs(requestBody, schemasToDelete) {
1532
+ if (typeof requestBody !== "object" || requestBody === null) return;
1533
+ if (requestBody.content) {
1534
+ const contentTypes = Object.values(requestBody.content);
1535
+ for (const contentType of contentTypes) {
1536
+ if (contentType.schema) {
1537
+ cleanupSchemaRefs(contentType.schema, schemasToDelete);
1538
+ }
1539
+ }
1540
+ }
1541
+ if (requestBody.properties) {
1542
+ for (const propName of Object.keys(requestBody.properties)) {
1543
+ const propSchema = requestBody.properties[propName];
1544
+ if (propSchema.$ref && typeof propSchema.$ref === "string") {
1545
+ const refName = propSchema.$ref.replace("#/components/schemas/", "");
1546
+ if (schemasToDelete.has(refName)) {
1547
+ delete requestBody.properties[propName];
1548
+ if (requestBody.required && Array.isArray(requestBody.required)) {
1549
+ requestBody.required = requestBody.required.filter((r) => r !== propName);
1550
+ }
1551
+ }
1552
+ } else {
1553
+ cleanupSchemaRefs(propSchema, schemasToDelete);
1554
+ }
1555
+ }
1556
+ }
1557
+ }
933
1558
  function convertToOpenApiPath(basePath, path4) {
934
1559
  const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
935
1560
  const converted = path4.replace(/:([^/]+)/g, "{$1}");
936
- return base + converted || "/";
1561
+ let fullPath = base + converted || "/";
1562
+ if (fullPath.endsWith("/") && fullPath !== "/") {
1563
+ fullPath = fullPath.slice(0, -1);
1564
+ }
1565
+ return fullPath;
937
1566
  }
938
1567
  function buildOperation(operation, ctx, controllerConsumes) {
939
1568
  const op = {
@@ -948,7 +1577,8 @@ function buildOperation(operation, ctx, controllerConsumes) {
948
1577
  if (parameters.length > 0) {
949
1578
  op.parameters = parameters;
950
1579
  }
951
- const responseSchema = typeToJsonSchema(operation.returnType, ctx, operation.returnTypeNode);
1580
+ const responseCtx = { ...ctx, mode: "response" };
1581
+ const responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
952
1582
  const status = operation.httpMethod === "POST" ? 201 : 200;
953
1583
  op.responses[status] = {
954
1584
  description: status === 201 ? "Created" : "OK",
@@ -961,8 +1591,9 @@ function buildOperation(operation, ctx, controllerConsumes) {
961
1591
  if (["POST", "PUT", "PATCH"].includes(operation.httpMethod) && operation.bodyParamIndex !== null) {
962
1592
  const bodyParam = operation.parameters[operation.bodyParamIndex];
963
1593
  if (bodyParam) {
964
- let bodySchema = typeToJsonSchema(bodyParam.type, ctx);
965
- bodySchema = mergeBodySchemaAnnotations(bodyParam, ctx, bodySchema);
1594
+ const requestCtx = { ...ctx, mode: "request" };
1595
+ let bodySchema = typeToJsonSchema(bodyParam.type, requestCtx);
1596
+ bodySchema = mergeBodySchemaAnnotations(bodyParam, requestCtx, bodySchema);
966
1597
  const contentType = operation.bodyContentType ?? controllerConsumes?.[0] ?? "application/json";
967
1598
  const requestBody = {
968
1599
  required: !bodyParam.isOptional,
@@ -989,12 +1620,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
989
1620
  const declarations = typeSymbol.getDeclarations();
990
1621
  if (!declarations || declarations.length === 0) return schema;
991
1622
  const classDecl = declarations[0];
992
- if (!import_typescript6.default.isClassDeclaration(classDecl)) return schema;
1623
+ if (!import_typescript9.default.isClassDeclaration(classDecl)) return schema;
993
1624
  const result = { ...schema };
994
1625
  const props = { ...result.properties };
995
1626
  for (const member of classDecl.members) {
996
- if (!import_typescript6.default.isPropertyDeclaration(member) || !member.name) continue;
997
- const propName = import_typescript6.default.isIdentifier(member.name) ? member.name.text : null;
1627
+ if (!import_typescript9.default.isPropertyDeclaration(member) || !member.name) continue;
1628
+ const propName = import_typescript9.default.isIdentifier(member.name) ? member.name.text : null;
998
1629
  if (!propName) continue;
999
1630
  if (!props[propName]) continue;
1000
1631
  const frags = extractPropertySchemaFragments(ctx.checker, member);
@@ -1005,137 +1636,17 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
1005
1636
  result.properties = props;
1006
1637
  return result;
1007
1638
  }
1008
- function buildPathParameters(operation, ctx, parameters) {
1009
- for (const paramIndex of operation.pathParamIndices) {
1010
- const param = operation.parameters[paramIndex];
1011
- if (param) {
1012
- let paramSchema = typeToJsonSchema(param.type, ctx);
1013
- if (param.paramNode) {
1014
- const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1015
- if (frags.length > 0) {
1016
- paramSchema = mergeFragments(paramSchema, ...frags);
1017
- }
1018
- }
1019
- parameters.push({
1020
- name: param.name,
1021
- in: "path",
1022
- required: !param.isOptional,
1023
- schema: paramSchema.$ref ? { type: "string", $ref: paramSchema.$ref } : paramSchema
1024
- });
1025
- }
1026
- }
1027
- }
1028
- function buildQueryParameters(operation, ctx, parameters) {
1029
- if (operation.queryObjectParamIndex !== null) {
1030
- const queryParam = operation.parameters[operation.queryObjectParamIndex];
1031
- if (!queryParam) return;
1032
- const queryStyle = extractQueryStyleOptions(ctx.checker, operation.methodDeclaration);
1033
- const querySchema = typeToJsonSchema(queryParam.type, ctx);
1034
- if (queryStyle?.style === "deepObject") {
1035
- const explode = queryStyle.explode ?? true;
1036
- const deepParam = {
1037
- name: queryParam.name,
1038
- in: "query",
1039
- required: !queryParam.isOptional,
1040
- schema: querySchema.$ref ? { $ref: querySchema.$ref } : querySchema,
1041
- style: "deepObject",
1042
- explode
1043
- };
1044
- if (queryStyle.allowReserved !== void 0) {
1045
- deepParam.allowReserved = queryStyle.allowReserved;
1046
- }
1047
- parameters.push(deepParam);
1048
- } else {
1049
- if (!querySchema.properties) return;
1050
- const queryObjProps = querySchema.properties;
1051
- for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1052
- const isRequired = querySchema.required?.includes(propName) ?? false;
1053
- const serialization = determineQuerySerialization(propSchema.type);
1054
- parameters.push({
1055
- name: propName,
1056
- in: "query",
1057
- required: isRequired,
1058
- schema: propSchema,
1059
- ...Object.keys(serialization).length > 0 ? serialization : {}
1060
- });
1061
- }
1062
- }
1063
- }
1064
- for (const paramIndex of operation.queryParamIndices) {
1065
- const param = operation.parameters[paramIndex];
1066
- if (param) {
1067
- let paramSchema = typeToJsonSchema(param.type, ctx);
1068
- if (param.paramNode) {
1069
- const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1070
- if (frags.length > 0) {
1071
- paramSchema = mergeFragments(paramSchema, ...frags);
1072
- }
1073
- }
1074
- const serialization = determineQuerySerialization(paramSchema.type);
1075
- parameters.push({
1076
- name: param.name,
1077
- in: "query",
1078
- required: !param.isOptional,
1079
- schema: paramSchema.$ref ? { type: "string", $ref: paramSchema.$ref } : paramSchema,
1080
- ...Object.keys(serialization).length > 0 ? serialization : {}
1081
- });
1082
- }
1083
- }
1084
- }
1085
- function determineQuerySerialization(schemaType) {
1086
- const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
1087
- const isArray = typeArray.includes("array");
1088
- if (isArray) {
1089
- return { style: "form", explode: true };
1090
- }
1091
- return {};
1092
- }
1093
- function buildHeaderParameters(operation, ctx, parameters) {
1094
- if (operation.headerObjectParamIndex === null) return;
1095
- const headerParam = operation.parameters[operation.headerObjectParamIndex];
1096
- if (!headerParam) return;
1097
- const headerSchema = typeToJsonSchema(headerParam.type, ctx);
1098
- if (!headerSchema.properties) return;
1099
- const headerObjProps = headerSchema.properties;
1100
- for (const [propName, propSchema] of Object.entries(headerObjProps)) {
1101
- const isRequired = headerSchema.required?.includes(propName) ?? false;
1102
- parameters.push({
1103
- name: propName,
1104
- in: "header",
1105
- required: isRequired,
1106
- schema: propSchema
1107
- });
1108
- }
1109
- }
1110
- function buildCookieParameters(operation, ctx, parameters) {
1111
- if (operation.cookieObjectParamIndex === null) return;
1112
- const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
1113
- if (!cookieParam) return;
1114
- const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
1115
- if (!cookieSchema.properties) return;
1116
- const cookieObjProps = cookieSchema.properties;
1117
- for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
1118
- const isRequired = cookieSchema.required?.includes(propName) ?? false;
1119
- parameters.push({
1120
- name: propName,
1121
- in: "cookie",
1122
- required: isRequired,
1123
- schema: propSchema,
1124
- style: "form",
1125
- explode: true
1126
- });
1127
- }
1128
- }
1129
1639
 
1130
1640
  // src/compiler/manifest/emit.ts
1131
- var import_typescript7 = __toESM(require("typescript"), 1);
1641
+ var import_typescript10 = __toESM(require("typescript"), 1);
1132
1642
  function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
1133
1643
  const components = /* @__PURE__ */ new Map();
1134
1644
  const ctx = {
1135
1645
  checker,
1136
1646
  components,
1137
1647
  typeStack: /* @__PURE__ */ new Set(),
1138
- typeNameStack: []
1648
+ typeNameStack: [],
1649
+ mode: "request"
1139
1650
  };
1140
1651
  const controllerEntries = controllers.map((ctrl) => ({
1141
1652
  controllerId: ctrl.className,
@@ -1149,7 +1660,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
1149
1660
  generator: {
1150
1661
  name: "adorn-api",
1151
1662
  version,
1152
- typescript: import_typescript7.default.version
1663
+ typescript: import_typescript10.default.version
1153
1664
  },
1154
1665
  schemas: {
1155
1666
  kind: "openapi-3.1",
@@ -1160,14 +1671,70 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
1160
1671
  controllers: controllerEntries
1161
1672
  };
1162
1673
  }
1674
+ function resolveSchemaRef2(schema, components) {
1675
+ const ref = schema.$ref;
1676
+ if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
1677
+ return schema;
1678
+ }
1679
+ const name = ref.replace("#/components/schemas/", "");
1680
+ const next = components.get(name);
1681
+ if (!next) return schema;
1682
+ return resolveSchemaRef2(next, components);
1683
+ }
1684
+ function resolveAndCollectObjectProps2(schema, components) {
1685
+ const resolved = resolveSchemaRef2(schema, components);
1686
+ const properties = {};
1687
+ const required = [];
1688
+ const processSchema = (s) => {
1689
+ const current = resolveSchemaRef2(s, components);
1690
+ if (current.properties) {
1691
+ for (const [key, val] of Object.entries(current.properties)) {
1692
+ if (!properties[key]) {
1693
+ properties[key] = val;
1694
+ }
1695
+ }
1696
+ }
1697
+ if (current.required) {
1698
+ for (const req of current.required) {
1699
+ if (!required.includes(req)) {
1700
+ required.push(req);
1701
+ }
1702
+ }
1703
+ }
1704
+ if (current.allOf) {
1705
+ for (const branch of current.allOf) {
1706
+ processSchema(branch);
1707
+ }
1708
+ }
1709
+ };
1710
+ processSchema(resolved);
1711
+ return { properties, required };
1712
+ }
1713
+ function isObjectLikeSchema2(schema, components) {
1714
+ const resolved = resolveSchemaRef2(schema, components);
1715
+ if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1716
+ return true;
1717
+ }
1718
+ if (resolved.allOf) {
1719
+ for (const branch of resolved.allOf) {
1720
+ if (isObjectLikeSchema2(branch, components)) {
1721
+ return true;
1722
+ }
1723
+ }
1724
+ }
1725
+ if (resolved.type === "array" && resolved.items) {
1726
+ const itemsSchema = resolveSchemaRef2(resolved.items, components);
1727
+ return isObjectLikeSchema2(itemsSchema, components);
1728
+ }
1729
+ return false;
1730
+ }
1163
1731
  function buildOperationEntry(op, ctx) {
1164
1732
  const args = {
1165
1733
  body: null,
1166
1734
  path: [],
1167
1735
  query: [],
1168
1736
  headers: [],
1169
- cookies: [],
1170
- paginationParamIndex: op.paginationParamIndex
1737
+ cookies: []
1171
1738
  };
1172
1739
  buildPathArgs(op, ctx, args);
1173
1740
  buildQueryArgs(op, ctx, args);
@@ -1234,53 +1801,39 @@ function buildPathArgs(op, ctx, args) {
1234
1801
  function buildQueryArgs(op, ctx, args) {
1235
1802
  if (op.queryObjectParamIndex !== null) {
1236
1803
  const queryParam = op.parameters[op.queryObjectParamIndex];
1237
- if (queryParam) {
1238
- const queryStyle = extractQueryStyleOptions(ctx.checker, op.methodDeclaration);
1239
- const querySchema = typeToJsonSchema(queryParam.type, ctx);
1240
- if (queryStyle?.style === "deepObject") {
1241
- const schemaRef = querySchema.$ref ?? "#/components/schemas/InlineQueryParam";
1242
- args.query.push({
1243
- name: queryParam.name,
1244
- index: queryParam.index,
1245
- required: !queryParam.isOptional,
1246
- schemaRef,
1247
- schemaType: querySchema.type,
1248
- serialization: {
1249
- style: "deepObject",
1250
- explode: queryStyle.explode ?? true,
1251
- allowReserved: queryStyle.allowReserved
1252
- }
1253
- });
1254
- } else {
1255
- if (!querySchema.properties) return;
1256
- for (const [propName, propSchema] of Object.entries(querySchema.properties)) {
1257
- const isRequired = querySchema.required?.includes(propName) ?? false;
1258
- let schemaRef = propSchema.$ref;
1259
- if (!schemaRef) {
1260
- schemaRef = "#/components/schemas/InlineQueryParam";
1261
- }
1262
- args.query.push({
1263
- name: propName,
1264
- index: queryParam.index,
1265
- required: !isRequired,
1266
- schemaRef,
1267
- schemaType: propSchema.type
1268
- });
1269
- }
1804
+ if (!queryParam) return;
1805
+ const querySchema = typeToJsonSchema(queryParam.type, ctx);
1806
+ const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps2(querySchema, ctx.components);
1807
+ for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1808
+ const isRequired = queryRequired.includes(propName) ?? false;
1809
+ const isObjectLike = isObjectLikeSchema2(propSchema, ctx.components);
1810
+ let schemaRef = propSchema.$ref;
1811
+ if (!schemaRef) {
1812
+ schemaRef = "#/components/schemas/InlineQueryParam";
1270
1813
  }
1814
+ args.query.push({
1815
+ name: propName,
1816
+ index: queryParam.index,
1817
+ required: isRequired,
1818
+ schemaRef,
1819
+ schemaType: propSchema.type,
1820
+ content: isObjectLike ? "application/json" : void 0
1821
+ });
1271
1822
  }
1272
1823
  }
1273
1824
  for (const paramIndex of op.queryParamIndices) {
1274
1825
  const param = op.parameters[paramIndex];
1275
1826
  if (param) {
1276
1827
  const paramSchema = typeToJsonSchema(param.type, ctx);
1828
+ const isObjectLike = isObjectLikeSchema2(paramSchema, ctx.components);
1277
1829
  const schemaRef = paramSchema.$ref ?? "#/components/schemas/InlineQueryParam";
1278
1830
  args.query.push({
1279
1831
  name: param.name,
1280
1832
  index: param.index,
1281
1833
  required: !param.isOptional,
1282
1834
  schemaRef,
1283
- schemaType: paramSchema.type
1835
+ schemaType: paramSchema.type,
1836
+ content: isObjectLike ? "application/json" : void 0
1284
1837
  });
1285
1838
  }
1286
1839
  }
@@ -1300,7 +1853,7 @@ function buildHeaderArgs(op, ctx, args) {
1300
1853
  args.headers.push({
1301
1854
  name: propName,
1302
1855
  index: headerParam.index,
1303
- required: !isRequired,
1856
+ required: isRequired,
1304
1857
  schemaRef,
1305
1858
  schemaType: propSchema.type
1306
1859
  });
@@ -1321,7 +1874,7 @@ function buildCookieArgs(op, ctx, args) {
1321
1874
  args.cookies.push({
1322
1875
  name: propName,
1323
1876
  index: cookieParam.index,
1324
- required: !isRequired,
1877
+ required: isRequired,
1325
1878
  schemaRef,
1326
1879
  schemaType: propSchema.type,
1327
1880
  serialization: { style: "form", explode: true }
@@ -1337,7 +1890,7 @@ var import_ajv = __toESM(require("ajv"), 1);
1337
1890
  var import_ajv_formats = __toESM(require("ajv-formats"), 1);
1338
1891
  var OAS_SCHEMA_ONLY = /* @__PURE__ */ new Set(["discriminator", "xml", "externalDocs", "example"]);
1339
1892
  function sanitizeSchemaForAjv(schema) {
1340
- if (schema == null || typeof schema !== "object") return schema;
1893
+ if (schema === null || typeof schema !== "object") return schema;
1341
1894
  if (Array.isArray(schema)) return schema.map(sanitizeSchemaForAjv);
1342
1895
  const out = {};
1343
1896
  for (const [k, v] of Object.entries(schema)) {
@@ -1348,7 +1901,7 @@ function sanitizeSchemaForAjv(schema) {
1348
1901
  return out;
1349
1902
  }
1350
1903
  function rewriteComponentRefs(schema) {
1351
- if (schema == null || typeof schema !== "object") return schema;
1904
+ if (schema === null || typeof schema !== "object") return schema;
1352
1905
  if (Array.isArray(schema)) return schema.map(rewriteComponentRefs);
1353
1906
  if (typeof schema.$ref === "string") {
1354
1907
  const ref = schema.$ref;
@@ -1425,16 +1978,13 @@ async function emitPrecompiledValidators(opts) {
1425
1978
  `;
1426
1979
  cjs += ` body: ${v.body ? `exports[${JSON.stringify(v.body)}]` : "undefined"},
1427
1980
  `;
1428
- cjs += ` response: {
1429
- `;
1981
+ cjs += " response: {\n";
1430
1982
  for (const [key, id] of Object.entries(v.response)) {
1431
1983
  cjs += ` ${JSON.stringify(key)}: exports[${JSON.stringify(id)}],
1432
1984
  `;
1433
1985
  }
1434
- cjs += ` }
1435
- `;
1436
- cjs += ` },
1437
- `;
1986
+ cjs += " }\n";
1987
+ cjs += " },\n";
1438
1988
  }
1439
1989
  cjs += "};\n";
1440
1990
  import_node_fs2.default.writeFileSync(cjsPath, cjs, "utf8");
@@ -1469,7 +2019,6 @@ export function validateResponse(operationId, status, contentType, data) {
1469
2019
  // src/compiler/cache/isStale.ts
1470
2020
  var import_node_fs3 = __toESM(require("fs"), 1);
1471
2021
  var import_node_path3 = __toESM(require("path"), 1);
1472
- var import_typescript8 = require("typescript");
1473
2022
  var import_meta = {};
1474
2023
  function readJson(p) {
1475
2024
  try {
@@ -1527,7 +2076,7 @@ function findLockfile(startDir) {
1527
2076
  for (const n of names) {
1528
2077
  const p = import_node_path3.default.join(dir, n);
1529
2078
  const mt = statMtimeMs(p);
1530
- if (mt != null) return { path: p, mtimeMs: mt };
2079
+ if (mt !== null) return { path: p, mtimeMs: mt };
1531
2080
  }
1532
2081
  const parent = import_node_path3.default.dirname(dir);
1533
2082
  if (parent === dir) break;
@@ -1552,22 +2101,22 @@ async function isStale(params) {
1552
2101
  const chain = collectTsconfigChain(tsconfigAbs);
1553
2102
  for (const cfg of chain) {
1554
2103
  const mt = statMtimeMs(cfg);
1555
- if (mt == null) return { stale: true, reason: "config-missing", detail: cfg };
2104
+ if (mt === null) return { stale: true, reason: "config-missing", detail: cfg };
1556
2105
  const cachedMt = cache.project.configFiles[cfg];
1557
- if (cachedMt == null || Math.abs(cachedMt - mt) > 1e-4) {
2106
+ if (cachedMt === null || Math.abs(cachedMt - mt) > 1e-4) {
1558
2107
  return { stale: true, reason: "config-updated", detail: cfg };
1559
2108
  }
1560
2109
  }
1561
2110
  if (cache.project.lockfile?.path) {
1562
2111
  const mt = statMtimeMs(cache.project.lockfile.path);
1563
- if (mt == null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
2112
+ if (mt === null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
1564
2113
  if (Math.abs(cache.project.lockfile.mtimeMs - mt) > 1e-4) {
1565
2114
  return { stale: true, reason: "lockfile-updated", detail: cache.project.lockfile.path };
1566
2115
  }
1567
2116
  }
1568
2117
  for (const [file, cachedMt] of Object.entries(cache.inputs)) {
1569
2118
  const mt = statMtimeMs(file);
1570
- if (mt == null) return { stale: true, reason: "input-missing", detail: file };
2119
+ if (mt === null) return { stale: true, reason: "input-missing", detail: file };
1571
2120
  if (Math.abs(cachedMt - mt) > 1e-4) return { stale: true, reason: "input-updated", detail: file };
1572
2121
  }
1573
2122
  return { stale: false, reason: "up-to-date" };
@@ -1576,7 +2125,7 @@ async function isStale(params) {
1576
2125
  // src/compiler/cache/writeCache.ts
1577
2126
  var import_node_fs4 = __toESM(require("fs"), 1);
1578
2127
  var import_node_path4 = __toESM(require("path"), 1);
1579
- var import_typescript9 = __toESM(require("typescript"), 1);
2128
+ var import_typescript11 = __toESM(require("typescript"), 1);
1580
2129
  function statMtimeMs2(p) {
1581
2130
  return import_node_fs4.default.statSync(p).mtimeMs;
1582
2131
  }
@@ -1609,7 +2158,7 @@ function writeCache(params) {
1609
2158
  generator: {
1610
2159
  name: "adorn-api",
1611
2160
  version: params.adornVersion,
1612
- typescript: import_typescript9.default.version
2161
+ typescript: import_typescript11.default.version
1613
2162
  },
1614
2163
  project: {
1615
2164
  tsconfigPath: params.tsconfigAbs,
@@ -1622,7 +2171,7 @@ function writeCache(params) {
1622
2171
  }
1623
2172
 
1624
2173
  // src/cli.ts
1625
- var import_typescript10 = __toESM(require("typescript"), 1);
2174
+ var import_typescript12 = __toESM(require("typescript"), 1);
1626
2175
  var import_node_process = __toESM(require("process"), 1);
1627
2176
  var ADORN_VERSION = "0.1.0";
1628
2177
  function log(msg) {
@@ -1633,6 +2182,26 @@ function debug(...args) {
1633
2182
  console.error("[adorn-api]", ...args);
1634
2183
  }
1635
2184
  }
2185
+ function sanitizeForJson(obj) {
2186
+ if (obj === null || obj === void 0) return obj;
2187
+ if (typeof obj !== "object") return obj;
2188
+ if (Array.isArray(obj)) {
2189
+ return obj.map((item) => sanitizeForJson(item));
2190
+ }
2191
+ const result = {};
2192
+ for (const [key, value] of Object.entries(obj)) {
2193
+ if (key.startsWith("__@") || key.startsWith("[")) continue;
2194
+ if (typeof value === "function") continue;
2195
+ if (value !== null && typeof value === "object") {
2196
+ const typeName = value.constructor?.name;
2197
+ if (typeName && !["Object", "Array", "String", "Number", "Boolean", "Date", "RegExp"].includes(typeName)) {
2198
+ continue;
2199
+ }
2200
+ }
2201
+ result[key] = sanitizeForJson(value);
2202
+ }
2203
+ return result;
2204
+ }
1636
2205
  async function buildCommand(args) {
1637
2206
  const projectIndex = args.indexOf("-p");
1638
2207
  const projectPath = projectIndex !== -1 ? args[projectIndex + 1] : "./tsconfig.json";
@@ -1650,7 +2219,7 @@ async function buildCommand(args) {
1650
2219
  outDir: outputDir,
1651
2220
  project: projectPath,
1652
2221
  adornVersion: ADORN_VERSION,
1653
- typescriptVersion: import_typescript10.default.version
2222
+ typescriptVersion: import_typescript12.default.version
1654
2223
  });
1655
2224
  if (!stale.stale) {
1656
2225
  log("adorn-api: artifacts up-to-date");
@@ -1671,7 +2240,7 @@ async function buildCommand(args) {
1671
2240
  const openapi = generateOpenAPI(controllers, checker, { title: "API", version: "1.0.0" });
1672
2241
  const manifest = generateManifest(controllers, checker, ADORN_VERSION, validationMode);
1673
2242
  (0, import_node_fs5.mkdirSync)(outputPath, { recursive: true });
1674
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.resolve)(outputPath, "openapi.json"), JSON.stringify(openapi, null, 2));
2243
+ (0, import_node_fs5.writeFileSync)((0, import_node_path5.resolve)(outputPath, "openapi.json"), JSON.stringify(sanitizeForJson(openapi), null, 2));
1675
2244
  (0, import_node_fs5.writeFileSync)((0, import_node_path5.resolve)(outputPath, "manifest.json"), JSON.stringify(manifest, null, 2));
1676
2245
  if (validationMode === "precompiled") {
1677
2246
  log("Generating precompiled validators...");