adorn-api 1.0.12 → 1.0.14

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 (171) hide show
  1. package/dist/adapter/express/auth.d.ts +8 -0
  2. package/dist/adapter/express/auth.d.ts.map +1 -1
  3. package/dist/adapter/express/bootstrap.d.ts +12 -0
  4. package/dist/adapter/express/bootstrap.d.ts.map +1 -1
  5. package/dist/adapter/express/coercion.d.ts +81 -1
  6. package/dist/adapter/express/coercion.d.ts.map +1 -1
  7. package/dist/adapter/express/index.d.ts +1 -0
  8. package/dist/adapter/express/index.d.ts.map +1 -1
  9. package/dist/adapter/express/merge.d.ts +17 -0
  10. package/dist/adapter/express/merge.d.ts.map +1 -1
  11. package/dist/adapter/express/openapi.d.ts +55 -0
  12. package/dist/adapter/express/openapi.d.ts.map +1 -1
  13. package/dist/adapter/express/router.d.ts +6 -0
  14. package/dist/adapter/express/router.d.ts.map +1 -1
  15. package/dist/adapter/express/swagger.d.ts +6 -0
  16. package/dist/adapter/express/swagger.d.ts.map +1 -1
  17. package/dist/adapter/express/types.d.ts +26 -0
  18. package/dist/adapter/express/types.d.ts.map +1 -1
  19. package/dist/adapter/express/validation.d.ts +19 -2
  20. package/dist/adapter/express/validation.d.ts.map +1 -1
  21. package/dist/cli.cjs +1016 -445
  22. package/dist/cli.cjs.map +1 -1
  23. package/dist/cli.js +1016 -445
  24. package/dist/cli.js.map +1 -1
  25. package/dist/compiler/analyze/index.d.ts +5 -0
  26. package/dist/compiler/analyze/index.d.ts.map +1 -0
  27. package/dist/compiler/analyze/scanControllers.d.ts +52 -0
  28. package/dist/compiler/analyze/scanControllers.d.ts.map +1 -1
  29. package/dist/compiler/cache/isStale.d.ts +26 -0
  30. package/dist/compiler/cache/isStale.d.ts.map +1 -1
  31. package/dist/compiler/cache/loadArtifacts.d.ts +36 -0
  32. package/dist/compiler/cache/loadArtifacts.d.ts.map +1 -1
  33. package/dist/compiler/cache/schema.d.ts +14 -0
  34. package/dist/compiler/cache/schema.d.ts.map +1 -1
  35. package/dist/compiler/cache/writeCache.d.ts +6 -0
  36. package/dist/compiler/cache/writeCache.d.ts.map +1 -1
  37. package/dist/compiler/gems.d.ts +75 -0
  38. package/dist/compiler/gems.d.ts.map +1 -0
  39. package/dist/compiler/generator/index.d.ts +7 -0
  40. package/dist/compiler/generator/index.d.ts.map +1 -0
  41. package/dist/compiler/generator/manifest.d.ts +23 -0
  42. package/dist/compiler/generator/manifest.d.ts.map +1 -0
  43. package/dist/compiler/generator/openapi.d.ts +118 -0
  44. package/dist/compiler/generator/openapi.d.ts.map +1 -0
  45. package/dist/compiler/graph/builder.d.ts +24 -0
  46. package/dist/compiler/graph/builder.d.ts.map +1 -0
  47. package/dist/compiler/graph/index.d.ts +7 -0
  48. package/dist/compiler/graph/index.d.ts.map +1 -0
  49. package/dist/compiler/graph/schemaGraph.d.ts +67 -0
  50. package/dist/compiler/graph/schemaGraph.d.ts.map +1 -0
  51. package/dist/compiler/graph/types.d.ts +203 -0
  52. package/dist/compiler/graph/types.d.ts.map +1 -0
  53. package/dist/compiler/index.d.ts +12 -0
  54. package/dist/compiler/index.d.ts.map +1 -0
  55. package/dist/compiler/ir/index.d.ts +7 -0
  56. package/dist/compiler/ir/index.d.ts.map +1 -0
  57. package/dist/compiler/ir/pipeline.d.ts +82 -0
  58. package/dist/compiler/ir/pipeline.d.ts.map +1 -0
  59. package/dist/compiler/ir/stages.d.ts +40 -0
  60. package/dist/compiler/ir/stages.d.ts.map +1 -0
  61. package/dist/compiler/ir/visitor.d.ts +98 -0
  62. package/dist/compiler/ir/visitor.d.ts.map +1 -0
  63. package/dist/compiler/manifest/emit.d.ts +14 -0
  64. package/dist/compiler/manifest/emit.d.ts.map +1 -1
  65. package/dist/compiler/manifest/format.d.ts +42 -0
  66. package/dist/compiler/manifest/format.d.ts.map +1 -1
  67. package/dist/compiler/manifest/index.d.ts +6 -0
  68. package/dist/compiler/manifest/index.d.ts.map +1 -0
  69. package/dist/compiler/runner/createProgram.d.ts +16 -0
  70. package/dist/compiler/runner/createProgram.d.ts.map +1 -1
  71. package/dist/compiler/runner/index.d.ts +5 -0
  72. package/dist/compiler/runner/index.d.ts.map +1 -0
  73. package/dist/compiler/schema/extractAnnotations.d.ts +47 -0
  74. package/dist/compiler/schema/extractAnnotations.d.ts.map +1 -1
  75. package/dist/compiler/schema/index.d.ts +6 -0
  76. package/dist/compiler/schema/index.d.ts.map +1 -0
  77. package/dist/compiler/schema/intersectionHandler.d.ts +44 -0
  78. package/dist/compiler/schema/intersectionHandler.d.ts.map +1 -0
  79. package/dist/compiler/schema/objectHandler.d.ts +106 -0
  80. package/dist/compiler/schema/objectHandler.d.ts.map +1 -0
  81. package/dist/compiler/schema/openapi.d.ts +16 -1
  82. package/dist/compiler/schema/openapi.d.ts.map +1 -1
  83. package/dist/compiler/schema/parameters.d.ts +90 -0
  84. package/dist/compiler/schema/parameters.d.ts.map +1 -0
  85. package/dist/compiler/schema/primitives.d.ts +68 -0
  86. package/dist/compiler/schema/primitives.d.ts.map +1 -0
  87. package/dist/compiler/schema/typeToJsonSchema.d.ts +22 -51
  88. package/dist/compiler/schema/typeToJsonSchema.d.ts.map +1 -1
  89. package/dist/compiler/schema/types.d.ts +69 -0
  90. package/dist/compiler/schema/types.d.ts.map +1 -0
  91. package/dist/compiler/schema/unionHandler.d.ts +70 -0
  92. package/dist/compiler/schema/unionHandler.d.ts.map +1 -0
  93. package/dist/compiler/transform/dedup.d.ts +35 -0
  94. package/dist/compiler/transform/dedup.d.ts.map +1 -0
  95. package/dist/compiler/transform/flatten.d.ts +50 -0
  96. package/dist/compiler/transform/flatten.d.ts.map +1 -0
  97. package/dist/compiler/transform/index.d.ts +7 -0
  98. package/dist/compiler/transform/index.d.ts.map +1 -0
  99. package/dist/compiler/transform/inline.d.ts +46 -0
  100. package/dist/compiler/transform/inline.d.ts.map +1 -0
  101. package/dist/compiler/validation/emitPrecompiledValidators.d.ts +16 -0
  102. package/dist/compiler/validation/emitPrecompiledValidators.d.ts.map +1 -1
  103. package/dist/compiler/validation/index.d.ts +5 -0
  104. package/dist/compiler/validation/index.d.ts.map +1 -0
  105. package/dist/decorators/Auth.d.ts +17 -0
  106. package/dist/decorators/Auth.d.ts.map +1 -1
  107. package/dist/decorators/Controller.d.ts +15 -0
  108. package/dist/decorators/Controller.d.ts.map +1 -1
  109. package/dist/decorators/Public.d.ts +13 -0
  110. package/dist/decorators/Public.d.ts.map +1 -1
  111. package/dist/decorators/Use.d.ts +18 -0
  112. package/dist/decorators/Use.d.ts.map +1 -1
  113. package/dist/decorators/methods.d.ts +20 -0
  114. package/dist/decorators/methods.d.ts.map +1 -1
  115. package/dist/express.cjs +73 -54
  116. package/dist/express.cjs.map +1 -1
  117. package/dist/express.js +73 -54
  118. package/dist/express.js.map +1 -1
  119. package/dist/http.d.ts +1 -2
  120. package/dist/http.d.ts.map +1 -1
  121. package/dist/index.cjs +161 -4
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.d.ts +2 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +156 -3
  126. package/dist/index.js.map +1 -1
  127. package/dist/metal/applyListQuery.d.ts +73 -0
  128. package/dist/metal/applyListQuery.d.ts.map +1 -1
  129. package/dist/metal/index.cjs +2 -2
  130. package/dist/metal/index.cjs.map +1 -1
  131. package/dist/metal/index.d.ts +4 -0
  132. package/dist/metal/index.d.ts.map +1 -1
  133. package/dist/metal/index.js +2 -2
  134. package/dist/metal/index.js.map +1 -1
  135. package/dist/metal/listQuery.d.ts +19 -0
  136. package/dist/metal/listQuery.d.ts.map +1 -1
  137. package/dist/metal/queryOptions.d.ts +8 -0
  138. package/dist/metal/queryOptions.d.ts.map +1 -1
  139. package/dist/metal/readMetalBag.d.ts +36 -0
  140. package/dist/metal/readMetalBag.d.ts.map +1 -1
  141. package/dist/metal/registerMetalEntities.d.ts +20 -0
  142. package/dist/metal/registerMetalEntities.d.ts.map +1 -1
  143. package/dist/metal/schemaFromEntity.d.ts +30 -0
  144. package/dist/metal/schemaFromEntity.d.ts.map +1 -1
  145. package/dist/metal/searchWhere.d.ts +39 -0
  146. package/dist/metal/searchWhere.d.ts.map +1 -1
  147. package/dist/metal/symbolMetadata.d.ts +6 -0
  148. package/dist/metal/symbolMetadata.d.ts.map +1 -1
  149. package/dist/runtime/auth/runtime.d.ts +155 -6
  150. package/dist/runtime/auth/runtime.d.ts.map +1 -1
  151. package/dist/runtime/metadata/bucket.d.ts +1 -2
  152. package/dist/runtime/metadata/bucket.d.ts.map +1 -1
  153. package/dist/runtime/metadata/key.d.ts +1 -1
  154. package/dist/runtime/metadata/key.d.ts.map +1 -1
  155. package/dist/runtime/metadata/read.d.ts +1 -2
  156. package/dist/runtime/metadata/read.d.ts.map +1 -1
  157. package/dist/runtime/metadata/types.d.ts +74 -0
  158. package/dist/runtime/metadata/types.d.ts.map +1 -1
  159. package/dist/runtime/polyfill.d.ts +1 -1
  160. package/dist/runtime/polyfill.d.ts.map +1 -1
  161. package/dist/runtime/upload.d.ts +37 -0
  162. package/dist/runtime/upload.d.ts.map +1 -1
  163. package/dist/runtime/validation/ajv.d.ts +100 -0
  164. package/dist/runtime/validation/ajv.d.ts.map +1 -1
  165. package/dist/runtime/validation/index.d.ts +9 -0
  166. package/dist/runtime/validation/index.d.ts.map +1 -1
  167. package/dist/scripts/adorn-example.cjs +238 -6
  168. package/dist/scripts/adorn-example.cjs.map +1 -1
  169. package/dist/utils/port.d.ts +9 -0
  170. package/dist/utils/port.d.ts.map +1 -0
  171. package/package.json +4 -1
package/dist/cli.js CHANGED
@@ -86,7 +86,7 @@ function analyzeClass(node, sourceFile, checker) {
86
86
  produces
87
87
  };
88
88
  }
89
- function extractClassConsumes(node, checker) {
89
+ function extractClassConsumes(node, _checker) {
90
90
  const decorator = findDecorator(node, "Consumes");
91
91
  if (!decorator) return void 0;
92
92
  const callExpr = decorator.expression;
@@ -102,7 +102,7 @@ function extractClassConsumes(node, checker) {
102
102
  }
103
103
  return void 0;
104
104
  }
105
- function extractClassProduces(node, checker) {
105
+ function extractClassProduces(node, _checker) {
106
106
  const decorator = findDecorator(node, "Produces");
107
107
  if (!decorator) return void 0;
108
108
  const callExpr = decorator.expression;
@@ -284,7 +284,7 @@ function extractDecoratorStringArg(decorator) {
284
284
  }
285
285
  return null;
286
286
  }
287
- function unwrapPromise(type, checker) {
287
+ function unwrapPromise(type, _checker) {
288
288
  const symbol = type.getSymbol();
289
289
  if (symbol?.getName() === "Promise") {
290
290
  const typeArgs = type.typeArguments;
@@ -305,12 +305,15 @@ function unwrapPromiseTypeNode(typeNode) {
305
305
  }
306
306
 
307
307
  // src/compiler/schema/openapi.ts
308
- import ts5 from "typescript";
308
+ import ts9 from "typescript";
309
309
 
310
310
  // src/compiler/schema/typeToJsonSchema.ts
311
+ import ts7 from "typescript";
312
+
313
+ // src/compiler/schema/primitives.ts
311
314
  import ts3 from "typescript";
312
- function typeToJsonSchema(type, ctx, typeNode) {
313
- const { checker } = ctx;
315
+ function handlePrimitiveType(type, ctx, typeNode) {
316
+ const { checker, propertyName } = ctx;
314
317
  if (type.flags & ts3.TypeFlags.Undefined) {
315
318
  return {};
316
319
  }
@@ -324,7 +327,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
324
327
  return { type: "string" };
325
328
  }
326
329
  if (type.flags & ts3.TypeFlags.Number) {
327
- return normalizeNumericType(type, checker, typeNode);
330
+ return normalizeNumericType(type, checker, typeNode, propertyName);
328
331
  }
329
332
  if (type.flags & ts3.TypeFlags.Boolean) {
330
333
  return { type: "boolean" };
@@ -348,26 +351,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
348
351
  const intrinsic = type.intrinsicName;
349
352
  return { type: "boolean", enum: [intrinsic === "true"] };
350
353
  }
351
- if (type.isUnion()) {
352
- return handleUnion(type, ctx, typeNode);
353
- }
354
- if (type.isIntersection()) {
355
- return handleIntersection(type, ctx, typeNode);
356
- }
357
- if (checker.isArrayType(type)) {
358
- const typeArgs = type.typeArguments;
359
- const itemType = typeArgs?.[0];
360
- const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
361
- return {
362
- type: "array",
363
- items,
364
- uniqueItems: isSetType(type, checker) ? true : void 0
365
- };
366
- }
367
- if (type.flags & ts3.TypeFlags.Object) {
368
- return handleObjectType(type, ctx, typeNode);
369
- }
370
- return {};
354
+ return null;
371
355
  }
372
356
  function isDateType(type, checker) {
373
357
  const symbol = type.getSymbol();
@@ -382,53 +366,51 @@ function isDateType(type, checker) {
382
366
  }
383
367
  return symbol?.getName() === "Date";
384
368
  }
385
- function isSetType(type, checker) {
386
- const symbol = type.getSymbol();
387
- if (!symbol) return false;
388
- const name = symbol.getName();
389
- if (name === "Set") return true;
390
- return false;
391
- }
392
- function getSchemaName(type, typeNode) {
393
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
394
- const aliasName = aliasSymbol?.getName();
395
- if (aliasName && aliasName !== "__type") {
396
- return aliasName;
369
+ function normalizeNumericType(type, checker, typeNode, propertyName) {
370
+ const typeName = getExplicitTypeNameFromNode(typeNode) ?? null;
371
+ const symbol = getEffectiveSymbol(type, checker);
372
+ const symbolName = symbol?.getName() ?? null;
373
+ if (shouldBeIntegerType(typeName) || shouldBeIntegerType(symbolName) || shouldBeIntegerType(propertyName ?? null)) {
374
+ return { type: "integer" };
397
375
  }
398
- const symbol = type.getSymbol();
399
- const symbolName = symbol?.getName?.();
400
- if (symbolName && symbolName !== "__type") {
401
- return symbolName;
376
+ return { type: "number" };
377
+ }
378
+ function shouldBeIntegerType(typeName) {
379
+ if (!typeName) return false;
380
+ const lower = typeName.toLowerCase();
381
+ return lower === "id" || lower.endsWith("id") || lower === "primarykey" || lower === "pk" || lower === "page" || lower === "pagesize" || lower === "totalitems" || lower === "limit" || lower === "offset";
382
+ }
383
+ function getExplicitTypeNameFromNode(typeNode) {
384
+ if (!typeNode) return null;
385
+ if (ts3.isTypeReferenceNode(typeNode)) {
386
+ if (ts3.isIdentifier(typeNode.typeName)) {
387
+ return typeNode.typeName.text;
388
+ }
402
389
  }
403
- const nodeName = getExplicitTypeNameFromNode(typeNode);
404
- if (nodeName && nodeName !== "__type") {
405
- return nodeName;
390
+ if (ts3.isTypeAliasDeclaration(typeNode.parent)) {
391
+ if (ts3.isIdentifier(typeNode.parent.name)) {
392
+ return typeNode.parent.name.text;
393
+ }
406
394
  }
407
395
  return null;
408
396
  }
409
- function buildNamedSchema(type, ctx, typeNode, build) {
410
- const name = getSchemaName(type, typeNode);
411
- if (!name) {
412
- return build();
413
- }
414
- const { components, typeStack } = ctx;
415
- if (components.has(name) || typeStack.has(type)) {
416
- return { $ref: `#/components/schemas/${name}` };
417
- }
418
- typeStack.add(type);
419
- const schema = build();
420
- typeStack.delete(type);
421
- if (!components.has(name)) {
422
- components.set(name, schema);
397
+ function getEffectiveSymbol(type, checker) {
398
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
399
+ if (aliasSymbol && aliasSymbol.flags & ts3.SymbolFlags.Alias) {
400
+ return checker.getAliasedSymbol(aliasSymbol);
423
401
  }
424
- return { $ref: `#/components/schemas/${name}` };
402
+ return type.getSymbol() ?? null;
425
403
  }
404
+
405
+ // src/compiler/schema/unionHandler.ts
406
+ import ts4 from "typescript";
426
407
  function handleUnion(type, ctx, typeNode) {
427
408
  return buildNamedSchema(type, ctx, typeNode, () => {
428
409
  const types = type.types;
429
- const nullType = types.find((t) => t.flags & ts3.TypeFlags.Null);
430
- const otherTypes = types.filter((t) => !(t.flags & ts3.TypeFlags.Null) && !(t.flags & ts3.TypeFlags.Undefined));
431
- const allStringLiterals = otherTypes.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
410
+ const nullType = types.find((t) => t.flags & ts4.TypeFlags.Null);
411
+ const undefinedType = types.find((t) => t.flags & ts4.TypeFlags.Undefined);
412
+ const otherTypes = types.filter((t) => !(t.flags & ts4.TypeFlags.Null) && !(t.flags & ts4.TypeFlags.Undefined));
413
+ const allStringLiterals = otherTypes.every((t) => t.flags & ts4.TypeFlags.StringLiteral);
432
414
  if (allStringLiterals && otherTypes.length > 0) {
433
415
  const enumValues = otherTypes.map((t) => t.value);
434
416
  const schema = { type: "string", enum: enumValues };
@@ -437,6 +419,14 @@ function handleUnion(type, ctx, typeNode) {
437
419
  }
438
420
  return schema;
439
421
  }
422
+ const allBooleanLiterals = otherTypes.length > 0 && otherTypes.every((t) => t.flags & ts4.TypeFlags.BooleanLiteral);
423
+ if (allBooleanLiterals) {
424
+ const schema = { type: "boolean" };
425
+ if (nullType || undefinedType) {
426
+ schema.type = ["boolean", "null"];
427
+ }
428
+ return schema;
429
+ }
440
430
  if (otherTypes.length === 1 && nullType) {
441
431
  const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
442
432
  if (typeof innerSchema.type === "string") {
@@ -466,49 +456,7 @@ function handleUnion(type, ctx, typeNode) {
466
456
  return {};
467
457
  });
468
458
  }
469
- function handleIntersection(type, ctx, typeNode) {
470
- return buildNamedSchema(type, ctx, typeNode, () => {
471
- const types = type.types;
472
- const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
473
- if (brandCollapsed) {
474
- return brandCollapsed;
475
- }
476
- const allOf = [];
477
- for (const t of types) {
478
- allOf.push(typeToJsonSchema(t, ctx));
479
- }
480
- return { allOf };
481
- });
482
- }
483
- function tryCollapseBrandedIntersection(types, ctx, typeNode) {
484
- const { checker } = ctx;
485
- const parts = [...types];
486
- const prim = parts.find(isPrimitiveLike);
487
- if (!prim) return null;
488
- const rest = parts.filter((p) => p !== prim);
489
- if (rest.every((r) => isBrandObject(checker, r, ctx))) {
490
- return typeToJsonSchema(prim, ctx);
491
- }
492
- return null;
493
- }
494
- function isPrimitiveLike(t) {
495
- return (t.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.Boolean | ts3.TypeFlags.BigInt)) !== 0 || (t.flags & ts3.TypeFlags.StringLiteral) !== 0 || (t.flags & ts3.TypeFlags.NumberLiteral) !== 0;
496
- }
497
- function isBrandObject(checker, t, ctx) {
498
- if (!(t.flags & ts3.TypeFlags.Object)) return false;
499
- const props = t.getProperties();
500
- if (props.length === 0) return false;
501
- const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
502
- for (const p of props) {
503
- if (!allowed.has(p.getName())) return false;
504
- }
505
- const callSigs = t.getCallSignatures?.();
506
- if (callSigs && callSigs.length > 0) return false;
507
- const constructSigs = t.getConstructSignatures?.();
508
- if (constructSigs && constructSigs.length > 0) return false;
509
- return true;
510
- }
511
- function detectDiscriminatedUnion(types, ctx, branches) {
459
+ function detectDiscriminatedUnion(types, ctx, _branches) {
512
460
  if (types.length < 2) return null;
513
461
  const candidates = findCommonPropertyNames(ctx.checker, types);
514
462
  for (const propName of candidates) {
@@ -539,10 +487,10 @@ function findCommonPropertyNames(checker, types) {
539
487
  function isRequiredProperty(checker, type, propName) {
540
488
  const sym = checker.getPropertyOfType(type, propName);
541
489
  if (!sym) return false;
542
- if (sym.flags & ts3.SymbolFlags.Optional) return false;
490
+ if (sym.flags & ts4.SymbolFlags.Optional) return false;
543
491
  const propType = checker.getTypeOfSymbol(sym);
544
492
  if (propType.isUnion?.()) {
545
- const hasUndefined = propType.types.some((t) => (t.flags & ts3.TypeFlags.Undefined) !== 0);
493
+ const hasUndefined = propType.types.some((t) => (t.flags & ts4.TypeFlags.Undefined) !== 0);
546
494
  if (hasUndefined) return false;
547
495
  }
548
496
  return true;
@@ -585,16 +533,186 @@ function getBranchSchemaName(type, ctx) {
585
533
  }
586
534
  return `Anonymous_${ctx.typeNameStack.length}`;
587
535
  }
536
+ function buildNamedSchema(type, ctx, typeNode, build) {
537
+ const name = getSchemaName(type, typeNode);
538
+ if (!name) {
539
+ return build();
540
+ }
541
+ const { components, typeStack } = ctx;
542
+ if (components.has(name) || typeStack.has(type)) {
543
+ return { $ref: `#/components/schemas/${name}` };
544
+ }
545
+ typeStack.add(type);
546
+ const schema = build();
547
+ typeStack.delete(type);
548
+ if (!components.has(name)) {
549
+ components.set(name, schema);
550
+ }
551
+ return { $ref: `#/components/schemas/${name}` };
552
+ }
553
+ function getSchemaName(type, typeNode) {
554
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
555
+ const aliasName = aliasSymbol?.getName();
556
+ if (aliasName && aliasName !== "__type") {
557
+ return aliasName;
558
+ }
559
+ const symbol = type.getSymbol();
560
+ const symbolName = symbol?.getName?.();
561
+ if (symbolName && symbolName !== "__type") {
562
+ return symbolName;
563
+ }
564
+ const nodeName = getExplicitTypeNameFromNode2(typeNode);
565
+ if (nodeName && nodeName !== "__type") {
566
+ return nodeName;
567
+ }
568
+ return null;
569
+ }
570
+ function getExplicitTypeNameFromNode2(typeNode) {
571
+ if (!typeNode) return null;
572
+ if (ts4.isTypeReferenceNode(typeNode)) {
573
+ if (ts4.isIdentifier(typeNode.typeName)) {
574
+ return typeNode.typeName.text;
575
+ }
576
+ }
577
+ if (ts4.isTypeAliasDeclaration(typeNode.parent)) {
578
+ if (ts4.isIdentifier(typeNode.parent.name)) {
579
+ return typeNode.parent.name.text;
580
+ }
581
+ }
582
+ return null;
583
+ }
584
+
585
+ // src/compiler/schema/intersectionHandler.ts
586
+ import ts5 from "typescript";
587
+ function handleIntersection(type, ctx, typeNode) {
588
+ return buildNamedSchema2(type, ctx, typeNode, () => {
589
+ const types = type.types;
590
+ const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
591
+ if (brandCollapsed) {
592
+ return brandCollapsed;
593
+ }
594
+ const allOf = [];
595
+ for (const t of types) {
596
+ const schema = typeToJsonSchema(t, ctx);
597
+ if (Object.keys(schema).length > 0) {
598
+ if (isEmptyObjectSchema(schema)) {
599
+ continue;
600
+ }
601
+ allOf.push(schema);
602
+ }
603
+ }
604
+ if (allOf.length === 0) {
605
+ return {};
606
+ }
607
+ if (allOf.length === 1) {
608
+ return allOf[0];
609
+ }
610
+ return { allOf };
611
+ });
612
+ }
613
+ function tryCollapseBrandedIntersection(types, ctx, _typeNode) {
614
+ const { checker } = ctx;
615
+ const parts = [...types];
616
+ const prim = parts.find(isPrimitiveLike);
617
+ if (!prim) return null;
618
+ const rest = parts.filter((p) => p !== prim);
619
+ if (rest.every((r) => isBrandObject(checker, r, ctx))) {
620
+ return typeToJsonSchema(prim, ctx);
621
+ }
622
+ return null;
623
+ }
624
+ function isPrimitiveLike(t) {
625
+ return (t.flags & (ts5.TypeFlags.String | ts5.TypeFlags.Number | ts5.TypeFlags.Boolean | ts5.TypeFlags.BigInt)) !== 0 || (t.flags & ts5.TypeFlags.StringLiteral) !== 0 || (t.flags & ts5.TypeFlags.NumberLiteral) !== 0;
626
+ }
627
+ function isBrandObject(checker, t, _ctx) {
628
+ if (!(t.flags & ts5.TypeFlags.Object)) return false;
629
+ const props = t.getProperties();
630
+ if (props.length === 0) return false;
631
+ const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
632
+ for (const p of props) {
633
+ if (!allowed.has(p.getName())) return false;
634
+ }
635
+ const callSigs = t.getCallSignatures?.();
636
+ if (callSigs && callSigs.length > 0) return false;
637
+ const constructSigs = t.getConstructSignatures?.();
638
+ if (constructSigs && constructSigs.length > 0) return false;
639
+ return true;
640
+ }
641
+ function isEmptyObjectSchema(schema) {
642
+ if (schema.type !== "object") {
643
+ return false;
644
+ }
645
+ if (!schema.properties || Object.keys(schema.properties).length === 0) {
646
+ if (!schema.additionalProperties) {
647
+ return true;
648
+ }
649
+ }
650
+ return false;
651
+ }
652
+ function buildNamedSchema2(type, ctx, typeNode, build) {
653
+ const name = getSchemaName2(type, typeNode);
654
+ if (!name) {
655
+ return build();
656
+ }
657
+ const { components, typeStack } = ctx;
658
+ if (components.has(name) || typeStack.has(type)) {
659
+ return { $ref: `#/components/schemas/${name}` };
660
+ }
661
+ typeStack.add(type);
662
+ const schema = build();
663
+ typeStack.delete(type);
664
+ if (!components.has(name)) {
665
+ components.set(name, schema);
666
+ }
667
+ return { $ref: `#/components/schemas/${name}` };
668
+ }
669
+ function getSchemaName2(type, typeNode) {
670
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
671
+ const aliasName = aliasSymbol?.getName();
672
+ if (aliasName && aliasName !== "__type") {
673
+ return aliasName;
674
+ }
675
+ const symbol = type.getSymbol();
676
+ const symbolName = symbol?.getName?.();
677
+ if (symbolName && symbolName !== "__type") {
678
+ return symbolName;
679
+ }
680
+ const nodeName = getExplicitTypeNameFromNode3(typeNode);
681
+ if (nodeName && nodeName !== "__type") {
682
+ return nodeName;
683
+ }
684
+ return null;
685
+ }
686
+ function getExplicitTypeNameFromNode3(typeNode) {
687
+ if (!typeNode) return null;
688
+ if (ts5.isTypeReferenceNode(typeNode)) {
689
+ if (ts5.isIdentifier(typeNode.typeName)) {
690
+ return typeNode.typeName.text;
691
+ }
692
+ }
693
+ if (ts5.isTypeAliasDeclaration(typeNode.parent)) {
694
+ if (ts5.isIdentifier(typeNode.parent.name)) {
695
+ return typeNode.parent.name.text;
696
+ }
697
+ }
698
+ return null;
699
+ }
700
+
701
+ // src/compiler/schema/objectHandler.ts
702
+ import ts6 from "typescript";
588
703
  function handleObjectType(type, ctx, typeNode) {
589
- const { checker, components, typeStack, mode } = ctx;
704
+ const { checker, components, typeStack } = ctx;
590
705
  const symbol = type.getSymbol();
591
706
  const typeName = symbol?.getName?.() ?? getTypeNameFromNode(typeNode, ctx);
592
707
  if (isMetalOrmWrapperType(type, checker)) {
593
708
  return handleMetalOrmWrapper(type, ctx);
594
709
  }
595
710
  if (typeName && typeName !== "__type") {
596
- if (components.has(typeName)) {
597
- return { $ref: `#/components/schemas/${typeName}` };
711
+ const isMetalOrmGeneric = METAL_ORM_WRAPPER_NAMES.some(
712
+ (name) => typeName === name || typeName.endsWith("Api")
713
+ );
714
+ if (isMetalOrmGeneric) {
715
+ return {};
598
716
  }
599
717
  if (typeStack.has(type)) {
600
718
  return { $ref: `#/components/schemas/${typeName}` };
@@ -607,46 +725,18 @@ function handleObjectType(type, ctx, typeNode) {
607
725
  const existing = components.get(typeName);
608
726
  if (!existing) {
609
727
  components.set(typeName, schema);
728
+ } else {
729
+ const merged = mergeSchemasIfNeeded(existing, schema);
730
+ if (merged !== existing) {
731
+ components.set(typeName, merged);
732
+ }
610
733
  }
611
734
  return { $ref: `#/components/schemas/${typeName}` };
612
735
  }
613
736
  typeStack.delete(type);
614
737
  return schema;
615
738
  }
616
- function getExplicitTypeNameFromNode(typeNode) {
617
- if (!typeNode) return null;
618
- if (ts3.isTypeReferenceNode(typeNode)) {
619
- if (ts3.isIdentifier(typeNode.typeName)) {
620
- return typeNode.typeName.text;
621
- }
622
- }
623
- if (ts3.isTypeAliasDeclaration(typeNode.parent)) {
624
- if (ts3.isIdentifier(typeNode.parent.name)) {
625
- return typeNode.parent.name.text;
626
- }
627
- }
628
- return null;
629
- }
630
- function shouldBeIntegerType(typeName) {
631
- if (!typeName) return false;
632
- const lower = typeName.toLowerCase();
633
- return lower === "id" || lower.endsWith("id") || lower === "primarykey" || lower === "pk";
634
- }
635
- function normalizeNumericType(type, checker, typeNode) {
636
- const typeName = getExplicitTypeNameFromNode(typeNode) ?? null;
637
- const symbol = getEffectiveSymbol(type, checker);
638
- const symbolName = symbol?.getName() ?? null;
639
- if (shouldBeIntegerType(typeName) || shouldBeIntegerType(symbolName)) {
640
- return { type: "integer" };
641
- }
642
- return { type: "number" };
643
- }
644
- function getTypeNameFromNode(typeNode, ctx) {
645
- const explicitName = getExplicitTypeNameFromNode(typeNode);
646
- if (explicitName) return explicitName;
647
- return `Anonymous_${ctx.typeNameStack.length}`;
648
- }
649
- function buildObjectSchema(type, ctx, typeNode) {
739
+ function buildObjectSchema(type, ctx, _typeNode) {
650
740
  const { checker, mode } = ctx;
651
741
  const properties = {};
652
742
  const required = [];
@@ -660,9 +750,10 @@ function buildObjectSchema(type, ctx, typeNode) {
660
750
  if (isMethodLike(propType)) {
661
751
  continue;
662
752
  }
663
- const isOptional = !!(prop.flags & ts3.SymbolFlags.Optional);
753
+ const isOptional = !!(prop.flags & ts6.SymbolFlags.Optional);
664
754
  const isRelation = isMetalOrmWrapperType(propType, checker);
665
- properties[propName] = typeToJsonSchema(propType, ctx);
755
+ const propCtx = { ...ctx, propertyName: propName };
756
+ properties[propName] = typeToJsonSchema(propType, propCtx);
666
757
  const shouldRequire = mode === "response" ? !isRelation && !isOptional : !isOptional;
667
758
  if (shouldRequire) {
668
759
  required.push(propName);
@@ -683,14 +774,14 @@ function buildObjectSchema(type, ctx, typeNode) {
683
774
  }
684
775
  return schema;
685
776
  }
686
- function isRecordType(type, checker) {
777
+ function isRecordType(type, _checker) {
687
778
  const symbol = type.getSymbol();
688
779
  if (!symbol) return false;
689
780
  const name = symbol.getName();
690
781
  if (name === "Record") return true;
691
782
  return false;
692
783
  }
693
- function getRecordValueType(type, checker) {
784
+ function getRecordValueType(type, _checker) {
694
785
  const symbol = type.getSymbol();
695
786
  if (!symbol) return null;
696
787
  const name = symbol.getName();
@@ -703,24 +794,8 @@ function getRecordValueType(type, checker) {
703
794
  }
704
795
  return null;
705
796
  }
706
- var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
707
797
  function isMetalOrmWrapperType(type, checker) {
708
- const aliasSymbol = type.aliasSymbol || type.symbol;
709
- if (!aliasSymbol) return false;
710
- return METAL_ORM_WRAPPER_NAMES.includes(aliasSymbol.getName());
711
- }
712
- function getWrapperTypeName(type, checker) {
713
- const symbol = getEffectiveSymbol(type, checker);
714
- if (!symbol) return null;
715
- const name = symbol.getName();
716
- return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
717
- }
718
- function getEffectiveSymbol(type, checker) {
719
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
720
- if (aliasSymbol && aliasSymbol.flags & ts3.SymbolFlags.Alias) {
721
- return checker.getAliasedSymbol(aliasSymbol);
722
- }
723
- return type.getSymbol() ?? null;
798
+ return !!findMetalOrmWrapper(type, checker);
724
799
  }
725
800
  function isMethodLike(type) {
726
801
  const callSigs = type.getCallSignatures?.();
@@ -729,6 +804,159 @@ function isMethodLike(type) {
729
804
  function isIteratorOrSymbolProperty(propName) {
730
805
  return propName.startsWith("__@") || propName.startsWith("[") || propName === Symbol.iterator.toString();
731
806
  }
807
+ function getTypeNameFromNode(typeNode, _ctx) {
808
+ const explicitName = getExplicitTypeNameFromNode4(typeNode);
809
+ if (explicitName) return explicitName;
810
+ return "Anonymous_${ctx.typeNameStack.length}";
811
+ }
812
+ function getExplicitTypeNameFromNode4(typeNode) {
813
+ if (!typeNode) return null;
814
+ if (ts6.isTypeReferenceNode(typeNode)) {
815
+ if (ts6.isIdentifier(typeNode.typeName)) {
816
+ return typeNode.typeName.text;
817
+ }
818
+ }
819
+ if (ts6.isTypeAliasDeclaration(typeNode.parent)) {
820
+ if (ts6.isIdentifier(typeNode.parent.name)) {
821
+ return typeNode.parent.name.text;
822
+ }
823
+ }
824
+ return null;
825
+ }
826
+ function mergeSchemasIfNeeded(existing, newSchema) {
827
+ if (existing.type === "array" && newSchema.type === "array") {
828
+ return mergeArraySchemas(existing, newSchema);
829
+ }
830
+ const result = { ...existing };
831
+ for (const [key, newValue] of Object.entries(newSchema)) {
832
+ if (key === "properties" && newValue) {
833
+ result.properties = mergePropertiesIfNeeded(existing.properties || {}, newValue);
834
+ } else if (key === "required" && newValue) {
835
+ result.required = mergeRequiredFields(existing.required || [], newValue);
836
+ } else if (!deepEqual(existing[key], newValue)) {
837
+ result[key] = newValue;
838
+ }
839
+ }
840
+ return result;
841
+ }
842
+ function mergePropertiesIfNeeded(existing, newProps) {
843
+ const result = { ...existing };
844
+ for (const [propName, newPropSchema] of Object.entries(newProps)) {
845
+ const existingProp = existing[propName];
846
+ if (!existingProp) {
847
+ result[propName] = newPropSchema;
848
+ } else if (deepEqual(existingProp, newPropSchema)) {
849
+ continue;
850
+ } else {
851
+ result[propName] = mergePropertySchemas(existingProp, newPropSchema);
852
+ }
853
+ }
854
+ return result;
855
+ }
856
+ function mergePropertySchemas(schema1, schema2) {
857
+ if (deepEqual(schema1, schema2)) {
858
+ return schema1;
859
+ }
860
+ if (schema1.type === "array" && schema2.type === "array") {
861
+ return mergeArraySchemas(schema1, schema2);
862
+ }
863
+ const existingOneOf = schema1.oneOf || schema1.anyOf;
864
+ const newOneOf = schema2.oneOf || schema2.anyOf;
865
+ if (existingOneOf) {
866
+ const mergedOneOf = [...existingOneOf];
867
+ if (newOneOf) {
868
+ for (const newItem of newOneOf) {
869
+ if (!mergedOneOf.some((item) => deepEqual(item, newItem))) {
870
+ mergedOneOf.push(newItem);
871
+ }
872
+ }
873
+ } else if (!mergedOneOf.some((item) => deepEqual(item, schema2))) {
874
+ mergedOneOf.push(schema2);
875
+ }
876
+ return { ...schema1, oneOf: mergedOneOf };
877
+ }
878
+ return {
879
+ oneOf: [schema1, schema2]
880
+ };
881
+ }
882
+ function mergeArraySchemas(schema1, schema2) {
883
+ const result = { type: "array" };
884
+ if (schema1.uniqueItems || schema2.uniqueItems) {
885
+ result.uniqueItems = true;
886
+ }
887
+ if (schema1.items && schema2.items) {
888
+ result.items = mergePropertySchemas(schema1.items, schema2.items);
889
+ } else if (schema1.items) {
890
+ result.items = schema1.items;
891
+ } else if (schema2.items) {
892
+ result.items = schema2.items;
893
+ }
894
+ return result;
895
+ }
896
+ function mergeRequiredFields(existing, newFields) {
897
+ const merged = /* @__PURE__ */ new Set([...existing, ...newFields]);
898
+ return Array.from(merged);
899
+ }
900
+ function deepEqual(a, b) {
901
+ if (a === b) return true;
902
+ if (a == null || b == null) return false;
903
+ if (typeof a !== typeof b) return false;
904
+ if (typeof a === "object") {
905
+ const aKeys = Object.keys(a);
906
+ const bKeys = Object.keys(b);
907
+ if (aKeys.length !== bKeys.length) return false;
908
+ for (const key of aKeys) {
909
+ if (!bKeys.includes(key)) return false;
910
+ if (!deepEqual(a[key], b[key])) return false;
911
+ }
912
+ return true;
913
+ }
914
+ return false;
915
+ }
916
+ var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
917
+ function findMetalOrmWrapper(type, checker) {
918
+ if (type.isIntersection()) {
919
+ let wrapperInfo = null;
920
+ let hasReadonlyArray = false;
921
+ for (const constituent of type.types) {
922
+ const result = findWrapperInType(constituent, checker);
923
+ if (result) {
924
+ wrapperInfo = result;
925
+ }
926
+ if (!(constituent.flags & ts6.TypeFlags.Object)) continue;
927
+ const symbol = constituent.getSymbol();
928
+ if (symbol?.getName() === "ReadonlyArray") {
929
+ hasReadonlyArray = true;
930
+ }
931
+ }
932
+ if (wrapperInfo) {
933
+ return { ...wrapperInfo, isReadonlyArray: hasReadonlyArray };
934
+ }
935
+ return null;
936
+ }
937
+ return findWrapperInType(type, checker);
938
+ }
939
+ function findWrapperInType(type, checker) {
940
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
941
+ const symbol = type.getSymbol();
942
+ const effectiveSymbol = aliasSymbol && aliasSymbol.flags & ts6.SymbolFlags.Alias ? checker.getAliasedSymbol(aliasSymbol) : symbol;
943
+ if (!effectiveSymbol) return null;
944
+ const name = effectiveSymbol.getName();
945
+ if (!METAL_ORM_WRAPPER_NAMES.includes(name)) return null;
946
+ const typeRef = type;
947
+ const typeArgs = typeRef.typeArguments || [];
948
+ return {
949
+ wrapperName: name,
950
+ targetTypeArgs: typeArgs,
951
+ isReadonlyArray: false
952
+ };
953
+ }
954
+ function getWrapperTypeName(type, _checker) {
955
+ const symbol = type.getSymbol();
956
+ if (!symbol) return null;
957
+ const name = symbol.getName();
958
+ return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
959
+ }
732
960
  function handleMetalOrmWrapper(type, ctx) {
733
961
  const typeRef = type;
734
962
  const typeArgs = typeRef.typeArguments;
@@ -736,7 +964,27 @@ function handleMetalOrmWrapper(type, ctx) {
736
964
  const wrapperName = getWrapperTypeName(type, ctx.checker);
737
965
  if (!wrapperName) return {};
738
966
  const wrapperRel = { wrapper: wrapperName };
967
+ if (!targetType) {
968
+ return { "x-metal-orm-rel": wrapperRel };
969
+ }
739
970
  if (wrapperName === "HasManyCollection" || wrapperName === "ManyToManyCollection") {
971
+ if (ctx.typeStack.has(targetType)) {
972
+ const items2 = {
973
+ type: "object",
974
+ properties: {
975
+ id: { type: "integer" }
976
+ },
977
+ required: ["id"]
978
+ };
979
+ if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
980
+ wrapperRel.pivot = typeArgs[1];
981
+ }
982
+ return {
983
+ type: "array",
984
+ items: items2,
985
+ "x-metal-orm-rel": wrapperRel
986
+ };
987
+ }
740
988
  const items = targetType ? typeToJsonSchema(targetType, ctx) : {};
741
989
  if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
742
990
  wrapperRel.pivot = typeArgs[1];
@@ -747,30 +995,149 @@ function handleMetalOrmWrapper(type, ctx) {
747
995
  "x-metal-orm-rel": wrapperRel
748
996
  };
749
997
  }
750
- const targetSchema = targetType ? typeToJsonSchema(targetType, ctx) : {};
998
+ if (wrapperName === "BelongsToReference" || wrapperName === "HasOneReference") {
999
+ return handleBelongsToReference(targetType, ctx, wrapperRel);
1000
+ }
1001
+ const targetSchema = typeToJsonSchema(targetType, ctx);
751
1002
  return {
752
1003
  ...targetSchema,
753
1004
  "x-metal-orm-rel": wrapperRel
754
1005
  };
755
1006
  }
1007
+ function handleBelongsToReference(targetType, ctx, wrapperRel) {
1008
+ const { components, typeStack } = ctx;
1009
+ const targetSymbol = targetType.getSymbol();
1010
+ const typeName = targetSymbol?.getName();
1011
+ if (!typeName) {
1012
+ return {
1013
+ type: "object",
1014
+ properties: {},
1015
+ "x-metal-orm-rel": wrapperRel
1016
+ };
1017
+ }
1018
+ const refSchemaName = `${typeName}Ref`;
1019
+ if (components.has(refSchemaName)) {
1020
+ return {
1021
+ $ref: `#/components/schemas/${refSchemaName}`,
1022
+ "x-metal-orm-rel": wrapperRel
1023
+ };
1024
+ }
1025
+ if (typeStack.has(targetType)) {
1026
+ const circularRefSchema = {
1027
+ type: "object",
1028
+ properties: {
1029
+ id: { type: "integer" }
1030
+ },
1031
+ required: ["id"]
1032
+ };
1033
+ components.set(refSchemaName, circularRefSchema);
1034
+ return {
1035
+ $ref: `#/components/schemas/${refSchemaName}`,
1036
+ "x-metal-orm-rel": wrapperRel
1037
+ };
1038
+ }
1039
+ const refSchema = buildRefSchema(targetType, ctx);
1040
+ components.set(refSchemaName, refSchema);
1041
+ return {
1042
+ $ref: `#/components/schemas/${refSchemaName}`,
1043
+ "x-metal-orm-rel": wrapperRel
1044
+ };
1045
+ }
1046
+ function buildRefSchema(type, ctx) {
1047
+ const { checker } = ctx;
1048
+ if (!(type.flags & ts6.TypeFlags.Object)) {
1049
+ return { type: "object", properties: {} };
1050
+ }
1051
+ const objectType = type;
1052
+ const properties = {};
1053
+ const required = [];
1054
+ const props = checker.getPropertiesOfType(objectType);
1055
+ for (const prop of props) {
1056
+ const propName = prop.getName();
1057
+ if (isIteratorOrSymbolProperty(propName)) {
1058
+ continue;
1059
+ }
1060
+ const propType = checker.getTypeOfSymbol(prop);
1061
+ if (isMethodLike(propType)) {
1062
+ continue;
1063
+ }
1064
+ const isOptional = !!(prop.flags & ts6.SymbolFlags.Optional);
1065
+ const isRelation = isMetalOrmWrapperType(propType, checker);
1066
+ if (isRelation) {
1067
+ continue;
1068
+ }
1069
+ const propCtx = { ...ctx, propertyName: propName };
1070
+ properties[propName] = typeToJsonSchema(propType, propCtx);
1071
+ if (!isOptional) {
1072
+ required.push(propName);
1073
+ }
1074
+ }
1075
+ const schema = {
1076
+ type: "object",
1077
+ properties
1078
+ };
1079
+ if (required.length > 0) {
1080
+ schema.required = required;
1081
+ }
1082
+ return schema;
1083
+ }
1084
+
1085
+ // src/compiler/schema/typeToJsonSchema.ts
1086
+ function typeToJsonSchema(type, ctx, typeNode) {
1087
+ const primitiveResult = handlePrimitiveType(type, ctx, typeNode);
1088
+ if (primitiveResult) {
1089
+ return primitiveResult;
1090
+ }
1091
+ if (type.isUnion()) {
1092
+ return handleUnion(type, ctx, typeNode);
1093
+ }
1094
+ if (type.isIntersection()) {
1095
+ return handleIntersection(type, ctx, typeNode);
1096
+ }
1097
+ if (ctx.checker.isArrayType(type)) {
1098
+ const typeArgs = type.typeArguments;
1099
+ const itemType = typeArgs?.[0];
1100
+ const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
1101
+ return {
1102
+ type: "array",
1103
+ items,
1104
+ uniqueItems: isSetType(type, ctx.checker) ? true : void 0
1105
+ };
1106
+ }
1107
+ if (type.flags & ts7.TypeFlags.Object) {
1108
+ const objectType = type;
1109
+ if (isMetalOrmWrapperType(type, ctx.checker)) {
1110
+ return handleMetalOrmWrapper(objectType, ctx);
1111
+ }
1112
+ return handleObjectType(objectType, ctx, typeNode);
1113
+ }
1114
+ return {};
1115
+ }
1116
+ function isSetType(type, _checker) {
1117
+ const symbol = type.getSymbol();
1118
+ if (!symbol) return false;
1119
+ const name = symbol.getName();
1120
+ if (name === "Set") return true;
1121
+ return false;
1122
+ }
756
1123
 
757
1124
  // src/compiler/schema/extractAnnotations.ts
758
- import ts4 from "typescript";
1125
+ import ts8 from "typescript";
759
1126
  function extractPropertySchemaFragments(checker, prop) {
760
- if (!ts4.canHaveDecorators(prop)) return [];
761
- const decs = ts4.getDecorators(prop);
1127
+ if (!ts8.canHaveDecorators(prop)) return [];
1128
+ const decs = ts8.getDecorators(prop);
762
1129
  if (!decs || decs.length === 0) return [];
763
1130
  const frags = [];
764
1131
  for (const d of decs) {
765
1132
  const expr = d.expression;
766
1133
  let callee;
767
1134
  let args;
768
- if (ts4.isCallExpression(expr)) {
1135
+ if (ts8.isCallExpression(expr)) {
769
1136
  callee = expr.expression;
770
1137
  args = expr.arguments;
771
1138
  } else {
772
1139
  callee = expr;
773
- args = ts4.factory.createNodeArray([]);
1140
+ args = ts8.factory.createNodeArray([]);
774
1141
  }
775
1142
  const sym = checker.getSymbolAtLocation(callee);
776
1143
  if (!sym) continue;
@@ -779,7 +1146,7 @@ function extractPropertySchemaFragments(checker, prop) {
779
1146
  const name = resolved.name;
780
1147
  if (name === "Schema") {
781
1148
  const obj = args[0];
782
- if (obj && ts4.isObjectLiteralExpression(obj)) {
1149
+ if (obj && ts8.isObjectLiteralExpression(obj)) {
783
1150
  const frag = objectLiteralToJson(obj);
784
1151
  if (frag) frags.push(frag);
785
1152
  }
@@ -801,7 +1168,7 @@ function extractPropertySchemaFragments(checker, prop) {
801
1168
  frags.push({ format: args[0].text });
802
1169
  } else if (name === "Pattern") {
803
1170
  const arg = args[0];
804
- if (arg && ts4.isRegularExpressionLiteral(arg)) {
1171
+ if (arg && ts8.isRegularExpressionLiteral(arg)) {
805
1172
  frags.push({ pattern: extractRegexPattern(arg.text) });
806
1173
  } else if (isStringLiteral(arg)) {
807
1174
  frags.push({ pattern: arg.text });
@@ -818,11 +1185,11 @@ function extractPropertySchemaFragments(checker, prop) {
818
1185
  frags.push({ multipleOf: Number(args[0].text) });
819
1186
  } else if (name === "Example") {
820
1187
  frags.push({ example: literalToJson(args[0]) });
821
- } else if (name === "Examples" && ts4.isArrayLiteralExpression(args[0])) {
1188
+ } else if (name === "Examples" && ts8.isArrayLiteralExpression(args[0])) {
822
1189
  frags.push({ examples: args[0].elements.map((e) => literalToJson(e)) });
823
1190
  } else if (name === "Description" && isStringLiteral(args[0])) {
824
1191
  frags.push({ description: args[0].text });
825
- } else if (name === "Enum" && ts4.isArrayLiteralExpression(args[0])) {
1192
+ } else if (name === "Enum" && ts8.isArrayLiteralExpression(args[0])) {
826
1193
  frags.push({ enum: args[0].elements.map((e) => literalToJson(e)) });
827
1194
  } else if (name === "Const") {
828
1195
  frags.push({ const: literalToJson(args[0]) });
@@ -830,9 +1197,9 @@ function extractPropertySchemaFragments(checker, prop) {
830
1197
  frags.push({ default: literalToJson(args[0]) });
831
1198
  } else if (name === "AdditionalProperties") {
832
1199
  const arg = args[0];
833
- if (arg && (arg.kind === ts4.SyntaxKind.FalseKeyword || arg.kind === ts4.SyntaxKind.TrueKeyword)) {
834
- frags.push({ additionalProperties: arg.kind === ts4.SyntaxKind.TrueKeyword });
835
- } else if (arg && ts4.isObjectLiteralExpression(arg)) {
1200
+ if (arg && (arg.kind === ts8.SyntaxKind.FalseKeyword || arg.kind === ts8.SyntaxKind.TrueKeyword)) {
1201
+ frags.push({ additionalProperties: arg.kind === ts8.SyntaxKind.TrueKeyword });
1202
+ } else if (arg && ts8.isObjectLiteralExpression(arg)) {
836
1203
  const obj = objectLiteralToJson(arg);
837
1204
  if (obj) frags.push({ additionalProperties: obj });
838
1205
  }
@@ -845,7 +1212,7 @@ function extractPropertySchemaFragments(checker, prop) {
845
1212
  return frags;
846
1213
  }
847
1214
  function resolveImportedDecorator(checker, sym) {
848
- const target = sym.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
1215
+ const target = sym.flags & ts8.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
849
1216
  const name = target.getName();
850
1217
  const decl = target.declarations?.[0];
851
1218
  if (!decl) return null;
@@ -856,10 +1223,10 @@ function resolveImportedDecorator(checker, sym) {
856
1223
  return null;
857
1224
  }
858
1225
  function isNumberLiteral(node) {
859
- return !!node && ts4.isNumericLiteral(node);
1226
+ return !!node && ts8.isNumericLiteral(node);
860
1227
  }
861
1228
  function isStringLiteral(node) {
862
- return !!node && ts4.isStringLiteral(node);
1229
+ return !!node && ts8.isStringLiteral(node);
863
1230
  }
864
1231
  function extractRegexPattern(text) {
865
1232
  const match = text.match(/^\/(.+)\/[gimsuy]*$/);
@@ -868,12 +1235,12 @@ function extractRegexPattern(text) {
868
1235
  function objectLiteralToJson(obj) {
869
1236
  const out = {};
870
1237
  for (const prop of obj.properties) {
871
- if (!ts4.isPropertyAssignment(prop)) continue;
1238
+ if (!ts8.isPropertyAssignment(prop)) continue;
872
1239
  const name = prop.name;
873
1240
  let key;
874
- if (ts4.isIdentifier(name)) {
1241
+ if (ts8.isIdentifier(name)) {
875
1242
  key = name.text;
876
- } else if (ts4.isStringLiteral(name)) {
1243
+ } else if (ts8.isStringLiteral(name)) {
877
1244
  key = name.text;
878
1245
  } else {
879
1246
  continue;
@@ -883,13 +1250,13 @@ function objectLiteralToJson(obj) {
883
1250
  return out;
884
1251
  }
885
1252
  function literalToJson(node) {
886
- if (ts4.isStringLiteral(node)) return node.text;
887
- if (ts4.isNumericLiteral(node)) return Number(node.text);
888
- if (node.kind === ts4.SyntaxKind.TrueKeyword) return true;
889
- if (node.kind === ts4.SyntaxKind.FalseKeyword) return false;
890
- if (node.kind === ts4.SyntaxKind.NullKeyword) return null;
891
- if (ts4.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
892
- if (ts4.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
1253
+ if (ts8.isStringLiteral(node)) return node.text;
1254
+ if (ts8.isNumericLiteral(node)) return Number(node.text);
1255
+ if (node.kind === ts8.SyntaxKind.TrueKeyword) return true;
1256
+ if (node.kind === ts8.SyntaxKind.FalseKeyword) return false;
1257
+ if (node.kind === ts8.SyntaxKind.NullKeyword) return null;
1258
+ if (ts8.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
1259
+ if (ts8.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
893
1260
  return void 0;
894
1261
  }
895
1262
  function mergeFragments(base, ...frags) {
@@ -900,7 +1267,302 @@ function mergeFragments(base, ...frags) {
900
1267
  return result;
901
1268
  }
902
1269
 
1270
+ // src/compiler/schema/parameters.ts
1271
+ function buildPathParameters(operation, ctx, parameters) {
1272
+ for (const paramIndex of operation.pathParamIndices) {
1273
+ const param = operation.parameters[paramIndex];
1274
+ if (param) {
1275
+ let paramSchema = typeToJsonSchema(param.type, ctx);
1276
+ if (param.paramNode) {
1277
+ const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1278
+ if (frags.length > 0) {
1279
+ paramSchema = mergeFragments(paramSchema, ...frags);
1280
+ }
1281
+ }
1282
+ const schema = paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema;
1283
+ const paramName = param.name.toLowerCase();
1284
+ const isIdParam = paramName === "id" || paramName.endsWith("id");
1285
+ if (!schema.$ref && schema.type === "number" && isIdParam) {
1286
+ schema.type = "integer";
1287
+ if (!schema.minimum) {
1288
+ schema.minimum = 1;
1289
+ }
1290
+ }
1291
+ parameters.push({
1292
+ name: param.name,
1293
+ in: "path",
1294
+ required: !param.isOptional,
1295
+ schema
1296
+ });
1297
+ }
1298
+ }
1299
+ }
1300
+ function buildQueryParameters(operation, ctx, parameters) {
1301
+ if (operation.queryObjectParamIndex !== null) {
1302
+ const queryParam = operation.parameters[operation.queryObjectParamIndex];
1303
+ if (!queryParam) return;
1304
+ const querySchema = typeToJsonSchema(queryParam.type, ctx);
1305
+ const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
1306
+ for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1307
+ const isRequired = queryRequired.includes(propName);
1308
+ const isDeepObject = isDeepObjectSchema(propSchema, ctx);
1309
+ const isObjectLike = isObjectLikeSchema(propSchema, ctx);
1310
+ const serialization = determineQuerySerialization(propSchema.type);
1311
+ const exampleValue = generateExampleValue(propSchema, propName);
1312
+ if (isDeepObject) {
1313
+ parameters.push({
1314
+ name: propName,
1315
+ in: "query",
1316
+ required: isRequired,
1317
+ style: "deepObject",
1318
+ explode: true,
1319
+ schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1320
+ });
1321
+ } else if (isObjectLike) {
1322
+ const schemaRef = propSchema.$ref || "#/components/schemas/InlineQueryParam";
1323
+ parameters.push({
1324
+ name: propName,
1325
+ in: "query",
1326
+ required: isRequired,
1327
+ schema: { type: "string" },
1328
+ description: `JSON-encoded object. ${exampleValue}`,
1329
+ examples: {
1330
+ default: { value: parseExampleValue(exampleValue) }
1331
+ },
1332
+ "x-adorn-jsonSchemaRef": schemaRef
1333
+ });
1334
+ } else {
1335
+ const paramDef = {
1336
+ name: propName,
1337
+ in: "query",
1338
+ required: isRequired,
1339
+ schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1340
+ };
1341
+ if (propName === "page") {
1342
+ paramDef.schema = { type: "integer", default: 1, minimum: 1 };
1343
+ } else if (propName === "pageSize") {
1344
+ paramDef.schema = { type: "integer", default: 10, minimum: 1 };
1345
+ } else if (propName === "totalItems") {
1346
+ paramDef.schema = { type: "integer", minimum: 0 };
1347
+ } else if (propName === "sort") {
1348
+ paramDef.schema = {
1349
+ oneOf: [
1350
+ { type: "string" },
1351
+ { type: "array", items: { type: "string" } }
1352
+ ]
1353
+ };
1354
+ } else if (propName === "q") {
1355
+ paramDef.schema = { type: "string" };
1356
+ } else if (propName === "hasComments") {
1357
+ paramDef.schema = { type: "boolean" };
1358
+ }
1359
+ if (Object.keys(serialization).length > 0) {
1360
+ Object.assign(paramDef, serialization);
1361
+ }
1362
+ parameters.push(paramDef);
1363
+ }
1364
+ }
1365
+ }
1366
+ for (const paramIndex of operation.queryParamIndices) {
1367
+ const param = operation.parameters[paramIndex];
1368
+ if (param) {
1369
+ let paramSchema = typeToJsonSchema(param.type, ctx);
1370
+ if (param.paramNode) {
1371
+ const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1372
+ if (frags.length > 0) {
1373
+ paramSchema = mergeFragments(paramSchema, ...frags);
1374
+ }
1375
+ }
1376
+ const isDeepObject = isDeepObjectSchema(paramSchema, ctx);
1377
+ const isObjectLike = isObjectLikeSchema(paramSchema, ctx);
1378
+ if (isDeepObject) {
1379
+ parameters.push({
1380
+ name: param.name,
1381
+ in: "query",
1382
+ required: !param.isOptional,
1383
+ style: "deepObject",
1384
+ explode: true,
1385
+ schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema
1386
+ });
1387
+ } else if (isObjectLike) {
1388
+ const schemaRef = paramSchema.$ref || "#/components/schemas/InlineQueryParam";
1389
+ const exampleValue = generateExampleValue(paramSchema, param.name);
1390
+ parameters.push({
1391
+ name: param.name,
1392
+ in: "query",
1393
+ required: !param.isOptional,
1394
+ schema: { type: "string" },
1395
+ description: `JSON-encoded object. ${exampleValue}`,
1396
+ examples: {
1397
+ default: { value: parseExampleValue(exampleValue) }
1398
+ },
1399
+ "x-adorn-jsonSchemaRef": schemaRef
1400
+ });
1401
+ } else {
1402
+ const serialization = determineQuerySerialization(paramSchema.type);
1403
+ parameters.push({
1404
+ name: param.name,
1405
+ in: "query",
1406
+ required: !param.isOptional,
1407
+ schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema,
1408
+ ...Object.keys(serialization).length > 0 ? serialization : {}
1409
+ });
1410
+ }
1411
+ }
1412
+ }
1413
+ }
1414
+ function buildHeaderParameters(operation, ctx, parameters) {
1415
+ if (operation.headerObjectParamIndex === null) return;
1416
+ const headerParam = operation.parameters[operation.headerObjectParamIndex];
1417
+ if (!headerParam) return;
1418
+ const headerSchema = typeToJsonSchema(headerParam.type, ctx);
1419
+ if (!headerSchema.properties) return;
1420
+ const headerObjProps = headerSchema.properties;
1421
+ for (const [propName, propSchema] of Object.entries(headerObjProps)) {
1422
+ const isRequired = headerSchema.required?.includes(propName) ?? false;
1423
+ parameters.push({
1424
+ name: propName,
1425
+ in: "header",
1426
+ required: isRequired,
1427
+ schema: propSchema
1428
+ });
1429
+ }
1430
+ }
1431
+ function buildCookieParameters(operation, ctx, parameters) {
1432
+ if (operation.cookieObjectParamIndex === null) return;
1433
+ const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
1434
+ if (!cookieParam) return;
1435
+ const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
1436
+ if (!cookieSchema.properties) return;
1437
+ const cookieObjProps = cookieSchema.properties;
1438
+ for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
1439
+ const isRequired = cookieSchema.required?.includes(propName) ?? false;
1440
+ parameters.push({
1441
+ name: propName,
1442
+ in: "cookie",
1443
+ required: isRequired,
1444
+ schema: propSchema,
1445
+ style: "form",
1446
+ explode: true
1447
+ });
1448
+ }
1449
+ }
1450
+ function determineQuerySerialization(schemaType) {
1451
+ const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
1452
+ const isArray = typeArray.includes("array");
1453
+ if (isArray) {
1454
+ return { style: "form", explode: true };
1455
+ }
1456
+ return {};
1457
+ }
1458
+ function generateExampleValue(schema, propName) {
1459
+ const resolved = resolveSchemaRef(schema, /* @__PURE__ */ new Map());
1460
+ if (resolved.type === "object" && resolved.properties) {
1461
+ const example = {};
1462
+ for (const [key, prop] of Object.entries(resolved.properties)) {
1463
+ const propResolved = resolveSchemaRef(prop, /* @__PURE__ */ new Map());
1464
+ if (propResolved.type === "string") {
1465
+ example[key] = "value";
1466
+ } else if (propResolved.type === "number" || propResolved.type === "integer") {
1467
+ example[key] = 1;
1468
+ } else if (propResolved.type === "boolean") {
1469
+ example[key] = true;
1470
+ } else if (Array.isArray(propResolved.type) && propResolved.type.includes("null")) {
1471
+ example[key] = null;
1472
+ } else if (propResolved.enum) {
1473
+ example[key] = propResolved.enum[0];
1474
+ } else {
1475
+ example[key] = "value";
1476
+ }
1477
+ }
1478
+ return `Example: ${propName}=${JSON.stringify(example)}`;
1479
+ }
1480
+ return `Example: ${propName}=${JSON.stringify({ key: "value" })}`;
1481
+ }
1482
+ function parseExampleValue(description) {
1483
+ const match = description.match(/Example:\s*\w+=(\{[^}]+\})/);
1484
+ if (match) {
1485
+ return match[1];
1486
+ }
1487
+ return JSON.stringify({ key: "value" });
1488
+ }
1489
+ function isDeepObjectSchema(schema, ctx) {
1490
+ const resolved = resolveSchemaRef(schema, ctx.components);
1491
+ if (resolved.type === "array") {
1492
+ return false;
1493
+ }
1494
+ if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1495
+ return true;
1496
+ }
1497
+ if (resolved.allOf) {
1498
+ for (const branch of resolved.allOf) {
1499
+ if (isDeepObjectSchema(branch, ctx)) {
1500
+ return true;
1501
+ }
1502
+ }
1503
+ }
1504
+ return false;
1505
+ }
1506
+ function isObjectLikeSchema(schema, ctx) {
1507
+ const resolved = resolveSchemaRef(schema, ctx.components);
1508
+ if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1509
+ return true;
1510
+ }
1511
+ if (resolved.allOf) {
1512
+ for (const branch of resolved.allOf) {
1513
+ if (isObjectLikeSchema(branch, ctx)) {
1514
+ return true;
1515
+ }
1516
+ }
1517
+ }
1518
+ if (resolved.type === "array" && resolved.items) {
1519
+ const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
1520
+ return isObjectLikeSchema(itemsSchema, ctx);
1521
+ }
1522
+ return false;
1523
+ }
1524
+ function resolveSchemaRef(schema, components) {
1525
+ const ref = schema.$ref;
1526
+ if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
1527
+ return schema;
1528
+ }
1529
+ const name = ref.replace("#/components/schemas/", "");
1530
+ const next = components.get(name);
1531
+ if (!next) return schema;
1532
+ return resolveSchemaRef(next, components);
1533
+ }
1534
+ function resolveAndCollectObjectProps(schema, components) {
1535
+ const resolved = resolveSchemaRef(schema, components);
1536
+ const properties = {};
1537
+ const required = [];
1538
+ const processSchema = (s) => {
1539
+ const current = resolveSchemaRef(s, components);
1540
+ if (current.properties) {
1541
+ for (const [key, val] of Object.entries(current.properties)) {
1542
+ if (!properties[key]) {
1543
+ properties[key] = val;
1544
+ }
1545
+ }
1546
+ }
1547
+ if (current.required) {
1548
+ for (const req of current.required) {
1549
+ if (!required.includes(req)) {
1550
+ required.push(req);
1551
+ }
1552
+ }
1553
+ }
1554
+ if (current.allOf) {
1555
+ for (const branch of current.allOf) {
1556
+ processSchema(branch);
1557
+ }
1558
+ }
1559
+ };
1560
+ processSchema(resolved);
1561
+ return { properties, required };
1562
+ }
1563
+
903
1564
  // src/compiler/schema/openapi.ts
1565
+ var METAL_ORM_WRAPPER_NAMES2 = ["BelongsToReference", "HasOneReference", "HasManyCollection", "ManyToManyCollection"];
904
1566
  function generateOpenAPI(controllers, checker, options = {}) {
905
1567
  const components = /* @__PURE__ */ new Map();
906
1568
  const ctx = {
@@ -917,21 +1579,124 @@ function generateOpenAPI(controllers, checker, options = {}) {
917
1579
  if (!paths[fullPath]) {
918
1580
  paths[fullPath] = {};
919
1581
  }
920
- const method = operation.httpMethod.toLowerCase();
921
- paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
1582
+ const method = operation.httpMethod.toLowerCase();
1583
+ paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
1584
+ }
1585
+ }
1586
+ const schemas = Object.fromEntries(components);
1587
+ cleanupMetalOrmWrappers(schemas, paths);
1588
+ return {
1589
+ openapi: "3.1.0",
1590
+ info: {
1591
+ title: options.title ?? "API",
1592
+ version: options.version ?? "1.0.0"
1593
+ },
1594
+ components: {
1595
+ schemas
1596
+ },
1597
+ paths
1598
+ };
1599
+ }
1600
+ function cleanupMetalOrmWrappers(schemas, paths) {
1601
+ const schemasToDelete = /* @__PURE__ */ new Set();
1602
+ for (const wrapperName of METAL_ORM_WRAPPER_NAMES2) {
1603
+ if (schemas[wrapperName]) {
1604
+ schemasToDelete.add(wrapperName);
1605
+ }
1606
+ if (schemas[`${wrapperName}Api`]) {
1607
+ schemasToDelete.add(`${wrapperName}Api`);
1608
+ }
1609
+ }
1610
+ for (const schema of Object.values(schemas)) {
1611
+ cleanupSchemaRefs(schema, schemasToDelete);
1612
+ }
1613
+ for (const pathItem of Object.values(paths)) {
1614
+ cleanupPathItemRefs(pathItem, schemasToDelete);
1615
+ }
1616
+ for (const schemaName of schemasToDelete) {
1617
+ delete schemas[schemaName];
1618
+ }
1619
+ }
1620
+ function cleanupSchemaRefs(schema, schemasToDelete) {
1621
+ if (typeof schema !== "object" || schema === null) {
1622
+ return;
1623
+ }
1624
+ if (schema.properties) {
1625
+ for (const propName of Object.keys(schema.properties)) {
1626
+ const propSchema = schema.properties[propName];
1627
+ if (propSchema.$ref && typeof propSchema.$ref === "string") {
1628
+ const refName = propSchema.$ref.replace("#/components/schemas/", "");
1629
+ if (schemasToDelete.has(refName)) {
1630
+ delete schema.properties[propName];
1631
+ if (schema.required && Array.isArray(schema.required)) {
1632
+ schema.required = schema.required.filter((r) => r !== propName);
1633
+ }
1634
+ }
1635
+ } else {
1636
+ cleanupSchemaRefs(propSchema, schemasToDelete);
1637
+ }
1638
+ }
1639
+ }
1640
+ if (schema.items) {
1641
+ cleanupSchemaRefs(schema.items, schemasToDelete);
1642
+ }
1643
+ if (schema.allOf) {
1644
+ for (const item of schema.allOf) {
1645
+ cleanupSchemaRefs(item, schemasToDelete);
1646
+ }
1647
+ }
1648
+ }
1649
+ function cleanupPathItemRefs(pathItem, schemasToDelete) {
1650
+ if (typeof pathItem !== "object" || pathItem === null) {
1651
+ return;
1652
+ }
1653
+ for (const method of Object.keys(pathItem)) {
1654
+ const operation = pathItem[method];
1655
+ if (typeof operation !== "object" || operation === null) continue;
1656
+ if (operation.requestBody) {
1657
+ cleanupRequestBodyRefs(operation.requestBody, schemasToDelete);
1658
+ }
1659
+ if (operation.responses) {
1660
+ const responses = Object.values(operation.responses);
1661
+ for (const response of responses) {
1662
+ if (response.content) {
1663
+ const contentTypes = Object.values(response.content);
1664
+ for (const contentType of contentTypes) {
1665
+ if (contentType.schema) {
1666
+ cleanupSchemaRefs(contentType.schema, schemasToDelete);
1667
+ }
1668
+ }
1669
+ }
1670
+ }
1671
+ }
1672
+ }
1673
+ }
1674
+ function cleanupRequestBodyRefs(requestBody, schemasToDelete) {
1675
+ if (typeof requestBody !== "object" || requestBody === null) return;
1676
+ if (requestBody.content) {
1677
+ const contentTypes = Object.values(requestBody.content);
1678
+ for (const contentType of contentTypes) {
1679
+ if (contentType.schema) {
1680
+ cleanupSchemaRefs(contentType.schema, schemasToDelete);
1681
+ }
1682
+ }
1683
+ }
1684
+ if (requestBody.properties) {
1685
+ for (const propName of Object.keys(requestBody.properties)) {
1686
+ const propSchema = requestBody.properties[propName];
1687
+ if (propSchema.$ref && typeof propSchema.$ref === "string") {
1688
+ const refName = propSchema.$ref.replace("#/components/schemas/", "");
1689
+ if (schemasToDelete.has(refName)) {
1690
+ delete requestBody.properties[propName];
1691
+ if (requestBody.required && Array.isArray(requestBody.required)) {
1692
+ requestBody.required = requestBody.required.filter((r) => r !== propName);
1693
+ }
1694
+ }
1695
+ } else {
1696
+ cleanupSchemaRefs(propSchema, schemasToDelete);
1697
+ }
922
1698
  }
923
1699
  }
924
- return {
925
- openapi: "3.1.0",
926
- info: {
927
- title: options.title ?? "API",
928
- version: options.version ?? "1.0.0"
929
- },
930
- components: {
931
- schemas: Object.fromEntries(components)
932
- },
933
- paths
934
- };
935
1700
  }
936
1701
  function convertToOpenApiPath(basePath, path4) {
937
1702
  const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
@@ -998,12 +1763,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
998
1763
  const declarations = typeSymbol.getDeclarations();
999
1764
  if (!declarations || declarations.length === 0) return schema;
1000
1765
  const classDecl = declarations[0];
1001
- if (!ts5.isClassDeclaration(classDecl)) return schema;
1766
+ if (!ts9.isClassDeclaration(classDecl)) return schema;
1002
1767
  const result = { ...schema };
1003
1768
  const props = { ...result.properties };
1004
1769
  for (const member of classDecl.members) {
1005
- if (!ts5.isPropertyDeclaration(member) || !member.name) continue;
1006
- const propName = ts5.isIdentifier(member.name) ? member.name.text : null;
1770
+ if (!ts9.isPropertyDeclaration(member) || !member.name) continue;
1771
+ const propName = ts9.isIdentifier(member.name) ? member.name.text : null;
1007
1772
  if (!propName) continue;
1008
1773
  if (!props[propName]) continue;
1009
1774
  const frags = extractPropertySchemaFragments(ctx.checker, member);
@@ -1014,220 +1779,9 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
1014
1779
  result.properties = props;
1015
1780
  return result;
1016
1781
  }
1017
- function buildPathParameters(operation, ctx, parameters) {
1018
- for (const paramIndex of operation.pathParamIndices) {
1019
- const param = operation.parameters[paramIndex];
1020
- if (param) {
1021
- let paramSchema = typeToJsonSchema(param.type, ctx);
1022
- if (param.paramNode) {
1023
- const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1024
- if (frags.length > 0) {
1025
- paramSchema = mergeFragments(paramSchema, ...frags);
1026
- }
1027
- }
1028
- const schema = paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema;
1029
- parameters.push({
1030
- name: param.name,
1031
- in: "path",
1032
- required: !param.isOptional,
1033
- schema
1034
- });
1035
- }
1036
- }
1037
- }
1038
- function isObjectLikeSchema(schema, ctx) {
1039
- const resolved = resolveSchemaRef(schema, ctx.components);
1040
- if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1041
- return true;
1042
- }
1043
- if (resolved.allOf) {
1044
- for (const branch of resolved.allOf) {
1045
- if (isObjectLikeSchema(branch, ctx)) {
1046
- return true;
1047
- }
1048
- }
1049
- }
1050
- if (resolved.type === "array" && resolved.items) {
1051
- const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
1052
- return isObjectLikeSchema(itemsSchema, ctx);
1053
- }
1054
- return false;
1055
- }
1056
- function resolveSchemaRef(schema, components) {
1057
- const ref = schema.$ref;
1058
- if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
1059
- return schema;
1060
- }
1061
- const name = ref.replace("#/components/schemas/", "");
1062
- const next = components.get(name);
1063
- if (!next) return schema;
1064
- return resolveSchemaRef(next, components);
1065
- }
1066
- function resolveAndCollectObjectProps(schema, components) {
1067
- const resolved = resolveSchemaRef(schema, components);
1068
- const properties = {};
1069
- const required = [];
1070
- const processSchema = (s) => {
1071
- const current = resolveSchemaRef(s, components);
1072
- if (current.properties) {
1073
- for (const [key, val] of Object.entries(current.properties)) {
1074
- if (!properties[key]) {
1075
- properties[key] = val;
1076
- }
1077
- }
1078
- }
1079
- if (current.required) {
1080
- for (const req of current.required) {
1081
- if (!required.includes(req)) {
1082
- required.push(req);
1083
- }
1084
- }
1085
- }
1086
- if (current.allOf) {
1087
- for (const branch of current.allOf) {
1088
- processSchema(branch);
1089
- }
1090
- }
1091
- };
1092
- processSchema(resolved);
1093
- return { properties, required };
1094
- }
1095
- function buildQueryParameters(operation, ctx, parameters) {
1096
- if (operation.queryObjectParamIndex !== null) {
1097
- const queryParam = operation.parameters[operation.queryObjectParamIndex];
1098
- if (!queryParam) return;
1099
- const querySchema = typeToJsonSchema(queryParam.type, ctx);
1100
- const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
1101
- for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1102
- const isRequired = queryRequired.includes(propName) ?? false;
1103
- const isObjectLike = isObjectLikeSchema(propSchema, ctx);
1104
- const serialization = determineQuerySerialization(propSchema.type);
1105
- if (isObjectLike) {
1106
- parameters.push({
1107
- name: propName,
1108
- in: "query",
1109
- required: isRequired,
1110
- content: {
1111
- "application/json": {
1112
- schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1113
- }
1114
- },
1115
- description: `URL-encoded JSON. Example: ${propName}=${encodeURIComponent(JSON.stringify({ example: "value" }))}`
1116
- });
1117
- } else {
1118
- const paramDef = {
1119
- name: propName,
1120
- in: "query",
1121
- required: isRequired,
1122
- schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1123
- };
1124
- if (propName === "page") {
1125
- paramDef.schema = { type: "integer", default: 1, minimum: 1 };
1126
- } else if (propName === "pageSize") {
1127
- paramDef.schema = { type: "integer", default: 10, minimum: 1 };
1128
- } else if (propName === "totalItems") {
1129
- paramDef.schema = { type: "integer", minimum: 0 };
1130
- } else if (propName === "sort") {
1131
- paramDef.schema = {
1132
- oneOf: [
1133
- { type: "string" },
1134
- { type: "array", items: { type: "string" } }
1135
- ]
1136
- };
1137
- } else if (propName === "q") {
1138
- paramDef.schema = { type: "string" };
1139
- } else if (propName === "hasComments") {
1140
- paramDef.schema = { type: "boolean" };
1141
- }
1142
- if (Object.keys(serialization).length > 0) {
1143
- Object.assign(paramDef, serialization);
1144
- }
1145
- parameters.push(paramDef);
1146
- }
1147
- }
1148
- }
1149
- for (const paramIndex of operation.queryParamIndices) {
1150
- const param = operation.parameters[paramIndex];
1151
- if (param) {
1152
- let paramSchema = typeToJsonSchema(param.type, ctx);
1153
- if (param.paramNode) {
1154
- const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1155
- if (frags.length > 0) {
1156
- paramSchema = mergeFragments(paramSchema, ...frags);
1157
- }
1158
- }
1159
- const isObjectLike = isObjectLikeSchema(paramSchema, ctx);
1160
- if (isObjectLike) {
1161
- parameters.push({
1162
- name: param.name,
1163
- in: "query",
1164
- required: !param.isOptional,
1165
- content: {
1166
- "application/json": {
1167
- schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema
1168
- }
1169
- }
1170
- });
1171
- } else {
1172
- const serialization = determineQuerySerialization(paramSchema.type);
1173
- parameters.push({
1174
- name: param.name,
1175
- in: "query",
1176
- required: !param.isOptional,
1177
- schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema,
1178
- ...Object.keys(serialization).length > 0 ? serialization : {}
1179
- });
1180
- }
1181
- }
1182
- }
1183
- }
1184
- function determineQuerySerialization(schemaType) {
1185
- const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
1186
- const isArray = typeArray.includes("array");
1187
- if (isArray) {
1188
- return { style: "form", explode: true };
1189
- }
1190
- return {};
1191
- }
1192
- function buildHeaderParameters(operation, ctx, parameters) {
1193
- if (operation.headerObjectParamIndex === null) return;
1194
- const headerParam = operation.parameters[operation.headerObjectParamIndex];
1195
- if (!headerParam) return;
1196
- const headerSchema = typeToJsonSchema(headerParam.type, ctx);
1197
- if (!headerSchema.properties) return;
1198
- const headerObjProps = headerSchema.properties;
1199
- for (const [propName, propSchema] of Object.entries(headerObjProps)) {
1200
- const isRequired = headerSchema.required?.includes(propName) ?? false;
1201
- parameters.push({
1202
- name: propName,
1203
- in: "header",
1204
- required: isRequired,
1205
- schema: propSchema
1206
- });
1207
- }
1208
- }
1209
- function buildCookieParameters(operation, ctx, parameters) {
1210
- if (operation.cookieObjectParamIndex === null) return;
1211
- const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
1212
- if (!cookieParam) return;
1213
- const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
1214
- if (!cookieSchema.properties) return;
1215
- const cookieObjProps = cookieSchema.properties;
1216
- for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
1217
- const isRequired = cookieSchema.required?.includes(propName) ?? false;
1218
- parameters.push({
1219
- name: propName,
1220
- in: "cookie",
1221
- required: isRequired,
1222
- schema: propSchema,
1223
- style: "form",
1224
- explode: true
1225
- });
1226
- }
1227
- }
1228
1782
 
1229
1783
  // src/compiler/manifest/emit.ts
1230
- import ts6 from "typescript";
1784
+ import ts10 from "typescript";
1231
1785
  function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
1232
1786
  const components = /* @__PURE__ */ new Map();
1233
1787
  const ctx = {
@@ -1249,7 +1803,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
1249
1803
  generator: {
1250
1804
  name: "adorn-api",
1251
1805
  version,
1252
- typescript: ts6.version
1806
+ typescript: ts10.version
1253
1807
  },
1254
1808
  schemas: {
1255
1809
  kind: "openapi-3.1",
@@ -1299,6 +1853,23 @@ function resolveAndCollectObjectProps2(schema, components) {
1299
1853
  processSchema(resolved);
1300
1854
  return { properties, required };
1301
1855
  }
1856
+ function isDeepObjectSchema2(schema, components) {
1857
+ const resolved = resolveSchemaRef2(schema, components);
1858
+ if (resolved.type === "array") {
1859
+ return false;
1860
+ }
1861
+ if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1862
+ return true;
1863
+ }
1864
+ if (resolved.allOf) {
1865
+ for (const branch of resolved.allOf) {
1866
+ if (isDeepObjectSchema2(branch, components)) {
1867
+ return true;
1868
+ }
1869
+ }
1870
+ }
1871
+ return false;
1872
+ }
1302
1873
  function isObjectLikeSchema2(schema, components) {
1303
1874
  const resolved = resolveSchemaRef2(schema, components);
1304
1875
  if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
@@ -1395,6 +1966,7 @@ function buildQueryArgs(op, ctx, args) {
1395
1966
  const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps2(querySchema, ctx.components);
1396
1967
  for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1397
1968
  const isRequired = queryRequired.includes(propName) ?? false;
1969
+ const isDeepObject = isDeepObjectSchema2(propSchema, ctx.components);
1398
1970
  const isObjectLike = isObjectLikeSchema2(propSchema, ctx.components);
1399
1971
  let schemaRef = propSchema.$ref;
1400
1972
  if (!schemaRef) {
@@ -1403,10 +1975,11 @@ function buildQueryArgs(op, ctx, args) {
1403
1975
  args.query.push({
1404
1976
  name: propName,
1405
1977
  index: queryParam.index,
1406
- required: !isRequired,
1978
+ required: isRequired,
1407
1979
  schemaRef,
1408
1980
  schemaType: propSchema.type,
1409
- content: isObjectLike ? "application/json" : void 0
1981
+ serialization: isDeepObject ? { style: "deepObject", explode: true } : void 0,
1982
+ content: !isDeepObject && isObjectLike ? "application/json" : void 0
1410
1983
  });
1411
1984
  }
1412
1985
  }
@@ -1414,6 +1987,7 @@ function buildQueryArgs(op, ctx, args) {
1414
1987
  const param = op.parameters[paramIndex];
1415
1988
  if (param) {
1416
1989
  const paramSchema = typeToJsonSchema(param.type, ctx);
1990
+ const isDeepObject = isDeepObjectSchema2(paramSchema, ctx.components);
1417
1991
  const isObjectLike = isObjectLikeSchema2(paramSchema, ctx.components);
1418
1992
  const schemaRef = paramSchema.$ref ?? "#/components/schemas/InlineQueryParam";
1419
1993
  args.query.push({
@@ -1422,7 +1996,8 @@ function buildQueryArgs(op, ctx, args) {
1422
1996
  required: !param.isOptional,
1423
1997
  schemaRef,
1424
1998
  schemaType: paramSchema.type,
1425
- content: isObjectLike ? "application/json" : void 0
1999
+ serialization: isDeepObject ? { style: "deepObject", explode: true } : void 0,
2000
+ content: !isDeepObject && isObjectLike ? "application/json" : void 0
1426
2001
  });
1427
2002
  }
1428
2003
  }
@@ -1442,7 +2017,7 @@ function buildHeaderArgs(op, ctx, args) {
1442
2017
  args.headers.push({
1443
2018
  name: propName,
1444
2019
  index: headerParam.index,
1445
- required: !isRequired,
2020
+ required: isRequired,
1446
2021
  schemaRef,
1447
2022
  schemaType: propSchema.type
1448
2023
  });
@@ -1463,7 +2038,7 @@ function buildCookieArgs(op, ctx, args) {
1463
2038
  args.cookies.push({
1464
2039
  name: propName,
1465
2040
  index: cookieParam.index,
1466
- required: !isRequired,
2041
+ required: isRequired,
1467
2042
  schemaRef,
1468
2043
  schemaType: propSchema.type,
1469
2044
  serialization: { style: "form", explode: true }
@@ -1479,7 +2054,7 @@ import Ajv from "ajv";
1479
2054
  import addFormats from "ajv-formats";
1480
2055
  var OAS_SCHEMA_ONLY = /* @__PURE__ */ new Set(["discriminator", "xml", "externalDocs", "example"]);
1481
2056
  function sanitizeSchemaForAjv(schema) {
1482
- if (schema == null || typeof schema !== "object") return schema;
2057
+ if (schema === null || typeof schema !== "object") return schema;
1483
2058
  if (Array.isArray(schema)) return schema.map(sanitizeSchemaForAjv);
1484
2059
  const out = {};
1485
2060
  for (const [k, v] of Object.entries(schema)) {
@@ -1490,7 +2065,7 @@ function sanitizeSchemaForAjv(schema) {
1490
2065
  return out;
1491
2066
  }
1492
2067
  function rewriteComponentRefs(schema) {
1493
- if (schema == null || typeof schema !== "object") return schema;
2068
+ if (schema === null || typeof schema !== "object") return schema;
1494
2069
  if (Array.isArray(schema)) return schema.map(rewriteComponentRefs);
1495
2070
  if (typeof schema.$ref === "string") {
1496
2071
  const ref = schema.$ref;
@@ -1567,16 +2142,13 @@ async function emitPrecompiledValidators(opts) {
1567
2142
  `;
1568
2143
  cjs += ` body: ${v.body ? `exports[${JSON.stringify(v.body)}]` : "undefined"},
1569
2144
  `;
1570
- cjs += ` response: {
1571
- `;
2145
+ cjs += " response: {\n";
1572
2146
  for (const [key, id] of Object.entries(v.response)) {
1573
2147
  cjs += ` ${JSON.stringify(key)}: exports[${JSON.stringify(id)}],
1574
2148
  `;
1575
2149
  }
1576
- cjs += ` }
1577
- `;
1578
- cjs += ` },
1579
- `;
2150
+ cjs += " }\n";
2151
+ cjs += " },\n";
1580
2152
  }
1581
2153
  cjs += "};\n";
1582
2154
  fs.writeFileSync(cjsPath, cjs, "utf8");
@@ -1611,7 +2183,6 @@ export function validateResponse(operationId, status, contentType, data) {
1611
2183
  // src/compiler/cache/isStale.ts
1612
2184
  import fs2 from "fs";
1613
2185
  import path2 from "path";
1614
- import "typescript";
1615
2186
  function readJson(p) {
1616
2187
  try {
1617
2188
  return JSON.parse(fs2.readFileSync(p, "utf8"));
@@ -1668,7 +2239,7 @@ function findLockfile(startDir) {
1668
2239
  for (const n of names) {
1669
2240
  const p = path2.join(dir, n);
1670
2241
  const mt = statMtimeMs(p);
1671
- if (mt != null) return { path: p, mtimeMs: mt };
2242
+ if (mt !== null) return { path: p, mtimeMs: mt };
1672
2243
  }
1673
2244
  const parent = path2.dirname(dir);
1674
2245
  if (parent === dir) break;
@@ -1693,22 +2264,22 @@ async function isStale(params) {
1693
2264
  const chain = collectTsconfigChain(tsconfigAbs);
1694
2265
  for (const cfg of chain) {
1695
2266
  const mt = statMtimeMs(cfg);
1696
- if (mt == null) return { stale: true, reason: "config-missing", detail: cfg };
2267
+ if (mt === null) return { stale: true, reason: "config-missing", detail: cfg };
1697
2268
  const cachedMt = cache.project.configFiles[cfg];
1698
- if (cachedMt == null || Math.abs(cachedMt - mt) > 1e-4) {
2269
+ if (cachedMt === null || Math.abs(cachedMt - mt) > 1e-4) {
1699
2270
  return { stale: true, reason: "config-updated", detail: cfg };
1700
2271
  }
1701
2272
  }
1702
2273
  if (cache.project.lockfile?.path) {
1703
2274
  const mt = statMtimeMs(cache.project.lockfile.path);
1704
- if (mt == null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
2275
+ if (mt === null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
1705
2276
  if (Math.abs(cache.project.lockfile.mtimeMs - mt) > 1e-4) {
1706
2277
  return { stale: true, reason: "lockfile-updated", detail: cache.project.lockfile.path };
1707
2278
  }
1708
2279
  }
1709
2280
  for (const [file, cachedMt] of Object.entries(cache.inputs)) {
1710
2281
  const mt = statMtimeMs(file);
1711
- if (mt == null) return { stale: true, reason: "input-missing", detail: file };
2282
+ if (mt === null) return { stale: true, reason: "input-missing", detail: file };
1712
2283
  if (Math.abs(cachedMt - mt) > 1e-4) return { stale: true, reason: "input-updated", detail: file };
1713
2284
  }
1714
2285
  return { stale: false, reason: "up-to-date" };
@@ -1717,7 +2288,7 @@ async function isStale(params) {
1717
2288
  // src/compiler/cache/writeCache.ts
1718
2289
  import fs3 from "fs";
1719
2290
  import path3 from "path";
1720
- import ts8 from "typescript";
2291
+ import ts11 from "typescript";
1721
2292
  function statMtimeMs2(p) {
1722
2293
  return fs3.statSync(p).mtimeMs;
1723
2294
  }
@@ -1750,7 +2321,7 @@ function writeCache(params) {
1750
2321
  generator: {
1751
2322
  name: "adorn-api",
1752
2323
  version: params.adornVersion,
1753
- typescript: ts8.version
2324
+ typescript: ts11.version
1754
2325
  },
1755
2326
  project: {
1756
2327
  tsconfigPath: params.tsconfigAbs,
@@ -1763,7 +2334,7 @@ function writeCache(params) {
1763
2334
  }
1764
2335
 
1765
2336
  // src/cli.ts
1766
- import ts9 from "typescript";
2337
+ import ts12 from "typescript";
1767
2338
  import process from "process";
1768
2339
  var ADORN_VERSION = "0.1.0";
1769
2340
  function log(msg) {
@@ -1811,7 +2382,7 @@ async function buildCommand(args) {
1811
2382
  outDir: outputDir,
1812
2383
  project: projectPath,
1813
2384
  adornVersion: ADORN_VERSION,
1814
- typescriptVersion: ts9.version
2385
+ typescriptVersion: ts12.version
1815
2386
  });
1816
2387
  if (!stale.stale) {
1817
2388
  log("adorn-api: artifacts up-to-date");