139 lines
3.5 KiB
TypeScript
139 lines
3.5 KiB
TypeScript
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|