UNPKG

@wearesage/schema

Version:

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

281 lines (210 loc) 6.81 kB
import { CodeFile } from './CodeFile'; import { Entity, Property, Id, ManyToOne, OneToMany, ManyToMany, Index, Timestamp, Auth , Labels } from '../core/decorators'; import { RelationshipType } from '../adapters/neo4j'; @Entity() @Labels(['CodeConstruct']) @Auth({ permissions: ['user'] }) export class CodeConstruct { @Id() id!: string; // Basic construct information @Property({ required: true }) @Index() name!: string; // Function name, class name, variable name, etc. @Property({ required: true }) @Index() constructType!: 'function' | 'class' | 'interface' | 'type' | 'variable' | 'constant' | 'enum' | 'namespace' | 'module' | 'import' | 'export' | 'decorator' | 'unknown'; @Property() description?: string; // From JSDoc comments @Property() visibility?: 'public' | 'private' | 'protected' | 'internal'; // TypeScript visibility @Property() isAsync?: boolean; // For functions @Property() isGenerator?: boolean; // For generator functions @Property() isExported?: boolean; // Is this construct exported? @Property() isDefault?: boolean; // Is this the default export? @Property() isAbstract?: boolean; // For classes/methods @Property() isStatic?: boolean; // For class members @Property() isReadonly?: boolean; // For properties @Property() isOptional?: boolean; // For interface properties // Source location information @Property({ required: true }) startLine!: number; @Property({ required: true }) startColumn!: number; @Property({ required: true }) endLine!: number; @Property({ required: true }) endColumn!: number; @Property() startPosition?: number; // Character position in file @Property() endPosition?: number; @Property() length?: number; // Length in characters // Type information (for TypeScript) @Property() typeAnnotation?: string; // The raw type annotation @Property() returnType?: string; // For functions @Property() parameters?: Array<{ name: string; type?: string; isOptional?: boolean; isRest?: boolean; defaultValue?: string; }>; @Property() genericParameters?: Array<{ name: string; constraint?: string; defaultType?: string; }>; // Function/method specific @Property() parameterCount?: number; @Property() complexity?: number; // Cyclomatic complexity @Property() linesOfCode?: number; // Class specific @Property() memberCount?: number; @Property() methodCount?: number; @Property() propertyCount?: number; // React specific @Property() isReactComponent?: boolean; @Property() isReactHook?: boolean; @Property() hasProps?: boolean; @Property() hasState?: boolean; @Property() usesHooks?: boolean; @Property() propTypes?: Array<{ name: string; type: string; required?: boolean; }>; // JSDoc and documentation @Property() jsDocComment?: string; @Property() jsDocTags?: Array<{ tag: string; value?: string; }>; @Property() hasDocumentation?: boolean; // Decorators (for classes, methods, properties) @Property() decorators?: Array<{ name: string; arguments?: string[]; }>; // Code quality metrics @Property() hasTests?: boolean; @Property() testCoverage?: number; @Property() isDeprecated?: boolean; @Property() deprecationMessage?: string; // Usage tracking @Property() usageCount?: number; // How many times this is referenced @Property() importCount?: number; // How many files import this @Property() callCount?: number; // How many times this function is called // Relationships // The file where this construct is defined @ManyToOne({ target: () => CodeFile, inverse: 'constructs', neo4j: { type: 'DEFINED_IN', direction: 'OUT' } }) @RelationshipType('DEFINED_IN') sourceFile!: CodeFile; // Parent construct (for nested functions, class methods, etc.) @ManyToOne({ target: () => CodeConstruct, inverse: 'childConstructs', neo4j: { type: 'CHILD_OF', direction: 'OUT' } }) @RelationshipType('CHILD_OF') parentConstruct?: CodeConstruct; // Child constructs (methods in a class, nested functions, etc.) @OneToMany({ target: () => CodeConstruct, inverse: 'parentConstruct' }) childConstructs!: CodeConstruct[]; // Inheritance relationships (classes extending other classes) @ManyToOne({ target: () => CodeConstruct, inverse: 'derivedClasses', neo4j: { type: 'EXTENDS', direction: 'OUT' } }) @RelationshipType('EXTENDS') baseClass?: CodeConstruct; @OneToMany({ target: () => CodeConstruct, inverse: 'baseClass' }) derivedClasses!: CodeConstruct[]; // Interface implementation @ManyToMany({ target: () => CodeConstruct, neo4j: { type: 'IMPLEMENTS', direction: 'OUT' } }) @RelationshipType('IMPLEMENTS') implementedInterfaces!: CodeConstruct[]; @ManyToMany({ target: () => CodeConstruct, neo4j: { type: 'IMPLEMENTED_BY', direction: 'IN' } }) @RelationshipType('IMPLEMENTED_BY') implementingClasses!: CodeConstruct[]; // Type relationships @ManyToOne({ target: () => CodeConstruct, inverse: 'typedReferences', neo4j: { type: 'TYPE_OF', direction: 'OUT' } }) @RelationshipType('TYPE_OF') typeDefinition?: CodeConstruct; @OneToMany({ target: () => CodeConstruct, inverse: 'typeDefinition' }) typedReferences!: CodeConstruct[]; // Function calls and references (handled by CodeRelationship entity) // Flexible metadata for framework-specific analysis @Property() metadata: Record<string, any> = {}; @Timestamp({ onCreate: true }) createdAt!: Date; @Timestamp({ onUpdate: true }) updatedAt!: Date; // Helper methods getFullyQualifiedName(): string { if (this.parentConstruct) { return `${this.parentConstruct.getFullyQualifiedName()}.${this.name}`; } return this.name; } isFunction(): boolean { return this.constructType === 'function'; } isClass(): boolean { return this.constructType === 'class'; } isInterface(): boolean { return this.constructType === 'interface'; } isType(): boolean { return this.constructType === 'type'; } isVariable(): boolean { return this.constructType === 'variable' || this.constructType === 'constant'; } getSignature(): string { if (this.isFunction() && this.parameters) { const params = this.parameters.map(p => `${p.name}${p.isOptional ? '?' : ''}${p.type ? `: ${p.type}` : ''}` ).join(', '); return `${this.name}(${params})${this.returnType ? `: ${this.returnType}` : ''}`; } return this.name; } getLocation(): string { return `${this.sourceFile.fileName}:${this.startLine}:${this.startColumn}`; } isPublicAPI(): boolean { return this.isExported === true && this.visibility !== 'private'; } }