Files
smartdb/ts/ts_smartdb/server/SmartdbServer.ts

160 lines
4.2 KiB
TypeScript
Raw Normal View History

import { RustDbBridge } from '../rust-db-bridge.js';
2026-03-26 16:03:25 +00:00
/**
* 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
2026-03-26 16:03:25 +00:00
*
* 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.
2026-03-26 16:03:25 +00:00
*
* @example
* ```typescript
* import { SmartdbServer } from '@push.rocks/smartdb';
2026-03-26 16:03:25 +00:00
* import { MongoClient } from 'mongodb';
*
* const server = new SmartdbServer({ port: 27017 });
* await server.start();
*
* const client = new MongoClient(server.getConnectionUri());
2026-03-26 16:03:25 +00:00
* await client.connect();
* ```
*/
export class SmartdbServer {
private options: ISmartdbServerOptions;
private bridge: RustDbBridge;
2026-03-26 16:03:25 +00:00
private isRunning = false;
private resolvedConnectionUri = '';
2026-03-26 16:03:25 +00:00
constructor(options: ISmartdbServerOptions = {}) {
this.options = {
port: options.port ?? 27017,
host: options.host ?? '127.0.0.1',
socketPath: options.socketPath,
2026-03-26 16:03:25 +00:00
storage: options.storage ?? 'memory',
storagePath: options.storagePath ?? './data',
persistPath: options.persistPath,
2026-03-26 16:03:25 +00:00
persistIntervalMs: options.persistIntervalMs ?? 60000,
};
this.bridge = new RustDbBridge();
2026-03-26 16:03:25 +00:00
}
/**
* 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`.'
);
2026-03-26 16:03:25 +00:00
}
// 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})`);
2026-03-26 16:03:25 +00:00
}
});
// 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;
2026-03-26 16:03:25 +00:00
}
/**
* Stop the server
*/
async stop(): Promise<void> {
if (!this.isRunning) {
2026-03-26 16:03:25 +00:00
return;
}
try {
await this.bridge.stopDb();
} catch {
// Bridge may already be dead
2026-03-26 16:03:25 +00:00
}
this.bridge.kill();
this.isRunning = false;
2026-03-26 16:03:25 +00:00
}
/**
* Get the connection URI for this server
*/
getConnectionUri(): string {
if (this.resolvedConnectionUri) {
return this.resolvedConnectionUri;
}
// Fallback: compute from options
if (this.options.socketPath) {
2026-03-26 16:03:25 +00:00
const encodedPath = encodeURIComponent(this.options.socketPath);
return `mongodb://${encodedPath}`;
}
return `mongodb://${this.options.host ?? '127.0.0.1'}:${this.options.port ?? 27017}`;
2026-03-26 16:03:25 +00:00
}
/**
* Get the socket path (if using Unix socket mode)
*/
get socketPath(): string | undefined {
return this.options.socketPath;
2026-03-26 16:03:25 +00:00
}
/**
* 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;
2026-03-26 16:03:25 +00:00
}
/**
* Get the host the server is bound to
*/
get host(): string {
return this.options.host ?? '127.0.0.1';
2026-03-26 16:03:25 +00:00
}
}