@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcteninc/core",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -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
- if (!symbol) return null;
584
-
585
- const name = symbol.getName();
586
- if (!name || name.length === 0) return null;
587
-
588
- const declarations = symbol.getDeclarations();
589
- if (!declarations || declarations.length === 0) return null;
590
-
591
- // Find the first declaration that's a type alias, interface, or class
592
- for (const decl of declarations) {
593
- if (
594
- ts.isTypeAliasDeclaration(decl) ||
595
- ts.isInterfaceDeclaration(decl) ||
596
- ts.isClassDeclaration(decl)
597
- ) {
598
- const sourceFile = decl.getSourceFile();
599
- if (sourceFile) {
600
- return {
601
- typeName: name,
602
- sourceFile: sourceFile.fileName,
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
- const typeInfo = getTypeInfoForGenerator(type, checker);
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
- const typeInfo = getTypeInfoForGenerator(type, checker);
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
- const result = await serializeType(type, checker, program, configPath, defs);
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 {