import { RustDbBridge } from '../rust-db-bridge.js'; /** * Server configuration options */ export interface ISmartdbServerOptions { /** Port to listen on (default: 27017) - ignored if socketPath is set */ port?: number; /** Host to bind to (default: 127.0.0.1) - ignored if socketPath is set */ host?: string; /** Unix socket path - if set, server listens on socket instead of TCP */ socketPath?: string; /** Storage type: 'memory' or 'file' (default: 'memory') */ storage?: 'memory' | 'file'; /** Path for file storage (required if storage is 'file') */ storagePath?: string; /** Enable persistence for memory storage */ persistPath?: string; /** Persistence interval in ms (default: 60000) */ persistIntervalMs?: number; } /** * SmartdbServer - Wire protocol compatible database server backed by Rust * * This server implements the wire protocol to allow official drivers to * connect and perform operations. The core engine runs as a Rust sidecar * binary managed via @push.rocks/smartrust IPC. * * @example * ```typescript * import { SmartdbServer } from '@push.rocks/smartdb'; * import { MongoClient } from 'mongodb'; * * const server = new SmartdbServer({ port: 27017 }); * await server.start(); * * const client = new MongoClient(server.getConnectionUri()); * await client.connect(); * ``` */ export class SmartdbServer { private options: ISmartdbServerOptions; private bridge: RustDbBridge; private isRunning = false; private resolvedConnectionUri = ''; constructor(options: ISmartdbServerOptions = {}) { this.options = { port: options.port ?? 27017, host: options.host ?? '127.0.0.1', socketPath: options.socketPath, storage: options.storage ?? 'memory', storagePath: options.storagePath ?? './data', persistPath: options.persistPath, persistIntervalMs: options.persistIntervalMs ?? 60000, }; this.bridge = new RustDbBridge(); } /** * Start the server */ async start(): Promise { if (this.isRunning) { throw new Error('Server is already running'); } const spawned = await this.bridge.spawn(); if (!spawned) { throw new Error( 'smartdb Rust binary not found. Set SMARTDB_RUST_BINARY env var, ' + 'install the platform package, or build locally with `tsrust`.' ); } // Forward unexpected exit this.bridge.on('exit', (code: number | null, signal: string | null) => { if (this.isRunning) { console.error(`smartdb Rust process exited unexpectedly (code=${code}, signal=${signal})`); } }); // Send config, get back connectionUri const result = await this.bridge.startDb({ port: this.options.port, host: this.options.host, socketPath: this.options.socketPath, storage: this.options.storage ?? 'memory', storagePath: this.options.storagePath, persistPath: this.options.persistPath, persistIntervalMs: this.options.persistIntervalMs, }); this.resolvedConnectionUri = result.connectionUri; this.isRunning = true; } /** * Stop the server */ async stop(): Promise { if (!this.isRunning) { return; } try { await this.bridge.stopDb(); } catch { // Bridge may already be dead } this.bridge.kill(); this.isRunning = false; } /** * Get the connection URI for this server */ getConnectionUri(): string { if (this.resolvedConnectionUri) { return this.resolvedConnectionUri; } // Fallback: compute from options if (this.options.socketPath) { const encodedPath = encodeURIComponent(this.options.socketPath); return `mongodb://${encodedPath}`; } return `mongodb://${this.options.host ?? '127.0.0.1'}:${this.options.port ?? 27017}`; } /** * Get the socket path (if using Unix socket mode) */ get socketPath(): string | undefined { return this.options.socketPath; } /** * Check if the server is running */ get running(): boolean { return this.isRunning; } /** * Get the port the server is listening on */ get port(): number { return this.options.port ?? 27017; } /** * Get the host the server is bound to */ get host(): string { return this.options.host ?? '127.0.0.1'; } }