@wearesage/schema
Version:
A flexible schema definition and validation system for TypeScript with multi-database support
135 lines (118 loc) • 4.01 kB
text/typescript
import { Type } from '../core/types';
/**
* Interface for all database adapters
*/
export interface DatabaseAdapter {
/**
* Name of the database type (Neo4j, MongoDB, PostgreSQL, etc.)
*/
readonly type: string;
/**
* Executes a query against the database
* @param entityType The entity class
* @param criteria Query criteria
* @returns Promise resolving to entity or null
*/
query<T>(entityType: Type<T>, criteria: object): Promise<T | null>;
/**
* Executes a query returning multiple results
* @param entityType The entity class
* @param criteria Query criteria
* @returns Promise resolving to array of entities
*/
queryMany<T>(entityType: Type<T>, criteria: object): Promise<T[]>;
/**
* Saves an entity to the database
* @param entity The entity to save
* @returns Promise resolving when save is complete
*/
save<T extends object>(entity: T): Promise<void>;
/**
* Deletes an entity from the database
* @param entityType The entity class
* @param id The entity ID
* @returns Promise resolving when delete is complete
*/
delete<T>(entityType: Type<T>, id: string | number): Promise<void>;
/**
* Executes a native query specific to this database
* @param query The native query string
* @param params Parameters to substitute in the query
* @returns Promise resolving to query results
*/
runNativeQuery<T>(query: string, params?: any): Promise<T>;
}
/**
* Decorator for specifying which database an entity extension is for
* @param type Database type name
*/
export function DatabaseAdapter(type: string) {
return function <T extends { new (...args: any[]): any }>(target: T): T {
// Store the database type on the class
Object.defineProperty(target, 'dbType', {
value: type,
writable: false
});
// Automatically register this adapter entity with the shared registry
// We need to find the base class it extends
const baseEntity = Object.getPrototypeOf(target);
if (baseEntity && baseEntity.name) {
// Register the adapter entity using the shared instance for backward compatibility
DatabaseAdapterRegistry.getInstance().registerAdapterEntity(
type,
baseEntity,
target
);
}
return target;
};
}
/**
* Registry for database-specific entities
*/
export class DatabaseAdapterRegistry {
private static sharedInstance: DatabaseAdapterRegistry = new DatabaseAdapterRegistry();
private adapterEntities = new Map<string, Map<Function, Function>>();
constructor() {}
/**
* Get the shared instance of the registry.
* This provides backward compatibility for existing code.
* New code should prefer direct instantiation when possible.
*/
static getInstance(): DatabaseAdapterRegistry {
return DatabaseAdapterRegistry.sharedInstance;
}
/**
* Register a database-specific entity extension
* @param dbType Database type
* @param baseEntity Base entity class
* @param adapterEntity Database-specific entity class
*/
registerAdapterEntity(
dbType: string,
baseEntity: Function,
adapterEntity: Function
): void {
if (!this.adapterEntities.has(dbType)) {
this.adapterEntities.set(dbType, new Map());
}
this.adapterEntities.get(dbType)!.set(baseEntity, adapterEntity);
}
/**
* Get a database-specific entity extension
* @param dbType Database type
* @param baseEntity Base entity class
* @returns Database-specific entity class or undefined
*/
getAdapterEntity(dbType: string, baseEntity: Function): Function | undefined {
return this.adapterEntities.get(dbType)?.get(baseEntity);
}
/**
* Get all database-specific entities for a database type
* @param dbType Database type
* @returns Map of base entities to adapter entities
*/
getAllAdapterEntities(dbType: string): Map<Function, Function> | undefined {
return this.adapterEntities.get(dbType);
}
}