adorn-api 1.0.12 → 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 (43) hide show
  1. package/dist/adapter/express/bootstrap.d.ts.map +1 -1
  2. package/dist/adapter/express/merge.d.ts.map +1 -1
  3. package/dist/adapter/express/router.d.ts.map +1 -1
  4. package/dist/cli.cjs +799 -409
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.js +799 -409
  7. package/dist/cli.js.map +1 -1
  8. package/dist/compiler/cache/isStale.d.ts.map +1 -1
  9. package/dist/compiler/cache/writeCache.d.ts.map +1 -1
  10. package/dist/compiler/schema/intersectionHandler.d.ts +7 -0
  11. package/dist/compiler/schema/intersectionHandler.d.ts.map +1 -0
  12. package/dist/compiler/schema/objectHandler.d.ts +20 -0
  13. package/dist/compiler/schema/objectHandler.d.ts.map +1 -0
  14. package/dist/compiler/schema/openapi.d.ts +1 -1
  15. package/dist/compiler/schema/openapi.d.ts.map +1 -1
  16. package/dist/compiler/schema/parameters.d.ts +18 -0
  17. package/dist/compiler/schema/parameters.d.ts.map +1 -0
  18. package/dist/compiler/schema/primitives.d.ts +10 -0
  19. package/dist/compiler/schema/primitives.d.ts.map +1 -0
  20. package/dist/compiler/schema/typeToJsonSchema.d.ts +2 -51
  21. package/dist/compiler/schema/typeToJsonSchema.d.ts.map +1 -1
  22. package/dist/compiler/schema/types.d.ts +54 -0
  23. package/dist/compiler/schema/types.d.ts.map +1 -0
  24. package/dist/compiler/schema/unionHandler.d.ts +10 -0
  25. package/dist/compiler/schema/unionHandler.d.ts.map +1 -0
  26. package/dist/express.cjs +41 -44
  27. package/dist/express.cjs.map +1 -1
  28. package/dist/express.js +41 -44
  29. package/dist/express.js.map +1 -1
  30. package/dist/http.d.ts +1 -2
  31. package/dist/http.d.ts.map +1 -1
  32. package/dist/index.cjs +1 -1
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/metal/index.cjs +2 -2
  37. package/dist/metal/index.cjs.map +1 -1
  38. package/dist/metal/index.js +2 -2
  39. package/dist/metal/index.js.map +1 -1
  40. package/dist/metal/listQuery.d.ts.map +1 -1
  41. package/dist/metal/queryOptions.d.ts.map +1 -1
  42. package/dist/metal/registerMetalEntities.d.ts.map +1 -1
  43. 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,14 +533,187 @@ 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") {
711
+ const isMetalOrmGeneric = METAL_ORM_WRAPPER_NAMES.some(
712
+ (name) => typeName === name || typeName.endsWith("Api")
713
+ );
714
+ if (isMetalOrmGeneric) {
715
+ return {};
716
+ }
596
717
  if (components.has(typeName)) {
597
718
  return { $ref: `#/components/schemas/${typeName}` };
598
719
  }
@@ -613,40 +734,7 @@ function handleObjectType(type, ctx, typeNode) {
613
734
  typeStack.delete(type);
614
735
  return schema;
615
736
  }
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) {
737
+ function buildObjectSchema(type, ctx, _typeNode) {
650
738
  const { checker, mode } = ctx;
651
739
  const properties = {};
652
740
  const required = [];
@@ -660,9 +748,10 @@ function buildObjectSchema(type, ctx, typeNode) {
660
748
  if (isMethodLike(propType)) {
661
749
  continue;
662
750
  }
663
- const isOptional = !!(prop.flags & ts3.SymbolFlags.Optional);
751
+ const isOptional = !!(prop.flags & ts6.SymbolFlags.Optional);
664
752
  const isRelation = isMetalOrmWrapperType(propType, checker);
665
- properties[propName] = typeToJsonSchema(propType, ctx);
753
+ const propCtx = { ...ctx, propertyName: propName };
754
+ properties[propName] = typeToJsonSchema(propType, propCtx);
666
755
  const shouldRequire = mode === "response" ? !isRelation && !isOptional : !isOptional;
667
756
  if (shouldRequire) {
668
757
  required.push(propName);
@@ -683,14 +772,14 @@ function buildObjectSchema(type, ctx, typeNode) {
683
772
  }
684
773
  return schema;
685
774
  }
686
- function isRecordType(type, checker) {
775
+ function isRecordType(type, _checker) {
687
776
  const symbol = type.getSymbol();
688
777
  if (!symbol) return false;
689
778
  const name = symbol.getName();
690
779
  if (name === "Record") return true;
691
780
  return false;
692
781
  }
693
- function getRecordValueType(type, checker) {
782
+ function getRecordValueType(type, _checker) {
694
783
  const symbol = type.getSymbol();
695
784
  if (!symbol) return null;
696
785
  const name = symbol.getName();
@@ -703,24 +792,8 @@ function getRecordValueType(type, checker) {
703
792
  }
704
793
  return null;
705
794
  }
706
- var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
707
795
  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;
796
+ return !!findMetalOrmWrapper(type, checker);
724
797
  }
725
798
  function isMethodLike(type) {
726
799
  const callSigs = type.getCallSignatures?.();
@@ -729,6 +802,69 @@ function isMethodLike(type) {
729
802
  function isIteratorOrSymbolProperty(propName) {
730
803
  return propName.startsWith("__@") || propName.startsWith("[") || propName === Symbol.iterator.toString();
731
804
  }
805
+ function getTypeNameFromNode(typeNode, _ctx) {
806
+ const explicitName = getExplicitTypeNameFromNode4(typeNode);
807
+ if (explicitName) return explicitName;
808
+ return "Anonymous_${ctx.typeNameStack.length}";
809
+ }
810
+ function getExplicitTypeNameFromNode4(typeNode) {
811
+ if (!typeNode) return null;
812
+ if (ts6.isTypeReferenceNode(typeNode)) {
813
+ if (ts6.isIdentifier(typeNode.typeName)) {
814
+ return typeNode.typeName.text;
815
+ }
816
+ }
817
+ if (ts6.isTypeAliasDeclaration(typeNode.parent)) {
818
+ if (ts6.isIdentifier(typeNode.parent.name)) {
819
+ return typeNode.parent.name.text;
820
+ }
821
+ }
822
+ return null;
823
+ }
824
+ var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
825
+ function findMetalOrmWrapper(type, checker) {
826
+ if (type.isIntersection()) {
827
+ let wrapperInfo = null;
828
+ let hasReadonlyArray = false;
829
+ for (const constituent of type.types) {
830
+ const result = findWrapperInType(constituent, checker);
831
+ if (result) {
832
+ wrapperInfo = result;
833
+ }
834
+ if (!(constituent.flags & ts6.TypeFlags.Object)) continue;
835
+ const symbol = constituent.getSymbol();
836
+ if (symbol?.getName() === "ReadonlyArray") {
837
+ hasReadonlyArray = true;
838
+ }
839
+ }
840
+ if (wrapperInfo) {
841
+ return { ...wrapperInfo, isReadonlyArray: hasReadonlyArray };
842
+ }
843
+ return null;
844
+ }
845
+ return findWrapperInType(type, checker);
846
+ }
847
+ function findWrapperInType(type, checker) {
848
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
849
+ const symbol = type.getSymbol();
850
+ const effectiveSymbol = aliasSymbol && aliasSymbol.flags & ts6.SymbolFlags.Alias ? checker.getAliasedSymbol(aliasSymbol) : symbol;
851
+ if (!effectiveSymbol) return null;
852
+ const name = effectiveSymbol.getName();
853
+ if (!METAL_ORM_WRAPPER_NAMES.includes(name)) return null;
854
+ const typeRef = type;
855
+ const typeArgs = typeRef.typeArguments || [];
856
+ return {
857
+ wrapperName: name,
858
+ targetTypeArgs: typeArgs,
859
+ isReadonlyArray: false
860
+ };
861
+ }
862
+ function getWrapperTypeName(type, _checker) {
863
+ const symbol = type.getSymbol();
864
+ if (!symbol) return null;
865
+ const name = symbol.getName();
866
+ return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
867
+ }
732
868
  function handleMetalOrmWrapper(type, ctx) {
733
869
  const typeRef = type;
734
870
  const typeArgs = typeRef.typeArguments;
@@ -747,30 +883,138 @@ function handleMetalOrmWrapper(type, ctx) {
747
883
  "x-metal-orm-rel": wrapperRel
748
884
  };
749
885
  }
750
- const targetSchema = targetType ? typeToJsonSchema(targetType, ctx) : {};
886
+ if (!targetType) {
887
+ return { "x-metal-orm-rel": wrapperRel };
888
+ }
889
+ if (wrapperName === "BelongsToReference" || wrapperName === "HasOneReference") {
890
+ return handleBelongsToReference(targetType, ctx, wrapperRel);
891
+ }
892
+ const targetSchema = typeToJsonSchema(targetType, ctx);
751
893
  return {
752
894
  ...targetSchema,
753
895
  "x-metal-orm-rel": wrapperRel
754
896
  };
755
897
  }
898
+ function handleBelongsToReference(targetType, ctx, wrapperRel) {
899
+ const { components } = ctx;
900
+ const targetSymbol = targetType.getSymbol();
901
+ const typeName = targetSymbol?.getName();
902
+ if (!typeName) {
903
+ return {
904
+ type: "object",
905
+ properties: {},
906
+ "x-metal-orm-rel": wrapperRel
907
+ };
908
+ }
909
+ const refSchemaName = `${typeName}Ref`;
910
+ if (components.has(refSchemaName)) {
911
+ return {
912
+ $ref: `#/components/schemas/${refSchemaName}`,
913
+ "x-metal-orm-rel": wrapperRel
914
+ };
915
+ }
916
+ const refSchema = buildRefSchema(targetType, ctx);
917
+ components.set(refSchemaName, refSchema);
918
+ return {
919
+ $ref: `#/components/schemas/${refSchemaName}`,
920
+ "x-metal-orm-rel": wrapperRel
921
+ };
922
+ }
923
+ function buildRefSchema(type, ctx) {
924
+ const { checker } = ctx;
925
+ if (!(type.flags & ts6.TypeFlags.Object)) {
926
+ return { type: "object", properties: {} };
927
+ }
928
+ const objectType = type;
929
+ const properties = {};
930
+ const required = [];
931
+ const props = checker.getPropertiesOfType(objectType);
932
+ for (const prop of props) {
933
+ const propName = prop.getName();
934
+ if (isIteratorOrSymbolProperty(propName)) {
935
+ continue;
936
+ }
937
+ const propType = checker.getTypeOfSymbol(prop);
938
+ if (isMethodLike(propType)) {
939
+ continue;
940
+ }
941
+ const isOptional = !!(prop.flags & ts6.SymbolFlags.Optional);
942
+ const isRelation = isMetalOrmWrapperType(propType, checker);
943
+ if (isRelation) {
944
+ continue;
945
+ }
946
+ const propCtx = { ...ctx, propertyName: propName };
947
+ properties[propName] = typeToJsonSchema(propType, propCtx);
948
+ if (!isOptional) {
949
+ required.push(propName);
950
+ }
951
+ }
952
+ const schema = {
953
+ type: "object",
954
+ properties
955
+ };
956
+ if (required.length > 0) {
957
+ schema.required = required;
958
+ }
959
+ return schema;
960
+ }
961
+
962
+ // src/compiler/schema/typeToJsonSchema.ts
963
+ function typeToJsonSchema(type, ctx, typeNode) {
964
+ const primitiveResult = handlePrimitiveType(type, ctx, typeNode);
965
+ if (primitiveResult) {
966
+ return primitiveResult;
967
+ }
968
+ if (type.isUnion()) {
969
+ return handleUnion(type, ctx, typeNode);
970
+ }
971
+ if (type.isIntersection()) {
972
+ return handleIntersection(type, ctx, typeNode);
973
+ }
974
+ if (ctx.checker.isArrayType(type)) {
975
+ const typeArgs = type.typeArguments;
976
+ const itemType = typeArgs?.[0];
977
+ const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
978
+ return {
979
+ type: "array",
980
+ items,
981
+ uniqueItems: isSetType(type, ctx.checker) ? true : void 0
982
+ };
983
+ }
984
+ if (type.flags & ts7.TypeFlags.Object) {
985
+ const objectType = type;
986
+ if (isMetalOrmWrapperType(type, ctx.checker)) {
987
+ return handleMetalOrmWrapper(objectType, ctx);
988
+ }
989
+ return handleObjectType(objectType, ctx, typeNode);
990
+ }
991
+ return {};
992
+ }
993
+ function isSetType(type, _checker) {
994
+ const symbol = type.getSymbol();
995
+ if (!symbol) return false;
996
+ const name = symbol.getName();
997
+ if (name === "Set") return true;
998
+ return false;
999
+ }
756
1000
 
757
1001
  // src/compiler/schema/extractAnnotations.ts
758
- import ts4 from "typescript";
1002
+ import ts8 from "typescript";
759
1003
  function extractPropertySchemaFragments(checker, prop) {
760
- if (!ts4.canHaveDecorators(prop)) return [];
761
- const decs = ts4.getDecorators(prop);
1004
+ if (!ts8.canHaveDecorators(prop)) return [];
1005
+ const decs = ts8.getDecorators(prop);
762
1006
  if (!decs || decs.length === 0) return [];
763
1007
  const frags = [];
764
1008
  for (const d of decs) {
765
1009
  const expr = d.expression;
766
1010
  let callee;
767
1011
  let args;
768
- if (ts4.isCallExpression(expr)) {
1012
+ if (ts8.isCallExpression(expr)) {
769
1013
  callee = expr.expression;
770
1014
  args = expr.arguments;
771
1015
  } else {
772
1016
  callee = expr;
773
- args = ts4.factory.createNodeArray([]);
1017
+ args = ts8.factory.createNodeArray([]);
774
1018
  }
775
1019
  const sym = checker.getSymbolAtLocation(callee);
776
1020
  if (!sym) continue;
@@ -779,7 +1023,7 @@ function extractPropertySchemaFragments(checker, prop) {
779
1023
  const name = resolved.name;
780
1024
  if (name === "Schema") {
781
1025
  const obj = args[0];
782
- if (obj && ts4.isObjectLiteralExpression(obj)) {
1026
+ if (obj && ts8.isObjectLiteralExpression(obj)) {
783
1027
  const frag = objectLiteralToJson(obj);
784
1028
  if (frag) frags.push(frag);
785
1029
  }
@@ -801,7 +1045,7 @@ function extractPropertySchemaFragments(checker, prop) {
801
1045
  frags.push({ format: args[0].text });
802
1046
  } else if (name === "Pattern") {
803
1047
  const arg = args[0];
804
- if (arg && ts4.isRegularExpressionLiteral(arg)) {
1048
+ if (arg && ts8.isRegularExpressionLiteral(arg)) {
805
1049
  frags.push({ pattern: extractRegexPattern(arg.text) });
806
1050
  } else if (isStringLiteral(arg)) {
807
1051
  frags.push({ pattern: arg.text });
@@ -818,11 +1062,11 @@ function extractPropertySchemaFragments(checker, prop) {
818
1062
  frags.push({ multipleOf: Number(args[0].text) });
819
1063
  } else if (name === "Example") {
820
1064
  frags.push({ example: literalToJson(args[0]) });
821
- } else if (name === "Examples" && ts4.isArrayLiteralExpression(args[0])) {
1065
+ } else if (name === "Examples" && ts8.isArrayLiteralExpression(args[0])) {
822
1066
  frags.push({ examples: args[0].elements.map((e) => literalToJson(e)) });
823
1067
  } else if (name === "Description" && isStringLiteral(args[0])) {
824
1068
  frags.push({ description: args[0].text });
825
- } else if (name === "Enum" && ts4.isArrayLiteralExpression(args[0])) {
1069
+ } else if (name === "Enum" && ts8.isArrayLiteralExpression(args[0])) {
826
1070
  frags.push({ enum: args[0].elements.map((e) => literalToJson(e)) });
827
1071
  } else if (name === "Const") {
828
1072
  frags.push({ const: literalToJson(args[0]) });
@@ -830,9 +1074,9 @@ function extractPropertySchemaFragments(checker, prop) {
830
1074
  frags.push({ default: literalToJson(args[0]) });
831
1075
  } else if (name === "AdditionalProperties") {
832
1076
  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)) {
1077
+ if (arg && (arg.kind === ts8.SyntaxKind.FalseKeyword || arg.kind === ts8.SyntaxKind.TrueKeyword)) {
1078
+ frags.push({ additionalProperties: arg.kind === ts8.SyntaxKind.TrueKeyword });
1079
+ } else if (arg && ts8.isObjectLiteralExpression(arg)) {
836
1080
  const obj = objectLiteralToJson(arg);
837
1081
  if (obj) frags.push({ additionalProperties: obj });
838
1082
  }
@@ -845,7 +1089,7 @@ function extractPropertySchemaFragments(checker, prop) {
845
1089
  return frags;
846
1090
  }
847
1091
  function resolveImportedDecorator(checker, sym) {
848
- const target = sym.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
1092
+ const target = sym.flags & ts8.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
849
1093
  const name = target.getName();
850
1094
  const decl = target.declarations?.[0];
851
1095
  if (!decl) return null;
@@ -856,10 +1100,10 @@ function resolveImportedDecorator(checker, sym) {
856
1100
  return null;
857
1101
  }
858
1102
  function isNumberLiteral(node) {
859
- return !!node && ts4.isNumericLiteral(node);
1103
+ return !!node && ts8.isNumericLiteral(node);
860
1104
  }
861
1105
  function isStringLiteral(node) {
862
- return !!node && ts4.isStringLiteral(node);
1106
+ return !!node && ts8.isStringLiteral(node);
863
1107
  }
864
1108
  function extractRegexPattern(text) {
865
1109
  const match = text.match(/^\/(.+)\/[gimsuy]*$/);
@@ -868,12 +1112,12 @@ function extractRegexPattern(text) {
868
1112
  function objectLiteralToJson(obj) {
869
1113
  const out = {};
870
1114
  for (const prop of obj.properties) {
871
- if (!ts4.isPropertyAssignment(prop)) continue;
1115
+ if (!ts8.isPropertyAssignment(prop)) continue;
872
1116
  const name = prop.name;
873
1117
  let key;
874
- if (ts4.isIdentifier(name)) {
1118
+ if (ts8.isIdentifier(name)) {
875
1119
  key = name.text;
876
- } else if (ts4.isStringLiteral(name)) {
1120
+ } else if (ts8.isStringLiteral(name)) {
877
1121
  key = name.text;
878
1122
  } else {
879
1123
  continue;
@@ -883,13 +1127,13 @@ function objectLiteralToJson(obj) {
883
1127
  return out;
884
1128
  }
885
1129
  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));
1130
+ if (ts8.isStringLiteral(node)) return node.text;
1131
+ if (ts8.isNumericLiteral(node)) return Number(node.text);
1132
+ if (node.kind === ts8.SyntaxKind.TrueKeyword) return true;
1133
+ if (node.kind === ts8.SyntaxKind.FalseKeyword) return false;
1134
+ if (node.kind === ts8.SyntaxKind.NullKeyword) return null;
1135
+ if (ts8.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
1136
+ if (ts8.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
893
1137
  return void 0;
894
1138
  }
895
1139
  function mergeFragments(base, ...frags) {
@@ -900,120 +1144,7 @@ function mergeFragments(base, ...frags) {
900
1144
  return result;
901
1145
  }
902
1146
 
903
- // src/compiler/schema/openapi.ts
904
- function generateOpenAPI(controllers, checker, options = {}) {
905
- const components = /* @__PURE__ */ new Map();
906
- const ctx = {
907
- checker,
908
- components,
909
- typeStack: /* @__PURE__ */ new Set(),
910
- typeNameStack: [],
911
- mode: "response"
912
- };
913
- const paths = {};
914
- for (const controller of controllers) {
915
- for (const operation of controller.operations) {
916
- const fullPath = convertToOpenApiPath(controller.basePath, operation.path);
917
- if (!paths[fullPath]) {
918
- paths[fullPath] = {};
919
- }
920
- const method = operation.httpMethod.toLowerCase();
921
- paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
922
- }
923
- }
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
- }
936
- function convertToOpenApiPath(basePath, path4) {
937
- const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
938
- const converted = path4.replace(/:([^/]+)/g, "{$1}");
939
- let fullPath = base + converted || "/";
940
- if (fullPath.endsWith("/") && fullPath !== "/") {
941
- fullPath = fullPath.slice(0, -1);
942
- }
943
- return fullPath;
944
- }
945
- function buildOperation(operation, ctx, controllerConsumes) {
946
- const op = {
947
- operationId: operation.operationId,
948
- responses: {}
949
- };
950
- const parameters = [];
951
- buildPathParameters(operation, ctx, parameters);
952
- buildQueryParameters(operation, ctx, parameters);
953
- buildHeaderParameters(operation, ctx, parameters);
954
- buildCookieParameters(operation, ctx, parameters);
955
- if (parameters.length > 0) {
956
- op.parameters = parameters;
957
- }
958
- const responseCtx = { ...ctx, mode: "response" };
959
- const responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
960
- const status = operation.httpMethod === "POST" ? 201 : 200;
961
- op.responses[status] = {
962
- description: status === 201 ? "Created" : "OK",
963
- content: {
964
- "application/json": {
965
- schema: responseSchema
966
- }
967
- }
968
- };
969
- if (["POST", "PUT", "PATCH"].includes(operation.httpMethod) && operation.bodyParamIndex !== null) {
970
- const bodyParam = operation.parameters[operation.bodyParamIndex];
971
- if (bodyParam) {
972
- const requestCtx = { ...ctx, mode: "request" };
973
- let bodySchema = typeToJsonSchema(bodyParam.type, requestCtx);
974
- bodySchema = mergeBodySchemaAnnotations(bodyParam, requestCtx, bodySchema);
975
- const contentType = operation.bodyContentType ?? controllerConsumes?.[0] ?? "application/json";
976
- const requestBody = {
977
- required: !bodyParam.isOptional,
978
- content: {}
979
- };
980
- if (contentType === "multipart/form-data") {
981
- requestBody.content["multipart/form-data"] = {
982
- schema: bodySchema
983
- };
984
- } else {
985
- requestBody.content[contentType] = {
986
- schema: bodySchema
987
- };
988
- }
989
- op.requestBody = requestBody;
990
- }
991
- }
992
- return op;
993
- }
994
- function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
995
- if (!schema.properties) return schema;
996
- const typeSymbol = bodyParam.type.getSymbol();
997
- if (!typeSymbol) return schema;
998
- const declarations = typeSymbol.getDeclarations();
999
- if (!declarations || declarations.length === 0) return schema;
1000
- const classDecl = declarations[0];
1001
- if (!ts5.isClassDeclaration(classDecl)) return schema;
1002
- const result = { ...schema };
1003
- const props = { ...result.properties };
1004
- 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;
1007
- if (!propName) continue;
1008
- if (!props[propName]) continue;
1009
- const frags = extractPropertySchemaFragments(ctx.checker, member);
1010
- if (frags.length > 0) {
1011
- props[propName] = mergeFragments(props[propName], ...frags);
1012
- }
1013
- }
1014
- result.properties = props;
1015
- return result;
1016
- }
1147
+ // src/compiler/schema/parameters.ts
1017
1148
  function buildPathParameters(operation, ctx, parameters) {
1018
1149
  for (const paramIndex of operation.pathParamIndices) {
1019
1150
  const param = operation.parameters[paramIndex];
@@ -1026,71 +1157,22 @@ function buildPathParameters(operation, ctx, parameters) {
1026
1157
  }
1027
1158
  }
1028
1159
  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);
1160
+ const paramName = param.name.toLowerCase();
1161
+ const isIdParam = paramName === "id" || paramName.endsWith("id");
1162
+ if (!schema.$ref && schema.type === "number" && isIdParam) {
1163
+ schema.type = "integer";
1164
+ if (!schema.minimum) {
1165
+ schema.minimum = 1;
1083
1166
  }
1084
1167
  }
1168
+ parameters.push({
1169
+ name: param.name,
1170
+ in: "path",
1171
+ required: !param.isOptional,
1172
+ schema
1173
+ });
1085
1174
  }
1086
- if (current.allOf) {
1087
- for (const branch of current.allOf) {
1088
- processSchema(branch);
1089
- }
1090
- }
1091
- };
1092
- processSchema(resolved);
1093
- return { properties, required };
1175
+ }
1094
1176
  }
1095
1177
  function buildQueryParameters(operation, ctx, parameters) {
1096
1178
  if (operation.queryObjectParamIndex !== null) {
@@ -1099,20 +1181,22 @@ function buildQueryParameters(operation, ctx, parameters) {
1099
1181
  const querySchema = typeToJsonSchema(queryParam.type, ctx);
1100
1182
  const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
1101
1183
  for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1102
- const isRequired = queryRequired.includes(propName) ?? false;
1184
+ const isRequired = queryRequired.includes(propName);
1103
1185
  const isObjectLike = isObjectLikeSchema(propSchema, ctx);
1104
1186
  const serialization = determineQuerySerialization(propSchema.type);
1187
+ const exampleValue = generateExampleValue(propSchema, propName);
1105
1188
  if (isObjectLike) {
1189
+ const schemaRef = propSchema.$ref || "#/components/schemas/InlineQueryParam";
1106
1190
  parameters.push({
1107
1191
  name: propName,
1108
1192
  in: "query",
1109
1193
  required: isRequired,
1110
- content: {
1111
- "application/json": {
1112
- schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1113
- }
1194
+ schema: { type: "string" },
1195
+ description: `JSON-encoded object. ${exampleValue}`,
1196
+ examples: {
1197
+ default: { value: parseExampleValue(exampleValue) }
1114
1198
  },
1115
- description: `URL-encoded JSON. Example: ${propName}=${encodeURIComponent(JSON.stringify({ example: "value" }))}`
1199
+ "x-adorn-jsonSchemaRef": schemaRef
1116
1200
  });
1117
1201
  } else {
1118
1202
  const paramDef = {
@@ -1158,15 +1242,18 @@ function buildQueryParameters(operation, ctx, parameters) {
1158
1242
  }
1159
1243
  const isObjectLike = isObjectLikeSchema(paramSchema, ctx);
1160
1244
  if (isObjectLike) {
1245
+ const schemaRef = paramSchema.$ref || "#/components/schemas/InlineQueryParam";
1246
+ const exampleValue = generateExampleValue(paramSchema, param.name);
1161
1247
  parameters.push({
1162
1248
  name: param.name,
1163
1249
  in: "query",
1164
1250
  required: !param.isOptional,
1165
- content: {
1166
- "application/json": {
1167
- schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema
1168
- }
1169
- }
1251
+ schema: { type: "string" },
1252
+ description: `JSON-encoded object. ${exampleValue}`,
1253
+ examples: {
1254
+ default: { value: parseExampleValue(exampleValue) }
1255
+ },
1256
+ "x-adorn-jsonSchemaRef": schemaRef
1170
1257
  });
1171
1258
  } else {
1172
1259
  const serialization = determineQuerySerialization(paramSchema.type);
@@ -1181,14 +1268,6 @@ function buildQueryParameters(operation, ctx, parameters) {
1181
1268
  }
1182
1269
  }
1183
1270
  }
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
1271
  function buildHeaderParameters(operation, ctx, parameters) {
1193
1272
  if (operation.headerObjectParamIndex === null) return;
1194
1273
  const headerParam = operation.parameters[operation.headerObjectParamIndex];
@@ -1225,9 +1304,324 @@ function buildCookieParameters(operation, ctx, parameters) {
1225
1304
  });
1226
1305
  }
1227
1306
  }
1307
+ function determineQuerySerialization(schemaType) {
1308
+ const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
1309
+ const isArray = typeArray.includes("array");
1310
+ if (isArray) {
1311
+ return { style: "form", explode: true };
1312
+ }
1313
+ return {};
1314
+ }
1315
+ function generateExampleValue(schema, propName) {
1316
+ const resolved = resolveSchemaRef(schema, /* @__PURE__ */ new Map());
1317
+ if (resolved.type === "object" && resolved.properties) {
1318
+ const example = {};
1319
+ for (const [key, prop] of Object.entries(resolved.properties)) {
1320
+ const propResolved = resolveSchemaRef(prop, /* @__PURE__ */ new Map());
1321
+ if (propResolved.type === "string") {
1322
+ example[key] = "value";
1323
+ } else if (propResolved.type === "number" || propResolved.type === "integer") {
1324
+ example[key] = 1;
1325
+ } else if (propResolved.type === "boolean") {
1326
+ example[key] = true;
1327
+ } else if (Array.isArray(propResolved.type) && propResolved.type.includes("null")) {
1328
+ example[key] = null;
1329
+ } else if (propResolved.enum) {
1330
+ example[key] = propResolved.enum[0];
1331
+ } else {
1332
+ example[key] = "value";
1333
+ }
1334
+ }
1335
+ return `Example: ${propName}=${JSON.stringify(example)}`;
1336
+ }
1337
+ return `Example: ${propName}=${JSON.stringify({ key: "value" })}`;
1338
+ }
1339
+ function parseExampleValue(description) {
1340
+ const match = description.match(/Example:\s*\w+=(\{[^}]+\})/);
1341
+ if (match) {
1342
+ return match[1];
1343
+ }
1344
+ return JSON.stringify({ key: "value" });
1345
+ }
1346
+ function isObjectLikeSchema(schema, ctx) {
1347
+ const resolved = resolveSchemaRef(schema, ctx.components);
1348
+ if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1349
+ return true;
1350
+ }
1351
+ if (resolved.allOf) {
1352
+ for (const branch of resolved.allOf) {
1353
+ if (isObjectLikeSchema(branch, ctx)) {
1354
+ return true;
1355
+ }
1356
+ }
1357
+ }
1358
+ if (resolved.type === "array" && resolved.items) {
1359
+ const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
1360
+ return isObjectLikeSchema(itemsSchema, ctx);
1361
+ }
1362
+ return false;
1363
+ }
1364
+ function resolveSchemaRef(schema, components) {
1365
+ const ref = schema.$ref;
1366
+ if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
1367
+ return schema;
1368
+ }
1369
+ const name = ref.replace("#/components/schemas/", "");
1370
+ const next = components.get(name);
1371
+ if (!next) return schema;
1372
+ return resolveSchemaRef(next, components);
1373
+ }
1374
+ function resolveAndCollectObjectProps(schema, components) {
1375
+ const resolved = resolveSchemaRef(schema, components);
1376
+ const properties = {};
1377
+ const required = [];
1378
+ const processSchema = (s) => {
1379
+ const current = resolveSchemaRef(s, components);
1380
+ if (current.properties) {
1381
+ for (const [key, val] of Object.entries(current.properties)) {
1382
+ if (!properties[key]) {
1383
+ properties[key] = val;
1384
+ }
1385
+ }
1386
+ }
1387
+ if (current.required) {
1388
+ for (const req of current.required) {
1389
+ if (!required.includes(req)) {
1390
+ required.push(req);
1391
+ }
1392
+ }
1393
+ }
1394
+ if (current.allOf) {
1395
+ for (const branch of current.allOf) {
1396
+ processSchema(branch);
1397
+ }
1398
+ }
1399
+ };
1400
+ processSchema(resolved);
1401
+ return { properties, required };
1402
+ }
1403
+
1404
+ // src/compiler/schema/openapi.ts
1405
+ var METAL_ORM_WRAPPER_NAMES2 = ["BelongsToReference", "HasOneReference", "HasManyCollection", "ManyToManyCollection"];
1406
+ function generateOpenAPI(controllers, checker, options = {}) {
1407
+ const components = /* @__PURE__ */ new Map();
1408
+ const ctx = {
1409
+ checker,
1410
+ components,
1411
+ typeStack: /* @__PURE__ */ new Set(),
1412
+ typeNameStack: [],
1413
+ mode: "response"
1414
+ };
1415
+ const paths = {};
1416
+ for (const controller of controllers) {
1417
+ for (const operation of controller.operations) {
1418
+ const fullPath = convertToOpenApiPath(controller.basePath, operation.path);
1419
+ if (!paths[fullPath]) {
1420
+ paths[fullPath] = {};
1421
+ }
1422
+ const method = operation.httpMethod.toLowerCase();
1423
+ paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
1424
+ }
1425
+ }
1426
+ const schemas = Object.fromEntries(components);
1427
+ cleanupMetalOrmWrappers(schemas, paths);
1428
+ return {
1429
+ openapi: "3.1.0",
1430
+ info: {
1431
+ title: options.title ?? "API",
1432
+ version: options.version ?? "1.0.0"
1433
+ },
1434
+ components: {
1435
+ schemas
1436
+ },
1437
+ paths
1438
+ };
1439
+ }
1440
+ function cleanupMetalOrmWrappers(schemas, paths) {
1441
+ const schemasToDelete = /* @__PURE__ */ new Set();
1442
+ for (const wrapperName of METAL_ORM_WRAPPER_NAMES2) {
1443
+ if (schemas[wrapperName]) {
1444
+ schemasToDelete.add(wrapperName);
1445
+ }
1446
+ if (schemas[`${wrapperName}Api`]) {
1447
+ schemasToDelete.add(`${wrapperName}Api`);
1448
+ }
1449
+ }
1450
+ for (const schema of Object.values(schemas)) {
1451
+ cleanupSchemaRefs(schema, schemasToDelete);
1452
+ }
1453
+ for (const pathItem of Object.values(paths)) {
1454
+ cleanupPathItemRefs(pathItem, schemasToDelete);
1455
+ }
1456
+ for (const schemaName of schemasToDelete) {
1457
+ delete schemas[schemaName];
1458
+ }
1459
+ }
1460
+ function cleanupSchemaRefs(schema, schemasToDelete) {
1461
+ if (typeof schema !== "object" || schema === null) {
1462
+ return;
1463
+ }
1464
+ if (schema.properties) {
1465
+ for (const propName of Object.keys(schema.properties)) {
1466
+ const propSchema = schema.properties[propName];
1467
+ if (propSchema.$ref && typeof propSchema.$ref === "string") {
1468
+ const refName = propSchema.$ref.replace("#/components/schemas/", "");
1469
+ if (schemasToDelete.has(refName)) {
1470
+ delete schema.properties[propName];
1471
+ if (schema.required && Array.isArray(schema.required)) {
1472
+ schema.required = schema.required.filter((r) => r !== propName);
1473
+ }
1474
+ }
1475
+ } else {
1476
+ cleanupSchemaRefs(propSchema, schemasToDelete);
1477
+ }
1478
+ }
1479
+ }
1480
+ if (schema.items) {
1481
+ cleanupSchemaRefs(schema.items, schemasToDelete);
1482
+ }
1483
+ if (schema.allOf) {
1484
+ for (const item of schema.allOf) {
1485
+ cleanupSchemaRefs(item, schemasToDelete);
1486
+ }
1487
+ }
1488
+ }
1489
+ function cleanupPathItemRefs(pathItem, schemasToDelete) {
1490
+ if (typeof pathItem !== "object" || pathItem === null) {
1491
+ return;
1492
+ }
1493
+ for (const method of Object.keys(pathItem)) {
1494
+ const operation = pathItem[method];
1495
+ if (typeof operation !== "object" || operation === null) continue;
1496
+ if (operation.requestBody) {
1497
+ cleanupRequestBodyRefs(operation.requestBody, schemasToDelete);
1498
+ }
1499
+ if (operation.responses) {
1500
+ const responses = Object.values(operation.responses);
1501
+ for (const response of responses) {
1502
+ if (response.content) {
1503
+ const contentTypes = Object.values(response.content);
1504
+ for (const contentType of contentTypes) {
1505
+ if (contentType.schema) {
1506
+ cleanupSchemaRefs(contentType.schema, schemasToDelete);
1507
+ }
1508
+ }
1509
+ }
1510
+ }
1511
+ }
1512
+ }
1513
+ }
1514
+ function cleanupRequestBodyRefs(requestBody, schemasToDelete) {
1515
+ if (typeof requestBody !== "object" || requestBody === null) return;
1516
+ if (requestBody.content) {
1517
+ const contentTypes = Object.values(requestBody.content);
1518
+ for (const contentType of contentTypes) {
1519
+ if (contentType.schema) {
1520
+ cleanupSchemaRefs(contentType.schema, schemasToDelete);
1521
+ }
1522
+ }
1523
+ }
1524
+ if (requestBody.properties) {
1525
+ for (const propName of Object.keys(requestBody.properties)) {
1526
+ const propSchema = requestBody.properties[propName];
1527
+ if (propSchema.$ref && typeof propSchema.$ref === "string") {
1528
+ const refName = propSchema.$ref.replace("#/components/schemas/", "");
1529
+ if (schemasToDelete.has(refName)) {
1530
+ delete requestBody.properties[propName];
1531
+ if (requestBody.required && Array.isArray(requestBody.required)) {
1532
+ requestBody.required = requestBody.required.filter((r) => r !== propName);
1533
+ }
1534
+ }
1535
+ } else {
1536
+ cleanupSchemaRefs(propSchema, schemasToDelete);
1537
+ }
1538
+ }
1539
+ }
1540
+ }
1541
+ function convertToOpenApiPath(basePath, path4) {
1542
+ const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
1543
+ const converted = path4.replace(/:([^/]+)/g, "{$1}");
1544
+ let fullPath = base + converted || "/";
1545
+ if (fullPath.endsWith("/") && fullPath !== "/") {
1546
+ fullPath = fullPath.slice(0, -1);
1547
+ }
1548
+ return fullPath;
1549
+ }
1550
+ function buildOperation(operation, ctx, controllerConsumes) {
1551
+ const op = {
1552
+ operationId: operation.operationId,
1553
+ responses: {}
1554
+ };
1555
+ const parameters = [];
1556
+ buildPathParameters(operation, ctx, parameters);
1557
+ buildQueryParameters(operation, ctx, parameters);
1558
+ buildHeaderParameters(operation, ctx, parameters);
1559
+ buildCookieParameters(operation, ctx, parameters);
1560
+ if (parameters.length > 0) {
1561
+ op.parameters = parameters;
1562
+ }
1563
+ const responseCtx = { ...ctx, mode: "response" };
1564
+ const responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
1565
+ const status = operation.httpMethod === "POST" ? 201 : 200;
1566
+ op.responses[status] = {
1567
+ description: status === 201 ? "Created" : "OK",
1568
+ content: {
1569
+ "application/json": {
1570
+ schema: responseSchema
1571
+ }
1572
+ }
1573
+ };
1574
+ if (["POST", "PUT", "PATCH"].includes(operation.httpMethod) && operation.bodyParamIndex !== null) {
1575
+ const bodyParam = operation.parameters[operation.bodyParamIndex];
1576
+ if (bodyParam) {
1577
+ const requestCtx = { ...ctx, mode: "request" };
1578
+ let bodySchema = typeToJsonSchema(bodyParam.type, requestCtx);
1579
+ bodySchema = mergeBodySchemaAnnotations(bodyParam, requestCtx, bodySchema);
1580
+ const contentType = operation.bodyContentType ?? controllerConsumes?.[0] ?? "application/json";
1581
+ const requestBody = {
1582
+ required: !bodyParam.isOptional,
1583
+ content: {}
1584
+ };
1585
+ if (contentType === "multipart/form-data") {
1586
+ requestBody.content["multipart/form-data"] = {
1587
+ schema: bodySchema
1588
+ };
1589
+ } else {
1590
+ requestBody.content[contentType] = {
1591
+ schema: bodySchema
1592
+ };
1593
+ }
1594
+ op.requestBody = requestBody;
1595
+ }
1596
+ }
1597
+ return op;
1598
+ }
1599
+ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
1600
+ if (!schema.properties) return schema;
1601
+ const typeSymbol = bodyParam.type.getSymbol();
1602
+ if (!typeSymbol) return schema;
1603
+ const declarations = typeSymbol.getDeclarations();
1604
+ if (!declarations || declarations.length === 0) return schema;
1605
+ const classDecl = declarations[0];
1606
+ if (!ts9.isClassDeclaration(classDecl)) return schema;
1607
+ const result = { ...schema };
1608
+ const props = { ...result.properties };
1609
+ for (const member of classDecl.members) {
1610
+ if (!ts9.isPropertyDeclaration(member) || !member.name) continue;
1611
+ const propName = ts9.isIdentifier(member.name) ? member.name.text : null;
1612
+ if (!propName) continue;
1613
+ if (!props[propName]) continue;
1614
+ const frags = extractPropertySchemaFragments(ctx.checker, member);
1615
+ if (frags.length > 0) {
1616
+ props[propName] = mergeFragments(props[propName], ...frags);
1617
+ }
1618
+ }
1619
+ result.properties = props;
1620
+ return result;
1621
+ }
1228
1622
 
1229
1623
  // src/compiler/manifest/emit.ts
1230
- import ts6 from "typescript";
1624
+ import ts10 from "typescript";
1231
1625
  function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
1232
1626
  const components = /* @__PURE__ */ new Map();
1233
1627
  const ctx = {
@@ -1249,7 +1643,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
1249
1643
  generator: {
1250
1644
  name: "adorn-api",
1251
1645
  version,
1252
- typescript: ts6.version
1646
+ typescript: ts10.version
1253
1647
  },
1254
1648
  schemas: {
1255
1649
  kind: "openapi-3.1",
@@ -1403,7 +1797,7 @@ function buildQueryArgs(op, ctx, args) {
1403
1797
  args.query.push({
1404
1798
  name: propName,
1405
1799
  index: queryParam.index,
1406
- required: !isRequired,
1800
+ required: isRequired,
1407
1801
  schemaRef,
1408
1802
  schemaType: propSchema.type,
1409
1803
  content: isObjectLike ? "application/json" : void 0
@@ -1442,7 +1836,7 @@ function buildHeaderArgs(op, ctx, args) {
1442
1836
  args.headers.push({
1443
1837
  name: propName,
1444
1838
  index: headerParam.index,
1445
- required: !isRequired,
1839
+ required: isRequired,
1446
1840
  schemaRef,
1447
1841
  schemaType: propSchema.type
1448
1842
  });
@@ -1463,7 +1857,7 @@ function buildCookieArgs(op, ctx, args) {
1463
1857
  args.cookies.push({
1464
1858
  name: propName,
1465
1859
  index: cookieParam.index,
1466
- required: !isRequired,
1860
+ required: isRequired,
1467
1861
  schemaRef,
1468
1862
  schemaType: propSchema.type,
1469
1863
  serialization: { style: "form", explode: true }
@@ -1479,7 +1873,7 @@ import Ajv from "ajv";
1479
1873
  import addFormats from "ajv-formats";
1480
1874
  var OAS_SCHEMA_ONLY = /* @__PURE__ */ new Set(["discriminator", "xml", "externalDocs", "example"]);
1481
1875
  function sanitizeSchemaForAjv(schema) {
1482
- if (schema == null || typeof schema !== "object") return schema;
1876
+ if (schema === null || typeof schema !== "object") return schema;
1483
1877
  if (Array.isArray(schema)) return schema.map(sanitizeSchemaForAjv);
1484
1878
  const out = {};
1485
1879
  for (const [k, v] of Object.entries(schema)) {
@@ -1490,7 +1884,7 @@ function sanitizeSchemaForAjv(schema) {
1490
1884
  return out;
1491
1885
  }
1492
1886
  function rewriteComponentRefs(schema) {
1493
- if (schema == null || typeof schema !== "object") return schema;
1887
+ if (schema === null || typeof schema !== "object") return schema;
1494
1888
  if (Array.isArray(schema)) return schema.map(rewriteComponentRefs);
1495
1889
  if (typeof schema.$ref === "string") {
1496
1890
  const ref = schema.$ref;
@@ -1567,16 +1961,13 @@ async function emitPrecompiledValidators(opts) {
1567
1961
  `;
1568
1962
  cjs += ` body: ${v.body ? `exports[${JSON.stringify(v.body)}]` : "undefined"},
1569
1963
  `;
1570
- cjs += ` response: {
1571
- `;
1964
+ cjs += " response: {\n";
1572
1965
  for (const [key, id] of Object.entries(v.response)) {
1573
1966
  cjs += ` ${JSON.stringify(key)}: exports[${JSON.stringify(id)}],
1574
1967
  `;
1575
1968
  }
1576
- cjs += ` }
1577
- `;
1578
- cjs += ` },
1579
- `;
1969
+ cjs += " }\n";
1970
+ cjs += " },\n";
1580
1971
  }
1581
1972
  cjs += "};\n";
1582
1973
  fs.writeFileSync(cjsPath, cjs, "utf8");
@@ -1611,7 +2002,6 @@ export function validateResponse(operationId, status, contentType, data) {
1611
2002
  // src/compiler/cache/isStale.ts
1612
2003
  import fs2 from "fs";
1613
2004
  import path2 from "path";
1614
- import "typescript";
1615
2005
  function readJson(p) {
1616
2006
  try {
1617
2007
  return JSON.parse(fs2.readFileSync(p, "utf8"));
@@ -1668,7 +2058,7 @@ function findLockfile(startDir) {
1668
2058
  for (const n of names) {
1669
2059
  const p = path2.join(dir, n);
1670
2060
  const mt = statMtimeMs(p);
1671
- if (mt != null) return { path: p, mtimeMs: mt };
2061
+ if (mt !== null) return { path: p, mtimeMs: mt };
1672
2062
  }
1673
2063
  const parent = path2.dirname(dir);
1674
2064
  if (parent === dir) break;
@@ -1693,22 +2083,22 @@ async function isStale(params) {
1693
2083
  const chain = collectTsconfigChain(tsconfigAbs);
1694
2084
  for (const cfg of chain) {
1695
2085
  const mt = statMtimeMs(cfg);
1696
- if (mt == null) return { stale: true, reason: "config-missing", detail: cfg };
2086
+ if (mt === null) return { stale: true, reason: "config-missing", detail: cfg };
1697
2087
  const cachedMt = cache.project.configFiles[cfg];
1698
- if (cachedMt == null || Math.abs(cachedMt - mt) > 1e-4) {
2088
+ if (cachedMt === null || Math.abs(cachedMt - mt) > 1e-4) {
1699
2089
  return { stale: true, reason: "config-updated", detail: cfg };
1700
2090
  }
1701
2091
  }
1702
2092
  if (cache.project.lockfile?.path) {
1703
2093
  const mt = statMtimeMs(cache.project.lockfile.path);
1704
- if (mt == null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
2094
+ if (mt === null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
1705
2095
  if (Math.abs(cache.project.lockfile.mtimeMs - mt) > 1e-4) {
1706
2096
  return { stale: true, reason: "lockfile-updated", detail: cache.project.lockfile.path };
1707
2097
  }
1708
2098
  }
1709
2099
  for (const [file, cachedMt] of Object.entries(cache.inputs)) {
1710
2100
  const mt = statMtimeMs(file);
1711
- if (mt == null) return { stale: true, reason: "input-missing", detail: file };
2101
+ if (mt === null) return { stale: true, reason: "input-missing", detail: file };
1712
2102
  if (Math.abs(cachedMt - mt) > 1e-4) return { stale: true, reason: "input-updated", detail: file };
1713
2103
  }
1714
2104
  return { stale: false, reason: "up-to-date" };
@@ -1717,7 +2107,7 @@ async function isStale(params) {
1717
2107
  // src/compiler/cache/writeCache.ts
1718
2108
  import fs3 from "fs";
1719
2109
  import path3 from "path";
1720
- import ts8 from "typescript";
2110
+ import ts11 from "typescript";
1721
2111
  function statMtimeMs2(p) {
1722
2112
  return fs3.statSync(p).mtimeMs;
1723
2113
  }
@@ -1750,7 +2140,7 @@ function writeCache(params) {
1750
2140
  generator: {
1751
2141
  name: "adorn-api",
1752
2142
  version: params.adornVersion,
1753
- typescript: ts8.version
2143
+ typescript: ts11.version
1754
2144
  },
1755
2145
  project: {
1756
2146
  tsconfigPath: params.tsconfigAbs,
@@ -1763,7 +2153,7 @@ function writeCache(params) {
1763
2153
  }
1764
2154
 
1765
2155
  // src/cli.ts
1766
- import ts9 from "typescript";
2156
+ import ts12 from "typescript";
1767
2157
  import process from "process";
1768
2158
  var ADORN_VERSION = "0.1.0";
1769
2159
  function log(msg) {
@@ -1811,7 +2201,7 @@ async function buildCommand(args) {
1811
2201
  outDir: outputDir,
1812
2202
  project: projectPath,
1813
2203
  adornVersion: ADORN_VERSION,
1814
- typescriptVersion: ts9.version
2204
+ typescriptVersion: ts12.version
1815
2205
  });
1816
2206
  if (!stale.stale) {
1817
2207
  log("adorn-api: artifacts up-to-date");