160 lines
4.2 KiB
TypeScript
160 lines
4.2 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
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';
|
|
}
|
|
}
|