@arcteninc/core 0.0.51 → 0.0.53

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.51",
3
+ "version": "0.0.53",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -692,6 +692,117 @@ function serializeTypeCustom(
692
692
  }
693
693
 
694
694
  const typeId = (type as any).id;
695
+
696
+ // Check for interfaces/type aliases FIRST (before general visited check)
697
+ // This allows us to handle recursive types properly with $defs
698
+ const symbol = type.getSymbol();
699
+ if (symbol) {
700
+ const declarations = symbol.getDeclarations();
701
+ if (declarations && declarations.length > 0) {
702
+ const firstDecl = declarations[0];
703
+
704
+ // Handle interfaces and type aliases (for recursive types)
705
+ if (ts.isInterfaceDeclaration(firstDecl) || ts.isTypeAliasDeclaration(firstDecl)) {
706
+ const typeName = symbol.getName();
707
+ if (typeName && defs) {
708
+ const defsKey = typeName; // Use the type name as key
709
+
710
+ // Check if already defined (from a previous call)
711
+ if (defs[defsKey]) {
712
+ return { isOptional: false, schema: { $ref: `#/$defs/${defsKey}` } };
713
+ }
714
+
715
+ // Check if we've visited this type before (circular/recursive reference)
716
+ // This happens when we encounter the same type while building its properties
717
+ if (typeId !== undefined && visited.has(typeId)) {
718
+ // Create a placeholder that will be filled in by the original call
719
+ if (!defs[defsKey]) {
720
+ defs[defsKey] = { type: 'object', properties: {} };
721
+ }
722
+ return { isOptional: false, schema: { $ref: `#/$defs/${defsKey}` } };
723
+ }
724
+
725
+ // Create placeholder first to handle recursion
726
+ defs[defsKey] = { type: 'object', properties: {} };
727
+
728
+ // Mark as visited BEFORE building properties (so recursion is detected)
729
+ if (typeId !== undefined) {
730
+ visited.add(typeId);
731
+ }
732
+
733
+ // Build the schema for this type
734
+ // For interfaces, get properties from both the type and the declaration
735
+ const props = checker.getPropertiesOfType(type);
736
+ const properties: Record<string, JsonSchemaProperty> = {};
737
+ const required: string[] = [];
738
+
739
+ // Extract properties from the interface/type alias declaration
740
+ if (ts.isInterfaceDeclaration(firstDecl)) {
741
+ for (const member of firstDecl.members) {
742
+ if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
743
+ const propName = member.name.text;
744
+ const isOptional = member.questionToken !== undefined;
745
+
746
+ if (member.type) {
747
+ const propType = checker.getTypeFromTypeNode(member.type);
748
+ const propResult = serializeTypeCustom(propType, checker, defs, visited, depth + 1);
749
+ properties[propName] = propResult.schema;
750
+
751
+ if (!isOptional && !propResult.isOptional) {
752
+ required.push(propName);
753
+ }
754
+ }
755
+ }
756
+ }
757
+ } else if (ts.isTypeAliasDeclaration(firstDecl) && firstDecl.type) {
758
+ // For type aliases, resolve the underlying type
759
+ const underlyingType = checker.getTypeFromTypeNode(firstDecl.type);
760
+ if (underlyingType.flags & ts.TypeFlags.Object) {
761
+ const aliasProps = checker.getPropertiesOfType(underlyingType);
762
+ for (const prop of aliasProps) {
763
+ const propName = prop.getName();
764
+ const propType = checker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration || prop.declarations?.[0] || null as any);
765
+ const isOptional = (prop.flags & ts.SymbolFlags.Optional) !== 0;
766
+
767
+ const propResult = serializeTypeCustom(propType, checker, defs, visited, depth + 1);
768
+ properties[propName] = propResult.schema;
769
+
770
+ if (!isOptional && !propResult.isOptional) {
771
+ required.push(propName);
772
+ }
773
+ }
774
+ }
775
+ } else if (props.length > 0) {
776
+ // Fallback: use getPropertiesOfType if declaration parsing didn't work
777
+ for (const prop of props) {
778
+ const propName = prop.getName();
779
+ const propType = checker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration || prop.declarations?.[0] || null as any);
780
+ const isOptional = (prop.flags & ts.SymbolFlags.Optional) !== 0;
781
+
782
+ const propResult = serializeTypeCustom(propType, checker, defs, visited, depth + 1);
783
+ properties[propName] = propResult.schema;
784
+
785
+ if (!isOptional && !propResult.isOptional) {
786
+ required.push(propName);
787
+ }
788
+ }
789
+ }
790
+
791
+ // Update the placeholder with the actual schema (even if empty, to avoid infinite loops)
792
+ defs[defsKey] = {
793
+ type: 'object',
794
+ properties,
795
+ ...(required.length > 0 && { required })
796
+ };
797
+
798
+ // Return reference
799
+ return { isOptional: false, schema: { $ref: `#/$defs/${defsKey}` } };
800
+ }
801
+ }
802
+ }
803
+ }
804
+
805
+ // General recursion check for non-interface types
695
806
  if (typeId !== undefined && visited.has(typeId)) {
696
807
  return { isOptional: false, schema: {} };
697
808
  }
@@ -725,7 +836,6 @@ function serializeTypeCustom(
725
836
  }
726
837
 
727
838
  // Handle enums (TypeScript enum types)
728
- const symbol = type.getSymbol();
729
839
  if (symbol) {
730
840
  const declarations = symbol.getDeclarations();
731
841
  if (declarations && declarations.length > 0) {
@@ -747,66 +857,6 @@ function serializeTypeCustom(
747
857
  return { isOptional: false, schema: { enum: enumMembers } };
748
858
  }
749
859
  }
750
-
751
- // Handle interfaces and type aliases (for recursive types)
752
- if (ts.isInterfaceDeclaration(firstDecl) || ts.isTypeAliasDeclaration(firstDecl)) {
753
- const typeName = symbol.getName();
754
- if (typeName && defs) {
755
- const defsKey = typeName; // Use the type name as key
756
-
757
- // Check if we've visited this type before (circular/recursive reference)
758
- if (visited.has(typeId)) {
759
- // Create a reference to avoid infinite recursion
760
- if (!defs[defsKey]) {
761
- // Create placeholder - will be filled in below
762
- defs[defsKey] = { type: 'object', properties: {} };
763
- }
764
- return { isOptional: false, schema: { $ref: `#/$defs/${defsKey}` } };
765
- }
766
-
767
- // Check if already defined (from a previous call)
768
- if (defs[defsKey]) {
769
- return { isOptional: false, schema: { $ref: `#/$defs/${defsKey}` } };
770
- }
771
-
772
- // Mark as visited and build the definition
773
- visited.add(typeId);
774
-
775
- // Build the schema for this type
776
- const props = checker.getPropertiesOfType(type);
777
- if (props.length > 0) {
778
- const properties: Record<string, JsonSchemaProperty> = {};
779
- const required: string[] = [];
780
-
781
- // Use the same visited set for properties to detect recursion
782
- for (const prop of props) {
783
- const propName = prop.getName();
784
- const propType = checker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration || prop.declarations?.[0] || checker.getDeclaredTypeOfSymbol(prop).symbol?.valueDeclaration || null as any);
785
- const isOptional = (prop.flags & ts.SymbolFlags.Optional) !== 0;
786
-
787
- // Recursively serialize the property type (will detect recursion via visited set)
788
- const propResult = serializeTypeCustom(propType, checker, defs, visited, depth + 1);
789
- properties[propName] = propResult.schema;
790
-
791
- if (!isOptional && !propResult.isOptional) {
792
- required.push(propName);
793
- }
794
- }
795
-
796
- const schema: JsonSchemaProperty = {
797
- type: 'object',
798
- properties,
799
- ...(required.length > 0 && { required })
800
- };
801
-
802
- // Store in defs
803
- defs[defsKey] = schema;
804
-
805
- // Return reference
806
- return { isOptional: false, schema: { $ref: `#/$defs/${defsKey}` } };
807
- }
808
- }
809
- }
810
860
  }
811
861
  }
812
862