/** * Prisma Database Adapter * * Production-grade database adapter using Prisma ORM. * As of Prisma 5.10.0+, ARM64 Windows is fully supported. */ import { logger } from '../../utils/logger.js'; // Type definitions export type PrismaClient = any; /** * Check if PostgreSQL is configured */ function hasPostgresConfig(): boolean { return !!process.env.DATABASE_URL; } /** * Database Adapter Interface * Generic interface for database operations */ export interface DatabaseAdapter { initialize(): Promise; disconnect(): Promise; query(sql: string, params?: any[]): Promise; execute(sql: string, params?: any[]): Promise; transaction(fn: (tx: any) => Promise): Promise; isAvailable(): boolean; } /** * Stub adapter for when Prisma is unavailable * Returns graceful no-ops and uses SQLite as fallback */ class StubDatabaseAdapter implements DatabaseAdapter { private reason: string; constructor(reason: string) { this.reason = reason; } async initialize(): Promise { logger.info(`â„šī¸ PostgreSQL skipped: ${this.reason}`); logger.info(' System will use SQLite + Neo4j (fully functional)'); } async disconnect(): Promise { // No-op } async query(_sql: string, _params?: any[]): Promise { throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`); } async transaction(_fn: (tx: any) => Promise): Promise { throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`); } async execute(_sql: string, _params?: any[]): Promise { throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`); } isAvailable(): boolean { return false; } getClient(): null { return null; } } /** * Prisma Database Adapter * Production-grade database adapter using Prisma ORM * * Only loads Prisma when: * 1. NOT on Windows ARM64 (no native binaries available) * 2. DATABASE_URL is configured */ class PrismaDatabaseAdapterImpl implements DatabaseAdapter { private prisma: any = null; private isInitialized = false; private loadError: string | null = null; constructor() { // Prisma loading is deferred to initialize() } async initialize(): Promise { if (this.isInitialized) return; try { // Dynamic import to defer binary loading const { PrismaClient } = await import('@prisma/client'); this.prisma = new PrismaClient({ log: process.env.NODE_ENV === 'development' ? ['info', 'warn', 'error'] : ['error'], }); await this.prisma.$connect(); logger.info('đŸ—„ī¸ Prisma Database connected to PostgreSQL'); // Enable pgvector extension if available (optional - we use Neo4j for vectors now) try { await this.prisma.$executeRaw`CREATE EXTENSION IF NOT EXISTS vector`; logger.info('đŸ”ĸ pgvector extension enabled'); } catch (_extError: any) { // Non-critical - Railway PostgreSQL doesn't have pgvector // We use Neo4j as primary vector store instead logger.info('â„šī¸ pgvector not available (using Neo4j for vector operations)'); } this.isInitialized = true; } catch (error: any) { this.loadError = error.message; logger.warn('âš ī¸ Prisma/PostgreSQL initialization failed:', { error: error.message }); logger.info(' Continuing with SQLite + Neo4j (fully functional)'); // Don't throw - allow graceful fallback } } async disconnect(): Promise { if (this.prisma && this.isInitialized) { await this.prisma.$disconnect(); logger.info('đŸ—„ī¸ Prisma Database disconnected'); } } async query(sql: string, params?: any[]): Promise { if (!this.prisma || !this.isInitialized) { throw new Error('PostgreSQL not connected. Use SQLite for local operations.'); } return this.prisma.$queryRawUnsafe(sql, ...(params || [])); } async execute(sql: string, params?: any[]): Promise { if (!this.prisma || !this.isInitialized) { throw new Error('PostgreSQL not connected. Use SQLite for local operations.'); } return this.prisma.$executeRawUnsafe(sql, ...(params || [])); } async transaction(fn: (tx: any) => Promise): Promise { if (!this.prisma || !this.isInitialized) { throw new Error('PostgreSQL not connected. Use SQLite for local operations.'); } return this.prisma.$transaction(fn); } isAvailable(): boolean { return this.isInitialized && this.prisma !== null; } getClient(): any { return this.prisma; } } // Singleton instance let dbInstance: DatabaseAdapter | null = null; /** * Get the database adapter (singleton) * * Returns: * - StubAdapter if DATABASE_URL is not configured * - PrismaDatabaseAdapterImpl otherwise (supports all platforms including ARM64 Windows) */ export function getDatabaseAdapter(): DatabaseAdapter & { getClient(): any } { if (!dbInstance) { if (!hasPostgresConfig()) { dbInstance = new StubDatabaseAdapter('DATABASE_URL not configured'); } else { dbInstance = new PrismaDatabaseAdapterImpl(); } } return dbInstance as DatabaseAdapter & { getClient(): any }; } // Export type alias for compatibility export { PrismaDatabaseAdapterImpl as PrismaDatabaseAdapter };