feat(tsmdb): implement TsmDB Mongo-wire-compatible server, add storage/engine modules and reorganize exports

This commit is contained in:
2026-02-01 23:33:35 +00:00
parent 678bf15eb4
commit fff77fbd8e
40 changed files with 261 additions and 95 deletions

View File

@@ -0,0 +1,138 @@
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<number> {
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<MongoClient> {
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<void> {
if (this.client) {
await this.client.close();
this.client = null;
}
if (this.server) {
await this.server.stop();
this.server = null;
}
}
}