UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

268 lines (210 loc) 7.81 kB
import { CodeConstruct } from './CodeConstruct'; import { CodeFile } from './CodeFile'; import { Entity, Property, Id, ManyToOne, Index, Timestamp, Auth , Labels } from '../core/decorators'; import { RelationshipType } from '../adapters/neo4j'; // Auto-generated from actual parser analysis of 80,480 relationships type RelationshipTypeEnum = // Top relationships from real parser data | 'REFERENCES' // 24,360 (30.27%) - Property access expressions | 'CALLS' // 18,518 (23.01%) - Function/method calls | 'DECLARES' // 12,290 (15.27%) - Variable/function declarations | 'TYPE_OF' // 7,217 (8.97%) - Type annotations | 'DEFINES' // 6,225 (7.73%) - Assignments, initializers | 'RETURNS' // 3,033 (3.77%) - Return statements | 'AWAITS' // 1,472 (1.83%) - Await expressions | 'IMPORTS' // 1,273 (1.58%) - Import declarations | 'CASTS_TO' // 995 (1.24%) - Type assertions | 'DESTRUCTURES' // 975 (1.21%) - Destructuring patterns | 'DECORATES' // 829 (1.03%) - Decorator expressions | 'UNION_WITH' // 657 (0.82%) - Union type members | 'CATCHES' // 561 (0.7%) - Catch clauses | 'BRANCHES_ON' // 479 (0.6%) - Conditional expressions | 'EXPORTS' // 459 (0.57%) - Export declarations | 'EXTENDS' // 445 - Inheritance relationships | 'SPREADS' // 354 - Spread syntax | 'THROWS' // 281 - Throw statements | 'IMPLEMENTS' // 38 - Interface implementations | 'INTERSECTS_WITH' // 19 - Intersection types // Legacy/future types | 'CHILD_OF' | 'NEXT_SIBLING' | 'SATISFIES' | 'IMPORTS_SYMBOL' | 'EXPORTS_SYMBOL' | 'REFERENCES_SYMBOL' | 'GENERIC_OF' | 'HAS_TYPE_ERROR' | 'HAS_LINT_ERROR' | 'HAS_BUILD_ERROR'; @Entity() @Labels(['CodeRelationship']) @Auth({ permissions: ['user'] }) export class CodeRelationship { @Id() id!: string; // The type of relationship (from TypeScript parser) @Property({ required: true }) @Index() relationshipType!: RelationshipTypeEnum; // Relationship category for easier querying @Property({ required: true }) @Index() category!: 'ast' | 'semantic' | 'module' | 'type' | 'error'; // Source and target entities @Property({ required: true }) fromType!: 'file' | 'construct'; // What type of entity is the source @Property({ required: true }) fromId!: string; // ID of the source entity @Property({ required: true }) fromName!: string; // Human-readable name of source @Property({ required: true }) toType!: 'file' | 'construct'; // What type of entity is the target @Property({ required: true }) toId!: string; // ID of the target entity @Property({ required: true }) toName!: string; // Human-readable name of target // Relationship quality and confidence @Property({ required: true }) @Index() confidence!: 'high' | 'medium' | 'low'; @Property() evidence?: string; // How we detected this relationship // Source location where this relationship was found @Property() sourceLocation?: { start: number; end: number; line?: number; column?: number; }; // Parser extraction metadata @Property() extractionMethod?: 'ast-traversal' | 'symbol-table' | 'program-api' | 'external-tool'; @Property() extractedAt?: Date; // When this relationship was extracted @Property() parserVersion?: string; // Version of parser that extracted this // Context information @Property() isDirectRelationship?: boolean; // Direct vs inferred relationship @Property() relationshipDepth?: number; // For transitive relationships @Property() isConditional?: boolean; // Only true under certain conditions @Property() conditions?: string[]; // What conditions must be met // Code-specific context @Property() isAsyncRelationship?: boolean; // For async/await relationships @Property() isTypeOnlyRelationship?: boolean; // TypeScript type-only imports @Property() importSpecifier?: string; // For import relationships: named, default, namespace @Property() accessModifier?: 'public' | 'private' | 'protected'; // For class member relationships // Quality metrics @Property() usageFrequency?: number; // How often this relationship is used @Property() isDeprecated?: boolean; // Is this relationship deprecated @Property() isCritical?: boolean; // Is this a critical dependency @Property() hasCircularDependency?: boolean; // Part of a circular dependency // Error information (for error relationships) @Property() errorSeverity?: 'error' | 'warning' | 'info'; @Property() errorCode?: string | number; @Property() errorMessage?: string; @Property() errorSource?: string; // tsc, eslint, etc. // Relationships to entities // Source file (for file-level relationships) @ManyToOne({ target: () => CodeFile, neo4j: { type: 'FROM_FILE', direction: 'OUT' } }) @RelationshipType('FROM_FILE') fromFile?: CodeFile; // Target file (for file-level relationships) @ManyToOne({ target: () => CodeFile, neo4j: { type: 'TO_FILE', direction: 'OUT' } }) @RelationshipType('TO_FILE') toFile?: CodeFile; // Source construct (for construct-level relationships) @ManyToOne({ target: () => CodeConstruct, neo4j: { type: 'FROM_CONSTRUCT', direction: 'OUT' } }) @RelationshipType('FROM_CONSTRUCT') fromConstruct?: CodeConstruct; // Target construct (for construct-level relationships) @ManyToOne({ target: () => CodeConstruct, neo4j: { type: 'TO_CONSTRUCT', direction: 'OUT' } }) @RelationshipType('TO_CONSTRUCT') toConstruct?: CodeConstruct; // Flexible metadata for relationship-specific data @Property() metadata: Record<string, any> = {}; @Timestamp({ onCreate: true }) createdAt!: Date; @Timestamp({ onUpdate: true }) updatedAt!: Date; // Helper methods getRelationshipDescription(): string { return `${this.fromName} ${this.relationshipType} ${this.toName}`; } isFileToFileRelationship(): boolean { return this.fromType === 'file' && this.toType === 'file'; } isConstructToConstructRelationship(): boolean { return this.fromType === 'construct' && this.toType === 'construct'; } isFileToConstructRelationship(): boolean { return this.fromType === 'file' && this.toType === 'construct'; } isConstructToFileRelationship(): boolean { return this.fromType === 'construct' && this.toType === 'file'; } isModuleRelationship(): boolean { return this.category === 'module'; } isSemanticRelationship(): boolean { return this.category === 'semantic'; } isTypeRelationship(): boolean { return this.category === 'type'; } isErrorRelationship(): boolean { return this.category === 'error'; } isImportRelationship(): boolean { return ['IMPORTS', 'IMPORTS_SYMBOL'].includes(this.relationshipType); } isExportRelationship(): boolean { return ['EXPORTS', 'EXPORTS_SYMBOL'].includes(this.relationshipType); } isCallRelationship(): boolean { return this.relationshipType === 'CALLS'; } isInheritanceRelationship(): boolean { return ['EXTENDS', 'IMPLEMENTS'].includes(this.relationshipType); } getSourceEntity(): { type: string; id: string; name: string } { return { type: this.fromType, id: this.fromId, name: this.fromName }; } getTargetEntity(): { type: string; id: string; name: string } { return { type: this.toType, id: this.toId, name: this.toName }; } getLocationString(): string { if (this.sourceLocation) { return `line ${this.sourceLocation.line}, column ${this.sourceLocation.column}`; } return 'unknown location'; } isHighConfidence(): boolean { return this.confidence === 'high'; } }