import * as plugins from './plugins.js'; import { TsmdbServer } from '../ts_tsmdb/index.js'; import type { MongoClient } from 'mongodb'; export interface ILocalTsmDbOptions { folderPath: string; port?: number; host?: string; } /** * LocalTsmDb - Convenience class for local MongoDB-compatible database * * This class wraps TsmdbServer and provides a simple interface for * starting a local file-based MongoDB-compatible server and connecting to it. * * @example * ```typescript * import { LocalTsmDb } from '@push.rocks/smartmongo'; * * const db = new LocalTsmDb({ folderPath: './data' }); * const client = await db.start(); * * // Use the MongoDB client * const collection = client.db('mydb').collection('users'); * await collection.insertOne({ name: 'Alice' }); * * // When done * await db.stop(); * ``` */ export class LocalTsmDb { private options: ILocalTsmDbOptions; private server: TsmdbServer | null = null; private client: MongoClient | null = null; constructor(options: ILocalTsmDbOptions) { this.options = options; } /** * Find an available port starting from the given port */ private async findAvailablePort(startPort = 27017): Promise { return new Promise((resolve, reject) => { const server = plugins.net.createServer(); server.listen(startPort, '127.0.0.1', () => { const addr = server.address(); const port = typeof addr === 'object' && addr ? addr.port : startPort; server.close(() => resolve(port)); }); server.on('error', () => { this.findAvailablePort(startPort + 1).then(resolve).catch(reject); }); }); } /** * Start the local TsmDB server and return a connected MongoDB client */ async start(): Promise { if (this.server && this.client) { throw new Error('LocalTsmDb is already running'); } const port = this.options.port ?? await this.findAvailablePort(); const host = this.options.host ?? '127.0.0.1'; this.server = new TsmdbServer({ port, host, storage: 'file', storagePath: this.options.folderPath, }); await this.server.start(); // Dynamically import mongodb to avoid requiring it as a hard dependency const mongodb = await import('mongodb'); this.client = new mongodb.MongoClient(this.server.getConnectionUri(), { directConnection: true, serverSelectionTimeoutMS: 5000, }); await this.client.connect(); return this.client; } /** * Get the MongoDB client (throws if not started) */ getClient(): MongoClient { if (!this.client) { throw new Error('LocalTsmDb is not running. Call start() first.'); } return this.client; } /** * Get the underlying TsmdbServer instance (throws if not started) */ getServer(): TsmdbServer { if (!this.server) { throw new Error('LocalTsmDb is not running. Call start() first.'); } return this.server; } /** * Get the connection URI */ getConnectionUri(): string { if (!this.server) { throw new Error('LocalTsmDb is not running. Call start() first.'); } return this.server.getConnectionUri(); } /** * Check if the server is running */ get running(): boolean { return this.server !== null && this.server.running; } /** * Stop the local TsmDB server and close the client connection */ async stop(): Promise { if (this.client) { await this.client.close(); this.client = null; } if (this.server) { await this.server.stop(); this.server = null; } } }