@arcteninc/core 0.0.47 → 0.0.48
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.
- package/package.json +1 -1
- package/scripts/cli-extract-types-auto.ts +120 -27
package/package.json
CHANGED
|
@@ -572,39 +572,108 @@ function resolveImportPath(
|
|
|
572
572
|
return null;
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Try to resolve an anonymous type to a named type by checking structural compatibility
|
|
577
|
+
* This helps when function parameters use anonymous types that match existing interfaces
|
|
578
|
+
*/
|
|
579
|
+
function tryResolveToNamedType(
|
|
580
|
+
type: ts.Type,
|
|
581
|
+
checker: ts.TypeChecker,
|
|
582
|
+
sourceFile: ts.SourceFile
|
|
583
|
+
): { typeName: string; sourceFile: string } | null {
|
|
584
|
+
// Only try for object types
|
|
585
|
+
if (!(type.flags & ts.TypeFlags.Object)) return null;
|
|
586
|
+
|
|
587
|
+
const props = checker.getPropertiesOfType(type);
|
|
588
|
+
if (props.length === 0) return null;
|
|
589
|
+
|
|
590
|
+
// Get all exported interfaces/types from the same file
|
|
591
|
+
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
|
|
592
|
+
if (!moduleSymbol) return null;
|
|
593
|
+
|
|
594
|
+
const fileSymbols = checker.getExportsOfModule(moduleSymbol);
|
|
595
|
+
if (!fileSymbols || fileSymbols.length === 0) return null;
|
|
596
|
+
|
|
597
|
+
// Check each exported type/interface to see if it matches structurally
|
|
598
|
+
for (const symbol of fileSymbols) {
|
|
599
|
+
if (!symbol) continue;
|
|
600
|
+
|
|
601
|
+
const name = symbol.getName();
|
|
602
|
+
if (!name || name.length === 0) continue;
|
|
603
|
+
|
|
604
|
+
const declarations = symbol.getDeclarations();
|
|
605
|
+
if (!declarations || declarations.length === 0) continue;
|
|
606
|
+
|
|
607
|
+
for (const decl of declarations) {
|
|
608
|
+
if (ts.isInterfaceDeclaration(decl) || ts.isTypeAliasDeclaration(decl)) {
|
|
609
|
+
// Get the type from the declaration
|
|
610
|
+
const declaredType = checker.getTypeAtLocation(decl);
|
|
611
|
+
|
|
612
|
+
// Check if properties match (simple structural check)
|
|
613
|
+
const declaredProps = checker.getPropertiesOfType(declaredType);
|
|
614
|
+
if (declaredProps.length === props.length) {
|
|
615
|
+
// Check if all property names match
|
|
616
|
+
const propNames = new Set(props.map(p => p.getName()));
|
|
617
|
+
const declaredPropNames = new Set(declaredProps.map(p => p.getName()));
|
|
618
|
+
|
|
619
|
+
if (propNames.size === declaredPropNames.size &&
|
|
620
|
+
[...propNames].every(n => declaredPropNames.has(n))) {
|
|
621
|
+
// Properties match - this might be the same type
|
|
622
|
+
return {
|
|
623
|
+
typeName: name,
|
|
624
|
+
sourceFile: sourceFile.fileName,
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
|
|
575
635
|
/**
|
|
576
636
|
* Try to get type name and source file for use with ts-json-schema-generator
|
|
577
637
|
*/
|
|
578
638
|
function getTypeInfoForGenerator(
|
|
579
639
|
type: ts.Type,
|
|
580
|
-
checker: ts.TypeChecker
|
|
640
|
+
checker: ts.TypeChecker,
|
|
641
|
+
paramSourceFile?: ts.SourceFile
|
|
581
642
|
): { typeName: string; sourceFile: string } | null {
|
|
582
643
|
const symbol = type.getSymbol();
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
644
|
+
|
|
645
|
+
// If it's a named type, use it directly
|
|
646
|
+
if (symbol) {
|
|
647
|
+
const name = symbol.getName();
|
|
648
|
+
if (name && name.length > 0) {
|
|
649
|
+
const declarations = symbol.getDeclarations();
|
|
650
|
+
if (declarations && declarations.length > 0) {
|
|
651
|
+
// Find the first declaration that's a type alias, interface, or class
|
|
652
|
+
for (const decl of declarations) {
|
|
653
|
+
if (
|
|
654
|
+
ts.isTypeAliasDeclaration(decl) ||
|
|
655
|
+
ts.isInterfaceDeclaration(decl) ||
|
|
656
|
+
ts.isClassDeclaration(decl)
|
|
657
|
+
) {
|
|
658
|
+
const sourceFile = decl.getSourceFile();
|
|
659
|
+
if (sourceFile) {
|
|
660
|
+
return {
|
|
661
|
+
typeName: name,
|
|
662
|
+
sourceFile: sourceFile.fileName,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
604
667
|
}
|
|
605
668
|
}
|
|
606
669
|
}
|
|
607
|
-
|
|
670
|
+
|
|
671
|
+
// If it's an anonymous type, try to resolve it to a named type
|
|
672
|
+
if (paramSourceFile) {
|
|
673
|
+
const resolved = tryResolveToNamedType(type, checker, paramSourceFile);
|
|
674
|
+
if (resolved) return resolved;
|
|
675
|
+
}
|
|
676
|
+
|
|
608
677
|
return null;
|
|
609
678
|
}
|
|
610
679
|
|
|
@@ -626,7 +695,17 @@ async function serializeTypeWithGenerator(
|
|
|
626
695
|
return null;
|
|
627
696
|
}
|
|
628
697
|
|
|
629
|
-
|
|
698
|
+
// Try to get source file from the type's declarations or use a fallback
|
|
699
|
+
let sourceFileForLookup: ts.SourceFile | undefined;
|
|
700
|
+
const symbol = type.getSymbol();
|
|
701
|
+
if (symbol) {
|
|
702
|
+
const declarations = symbol.getDeclarations();
|
|
703
|
+
if (declarations && declarations.length > 0) {
|
|
704
|
+
sourceFileForLookup = declarations[0].getSourceFile();
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const typeInfo = getTypeInfoForGenerator(type, checker, sourceFileForLookup);
|
|
630
709
|
if (!typeInfo) return null;
|
|
631
710
|
|
|
632
711
|
try {
|
|
@@ -680,13 +759,26 @@ async function serializeType(
|
|
|
680
759
|
checker: ts.TypeChecker,
|
|
681
760
|
program: ts.Program | undefined,
|
|
682
761
|
configPath: string | undefined,
|
|
683
|
-
defs?: Record<string, JsonSchemaProperty
|
|
762
|
+
defs?: Record<string, JsonSchemaProperty>,
|
|
763
|
+
sourceFileForLookup?: ts.SourceFile
|
|
684
764
|
): Promise<{ isOptional: boolean; schema: JsonSchemaProperty }> {
|
|
685
765
|
if (!program || !configPath) {
|
|
686
766
|
return { isOptional: false, schema: {} }; // Empty schema = any value
|
|
687
767
|
}
|
|
688
768
|
|
|
689
|
-
|
|
769
|
+
// Use provided sourceFileForLookup or try to find one from program
|
|
770
|
+
let lookupFile = sourceFileForLookup;
|
|
771
|
+
if (!lookupFile && program) {
|
|
772
|
+
// Try to find a source file that might contain the type
|
|
773
|
+
// This is a best-effort approach for anonymous types
|
|
774
|
+
const sourceFiles = program.getSourceFiles();
|
|
775
|
+
if (sourceFiles.length > 0) {
|
|
776
|
+
// Use the first source file as a fallback (could be improved)
|
|
777
|
+
lookupFile = sourceFiles[0];
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const typeInfo = getTypeInfoForGenerator(type, checker, lookupFile);
|
|
690
782
|
if (!typeInfo) {
|
|
691
783
|
// Anonymous/inline type - can't serialize with generator
|
|
692
784
|
return { isOptional: false, schema: {} }; // Empty schema = any value
|
|
@@ -907,7 +999,8 @@ async function extractFunctionMetadata(
|
|
|
907
999
|
|
|
908
1000
|
// Use ts-json-schema-generator for named types only
|
|
909
1001
|
// Anonymous types are not supported - agents can't use them anyway
|
|
910
|
-
|
|
1002
|
+
// Pass sourceFile to help resolve anonymous types to named types
|
|
1003
|
+
const result = await serializeType(type, checker, program, configPath, defs, sourceFile);
|
|
911
1004
|
propSchema = result.schema;
|
|
912
1005
|
isOptional = result.isOptional;
|
|
913
1006
|
} else {
|