@wearesage/schema
Version:
A flexible schema definition and validation system for TypeScript with multi-database support
326 lines (276 loc) • 7.99 kB
text/typescript
export type Type<T> = new (...args: any[]) => T;
export type EntityOptions = {
name?: string;
description?: string;
};
export type PropertyOptions = {
name?: string;
type?: any;
required?: boolean;
unique?: boolean;
default?: any;
description?: string;
readOnly?: boolean;
writeOnly?: boolean;
};
export type RelationshipOptions = {
name?: string;
target: any;
inverse?: string;
required?: boolean;
cardinality?: "one" | "many";
};
export type RelationshipTypeFunc<T> = () => Type<T> | string;
// New relationship options using type function approach
// Database-specific relationship options
export type Neo4jRelationshipOptions = {
direction?: 'IN' | 'OUT';
type?: string;
properties?: Record<string, any>;
};
export type MongoDBRelationshipOptions = {
embedded?: boolean;
collection?: string;
};
export type PostgreSQLRelationshipOptions = {
joinTable?: string;
foreignKeyColumn?: string;
inverse_foreignKeyColumn?: string;
};
export type OneToOneOptions<T> = {
target: RelationshipTypeFunc<T>;
inverse?: string;
name?: string;
required?: boolean;
neo4j?: Neo4jRelationshipOptions;
mongodb?: MongoDBRelationshipOptions;
postgresql?: PostgreSQLRelationshipOptions;
};
export type OneToManyOptions<T> = {
target: RelationshipTypeFunc<T>;
inverse?: string;
name?: string;
required?: boolean;
neo4j?: Neo4jRelationshipOptions;
mongodb?: MongoDBRelationshipOptions;
postgresql?: PostgreSQLRelationshipOptions;
};
export type ManyToOneOptions<T> = {
target: RelationshipTypeFunc<T>;
inverse?: string;
name?: string;
required?: boolean;
neo4j?: Neo4jRelationshipOptions;
mongodb?: MongoDBRelationshipOptions;
postgresql?: PostgreSQLRelationshipOptions;
};
export type ManyToManyOptions<T> = {
target: RelationshipTypeFunc<T>;
inverse?: string;
name?: string;
required?: boolean;
neo4j?: Neo4jRelationshipOptions;
mongodb?: MongoDBRelationshipOptions;
postgresql?: PostgreSQLRelationshipOptions;
};
// Validation decorator types
export type ValidationRule = {
type: string;
options?: any;
message?: string;
};
export type CustomValidatorFunction = (value: any) => boolean | Promise<boolean>;
export type MinMaxOptions = {
value: number;
message?: string;
};
export type PatternOptions = {
pattern: RegExp;
message?: string;
};
export type EmailOptions = {
message?: string;
};
export type URLOptions = {
message?: string;
protocols?: string[];
};
export type CustomValidationOptions = {
validator: CustomValidatorFunction;
message?: string;
};
export type LengthOptions = {
min?: number;
max?: number;
message?: string;
};
export type IndexOptions = {
unique?: boolean;
sparse?: boolean;
background?: boolean;
name?: string;
direction?: 'asc' | 'desc';
ttl?: number;
};
export type TimestampOptions = {
onCreate?: boolean;
onUpdate?: boolean;
precision?: 'milliseconds' | 'seconds';
};
// Validation result types
export type ValidationError = {
property: string;
value: any;
message: string;
rule: string;
target: string;
};
export type ValidationResult = {
isValid: boolean;
errors: ValidationError[];
};
export type ValidatorFunction = (value: any, options?: any) => boolean | Promise<boolean>;
// Query Builder Types
export type QueryCondition = {
property: string;
operator: QueryOperator;
value: any;
async?: boolean;
};
export type QueryOperator =
| 'equals' | 'eq'
| 'not_equals' | 'ne' | 'not'
| 'greater_than' | 'gt'
| 'greater_than_or_equal' | 'gte'
| 'less_than' | 'lt'
| 'less_than_or_equal' | 'lte'
| 'in' | 'not_in'
| 'like' | 'ilike'
| 'contains' | 'starts_with' | 'ends_with'
| 'is_null' | 'is_not_null'
| 'between' | 'not_between'
| 'regex' | 'not_regex';
export type QueryLogicalOperator = 'and' | 'or' | 'not';
export type QuerySort = {
property: string;
direction: 'asc' | 'desc';
};
export type QueryInclude = {
relationship: string;
nested?: QueryInclude[];
};
export type QueryOptions = {
limit?: number;
offset?: number;
sort?: QuerySort[];
include?: QueryInclude[];
};
export type QueryResult<T> = {
data: T[];
total: number;
hasMore: boolean;
metadata: {
query: string;
executionTime: number;
database: string;
cached?: boolean;
};
};
// Async condition function type - for agents to use external data in queries
export type AsyncConditionFunction<T> = (entity: T) => Promise<boolean>;
// Type-safe query builder interface
export interface IQueryBuilder<T> {
// Basic conditions
where(property: keyof T): IQueryConditionBuilder<T>;
// Logical operators
and(property: keyof T): IQueryConditionBuilder<T>;
or(property: keyof T): IQueryConditionBuilder<T>;
not(property: keyof T): IQueryConditionBuilder<T>;
// Async conditions for AI agents
whereAsync(condition: AsyncConditionFunction<T>): IQueryBuilder<T>;
andAsync(condition: AsyncConditionFunction<T>): IQueryBuilder<T>;
orAsync(condition: AsyncConditionFunction<T>): IQueryBuilder<T>;
// Relationships
include(relationship: string): IQueryBuilder<T>;
includeNested(relationship: string, nested: QueryInclude[]): IQueryBuilder<T>;
// Sorting & Pagination
orderBy(property: keyof T, direction?: 'asc' | 'desc'): IQueryBuilder<T>;
limit(count: number): IQueryBuilder<T>;
offset(count: number): IQueryBuilder<T>;
// Execution
execute(): Promise<QueryResult<T>>;
first(): Promise<T | null>;
count(): Promise<number>;
exists(): Promise<boolean>;
// Introspection for AI agents
getAvailableProperties(): (keyof T)[];
getAvailableRelationships(): string[];
getAvailableOperators(): QueryOperator[];
describeProperty(property: keyof T): PropertyMetadata;
describeRelationship(relationship: string): RelationshipMetadata;
// Internal methods (for testing and compiler)
addCondition(condition: QueryCondition): void;
addAsyncCondition(condition: AsyncConditionFunction<T>): void;
getConditions(): QueryCondition[];
getAsyncConditions(): AsyncConditionFunction<T>[];
getSorts(): QuerySort[];
getIncludes(): QueryInclude[];
getLimit(): number | undefined;
getOffset(): number | undefined;
}
export interface IQueryConditionBuilder<T> {
// Comparison operators
equals(value: any): IQueryBuilder<T>;
eq(value: any): IQueryBuilder<T>;
not(value: any): IQueryBuilder<T>;
ne(value: any): IQueryBuilder<T>;
// Numeric operators
gt(value: number): IQueryBuilder<T>;
gte(value: number): IQueryBuilder<T>;
lt(value: number): IQueryBuilder<T>;
lte(value: number): IQueryBuilder<T>;
between(min: number, max: number): IQueryBuilder<T>;
// Array operators
in(values: any[]): IQueryBuilder<T>;
notIn(values: any[]): IQueryBuilder<T>;
// String operators
like(pattern: string): IQueryBuilder<T>;
ilike(pattern: string): IQueryBuilder<T>;
contains(value: string): IQueryBuilder<T>;
startsWith(value: string): IQueryBuilder<T>;
endsWith(value: string): IQueryBuilder<T>;
regex(pattern: RegExp): IQueryBuilder<T>;
// Null operators
isNull(): IQueryBuilder<T>;
isNotNull(): IQueryBuilder<T>;
// Async operators for AI agents
equalsAsync(valueProvider: () => Promise<any>): IQueryBuilder<T>;
matchesAsync(condition: AsyncConditionFunction<T>): IQueryBuilder<T>;
}
// Metadata types for AI agent discovery
export type PropertyMetadata = {
name: string;
type: string;
required: boolean;
unique: boolean;
indexed: boolean;
description?: string;
validationRules: ValidationRule[];
examples?: any[];
};
export type RelationshipMetadata = {
name: string;
type: 'one-to-one' | 'one-to-many' | 'many-to-one' | 'many-to-many';
target: string;
inverse?: string;
required: boolean;
description?: string;
};
// Query compilation for different databases
export type CompiledQuery = {
sql?: string;
mongodb?: any;
neo4j?: string;
redis?: string[];
parameters: Record<string, any>;
};