@push.rocks/smartmongo 2.0.12 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/congodb/congodb.plugins.d.ts +10 -0
  3. package/dist_ts/congodb/congodb.plugins.js +14 -0
  4. package/dist_ts/congodb/engine/AggregationEngine.d.ts +66 -0
  5. package/dist_ts/congodb/engine/AggregationEngine.js +189 -0
  6. package/dist_ts/congodb/engine/IndexEngine.d.ts +77 -0
  7. package/dist_ts/congodb/engine/IndexEngine.js +376 -0
  8. package/dist_ts/congodb/engine/QueryEngine.d.ts +54 -0
  9. package/dist_ts/congodb/engine/QueryEngine.js +271 -0
  10. package/dist_ts/congodb/engine/TransactionEngine.d.ts +85 -0
  11. package/dist_ts/congodb/engine/TransactionEngine.js +287 -0
  12. package/dist_ts/congodb/engine/UpdateEngine.d.ts +47 -0
  13. package/dist_ts/congodb/engine/UpdateEngine.js +461 -0
  14. package/dist_ts/congodb/errors/CongoErrors.d.ts +100 -0
  15. package/dist_ts/congodb/errors/CongoErrors.js +155 -0
  16. package/dist_ts/congodb/index.d.ts +19 -0
  17. package/dist_ts/congodb/index.js +26 -0
  18. package/dist_ts/congodb/server/CommandRouter.d.ts +51 -0
  19. package/dist_ts/congodb/server/CommandRouter.js +132 -0
  20. package/dist_ts/congodb/server/CongoServer.d.ts +95 -0
  21. package/dist_ts/congodb/server/CongoServer.js +227 -0
  22. package/dist_ts/congodb/server/WireProtocol.d.ts +117 -0
  23. package/dist_ts/congodb/server/WireProtocol.js +298 -0
  24. package/dist_ts/congodb/server/handlers/AdminHandler.d.ts +100 -0
  25. package/dist_ts/congodb/server/handlers/AdminHandler.js +568 -0
  26. package/dist_ts/congodb/server/handlers/AggregateHandler.d.ts +31 -0
  27. package/dist_ts/congodb/server/handlers/AggregateHandler.js +277 -0
  28. package/dist_ts/congodb/server/handlers/DeleteHandler.d.ts +8 -0
  29. package/dist_ts/congodb/server/handlers/DeleteHandler.js +83 -0
  30. package/dist_ts/congodb/server/handlers/FindHandler.d.ts +31 -0
  31. package/dist_ts/congodb/server/handlers/FindHandler.js +261 -0
  32. package/dist_ts/congodb/server/handlers/HelloHandler.d.ts +11 -0
  33. package/dist_ts/congodb/server/handlers/HelloHandler.js +62 -0
  34. package/dist_ts/congodb/server/handlers/IndexHandler.d.ts +20 -0
  35. package/dist_ts/congodb/server/handlers/IndexHandler.js +183 -0
  36. package/dist_ts/congodb/server/handlers/InsertHandler.d.ts +8 -0
  37. package/dist_ts/congodb/server/handlers/InsertHandler.js +76 -0
  38. package/dist_ts/congodb/server/handlers/UpdateHandler.d.ts +24 -0
  39. package/dist_ts/congodb/server/handlers/UpdateHandler.js +270 -0
  40. package/dist_ts/congodb/server/handlers/index.d.ts +8 -0
  41. package/dist_ts/congodb/server/handlers/index.js +10 -0
  42. package/dist_ts/congodb/server/index.d.ts +6 -0
  43. package/dist_ts/congodb/server/index.js +7 -0
  44. package/dist_ts/congodb/storage/FileStorageAdapter.d.ts +61 -0
  45. package/dist_ts/congodb/storage/FileStorageAdapter.js +396 -0
  46. package/dist_ts/congodb/storage/IStorageAdapter.d.ts +140 -0
  47. package/dist_ts/congodb/storage/IStorageAdapter.js +2 -0
  48. package/dist_ts/congodb/storage/MemoryStorageAdapter.d.ts +66 -0
  49. package/dist_ts/congodb/storage/MemoryStorageAdapter.js +367 -0
  50. package/dist_ts/congodb/storage/OpLog.d.ts +93 -0
  51. package/dist_ts/congodb/storage/OpLog.js +221 -0
  52. package/dist_ts/congodb/types/interfaces.d.ts +363 -0
  53. package/dist_ts/congodb/types/interfaces.js +2 -0
  54. package/dist_ts/index.d.ts +1 -0
  55. package/dist_ts/index.js +8 -6
  56. package/dist_ts/smartmongo.plugins.d.ts +1 -1
  57. package/dist_ts/smartmongo.plugins.js +2 -2
  58. package/npmextra.json +17 -7
  59. package/package.json +20 -12
  60. package/readme.hints.md +89 -1
  61. package/ts/00_commitinfo_data.ts +1 -1
  62. package/ts/congodb/congodb.plugins.ts +17 -0
  63. package/ts/congodb/engine/AggregationEngine.ts +283 -0
  64. package/ts/congodb/engine/IndexEngine.ts +479 -0
  65. package/ts/congodb/engine/QueryEngine.ts +301 -0
  66. package/ts/congodb/engine/TransactionEngine.ts +351 -0
  67. package/ts/congodb/engine/UpdateEngine.ts +506 -0
  68. package/ts/congodb/errors/CongoErrors.ts +181 -0
  69. package/ts/congodb/index.ts +37 -0
  70. package/ts/congodb/server/CommandRouter.ts +180 -0
  71. package/ts/congodb/server/CongoServer.ts +298 -0
  72. package/ts/congodb/server/WireProtocol.ts +416 -0
  73. package/ts/congodb/server/handlers/AdminHandler.ts +614 -0
  74. package/ts/congodb/server/handlers/AggregateHandler.ts +342 -0
  75. package/ts/congodb/server/handlers/DeleteHandler.ts +100 -0
  76. package/ts/congodb/server/handlers/FindHandler.ts +301 -0
  77. package/ts/congodb/server/handlers/HelloHandler.ts +78 -0
  78. package/ts/congodb/server/handlers/IndexHandler.ts +207 -0
  79. package/ts/congodb/server/handlers/InsertHandler.ts +91 -0
  80. package/ts/congodb/server/handlers/UpdateHandler.ts +315 -0
  81. package/ts/congodb/server/handlers/index.ts +10 -0
  82. package/ts/congodb/server/index.ts +10 -0
  83. package/ts/congodb/storage/FileStorageAdapter.ts +479 -0
  84. package/ts/congodb/storage/IStorageAdapter.ts +202 -0
  85. package/ts/congodb/storage/MemoryStorageAdapter.ts +443 -0
  86. package/ts/congodb/storage/OpLog.ts +282 -0
  87. package/ts/congodb/types/interfaces.ts +433 -0
  88. package/ts/index.ts +3 -0
  89. package/ts/smartmongo.plugins.ts +1 -1
@@ -0,0 +1,37 @@
1
+ // CongoDB - MongoDB Wire Protocol compatible in-memory database server
2
+ // Use the official MongoDB driver to connect to CongoServer
3
+
4
+ // Re-export plugins for external use
5
+ import * as plugins from './congodb.plugins.js';
6
+ export { plugins };
7
+
8
+ // Export BSON types for convenience
9
+ export { ObjectId, Binary, Timestamp, Long, Decimal128, UUID } from 'bson';
10
+
11
+ // Export all types
12
+ export * from './types/interfaces.js';
13
+
14
+ // Export errors
15
+ export * from './errors/CongoErrors.js';
16
+
17
+ // Export storage adapters
18
+ export type { IStorageAdapter } from './storage/IStorageAdapter.js';
19
+ export { MemoryStorageAdapter } from './storage/MemoryStorageAdapter.js';
20
+ export { FileStorageAdapter } from './storage/FileStorageAdapter.js';
21
+ export { OpLog } from './storage/OpLog.js';
22
+
23
+ // Export engines
24
+ export { QueryEngine } from './engine/QueryEngine.js';
25
+ export { UpdateEngine } from './engine/UpdateEngine.js';
26
+ export { AggregationEngine } from './engine/AggregationEngine.js';
27
+ export { IndexEngine } from './engine/IndexEngine.js';
28
+ export { TransactionEngine } from './engine/TransactionEngine.js';
29
+
30
+ // Export server (the main entry point for using CongoDB)
31
+ export { CongoServer } from './server/CongoServer.js';
32
+ export type { ICongoServerOptions } from './server/CongoServer.js';
33
+
34
+ // Export wire protocol utilities (for advanced usage)
35
+ export { WireProtocol } from './server/WireProtocol.js';
36
+ export { CommandRouter } from './server/CommandRouter.js';
37
+ export type { ICommandHandler, IHandlerContext, ICursorState } from './server/CommandRouter.js';
@@ -0,0 +1,180 @@
1
+ import * as plugins from '../congodb.plugins.js';
2
+ import type { IStorageAdapter } from '../storage/IStorageAdapter.js';
3
+ import type { IParsedCommand } from './WireProtocol.js';
4
+ import type { CongoServer } from './CongoServer.js';
5
+
6
+ // Import handlers
7
+ import { HelloHandler } from './handlers/HelloHandler.js';
8
+ import { InsertHandler } from './handlers/InsertHandler.js';
9
+ import { FindHandler } from './handlers/FindHandler.js';
10
+ import { UpdateHandler } from './handlers/UpdateHandler.js';
11
+ import { DeleteHandler } from './handlers/DeleteHandler.js';
12
+ import { AggregateHandler } from './handlers/AggregateHandler.js';
13
+ import { IndexHandler } from './handlers/IndexHandler.js';
14
+ import { AdminHandler } from './handlers/AdminHandler.js';
15
+
16
+ /**
17
+ * Handler context passed to command handlers
18
+ */
19
+ export interface IHandlerContext {
20
+ storage: IStorageAdapter;
21
+ server: CongoServer;
22
+ database: string;
23
+ command: plugins.bson.Document;
24
+ documentSequences?: Map<string, plugins.bson.Document[]>;
25
+ }
26
+
27
+ /**
28
+ * Command handler interface
29
+ */
30
+ export interface ICommandHandler {
31
+ handle(context: IHandlerContext): Promise<plugins.bson.Document>;
32
+ }
33
+
34
+ /**
35
+ * CommandRouter - Routes incoming commands to appropriate handlers
36
+ */
37
+ export class CommandRouter {
38
+ private storage: IStorageAdapter;
39
+ private server: CongoServer;
40
+ private handlers: Map<string, ICommandHandler> = new Map();
41
+
42
+ // Cursor state for getMore operations
43
+ private cursors: Map<bigint, ICursorState> = new Map();
44
+ private cursorIdCounter: bigint = BigInt(1);
45
+
46
+ constructor(storage: IStorageAdapter, server: CongoServer) {
47
+ this.storage = storage;
48
+ this.server = server;
49
+ this.registerHandlers();
50
+ }
51
+
52
+ /**
53
+ * Register all command handlers
54
+ */
55
+ private registerHandlers(): void {
56
+ // Create handler instances with shared state
57
+ const helloHandler = new HelloHandler();
58
+ const findHandler = new FindHandler(this.cursors, () => this.cursorIdCounter++);
59
+ const insertHandler = new InsertHandler();
60
+ const updateHandler = new UpdateHandler();
61
+ const deleteHandler = new DeleteHandler();
62
+ const aggregateHandler = new AggregateHandler(this.cursors, () => this.cursorIdCounter++);
63
+ const indexHandler = new IndexHandler();
64
+ const adminHandler = new AdminHandler();
65
+
66
+ // Handshake commands
67
+ this.handlers.set('hello', helloHandler);
68
+ this.handlers.set('ismaster', helloHandler);
69
+ this.handlers.set('isMaster', helloHandler);
70
+
71
+ // CRUD commands
72
+ this.handlers.set('find', findHandler);
73
+ this.handlers.set('insert', insertHandler);
74
+ this.handlers.set('update', updateHandler);
75
+ this.handlers.set('delete', deleteHandler);
76
+ this.handlers.set('findAndModify', updateHandler);
77
+ this.handlers.set('getMore', findHandler);
78
+ this.handlers.set('killCursors', findHandler);
79
+
80
+ // Aggregation
81
+ this.handlers.set('aggregate', aggregateHandler);
82
+ this.handlers.set('count', findHandler);
83
+ this.handlers.set('distinct', findHandler);
84
+
85
+ // Index operations
86
+ this.handlers.set('createIndexes', indexHandler);
87
+ this.handlers.set('dropIndexes', indexHandler);
88
+ this.handlers.set('listIndexes', indexHandler);
89
+
90
+ // Admin/Database operations
91
+ this.handlers.set('ping', adminHandler);
92
+ this.handlers.set('listDatabases', adminHandler);
93
+ this.handlers.set('listCollections', adminHandler);
94
+ this.handlers.set('drop', adminHandler);
95
+ this.handlers.set('dropDatabase', adminHandler);
96
+ this.handlers.set('create', adminHandler);
97
+ this.handlers.set('serverStatus', adminHandler);
98
+ this.handlers.set('buildInfo', adminHandler);
99
+ this.handlers.set('whatsmyuri', adminHandler);
100
+ this.handlers.set('getLog', adminHandler);
101
+ this.handlers.set('hostInfo', adminHandler);
102
+ this.handlers.set('replSetGetStatus', adminHandler);
103
+ this.handlers.set('isMaster', helloHandler);
104
+ this.handlers.set('saslStart', adminHandler);
105
+ this.handlers.set('saslContinue', adminHandler);
106
+ this.handlers.set('endSessions', adminHandler);
107
+ this.handlers.set('abortTransaction', adminHandler);
108
+ this.handlers.set('commitTransaction', adminHandler);
109
+ this.handlers.set('collStats', adminHandler);
110
+ this.handlers.set('dbStats', adminHandler);
111
+ this.handlers.set('connectionStatus', adminHandler);
112
+ this.handlers.set('currentOp', adminHandler);
113
+ this.handlers.set('collMod', adminHandler);
114
+ this.handlers.set('renameCollection', adminHandler);
115
+ }
116
+
117
+ /**
118
+ * Route a command to its handler
119
+ */
120
+ async route(parsedCommand: IParsedCommand): Promise<plugins.bson.Document> {
121
+ const { commandName, command, database, documentSequences } = parsedCommand;
122
+
123
+ // Create handler context
124
+ const context: IHandlerContext = {
125
+ storage: this.storage,
126
+ server: this.server,
127
+ database,
128
+ command,
129
+ documentSequences,
130
+ };
131
+
132
+ // Find handler
133
+ const handler = this.handlers.get(commandName);
134
+
135
+ if (!handler) {
136
+ // Unknown command
137
+ return {
138
+ ok: 0,
139
+ errmsg: `no such command: '${commandName}'`,
140
+ code: 59,
141
+ codeName: 'CommandNotFound',
142
+ };
143
+ }
144
+
145
+ try {
146
+ return await handler.handle(context);
147
+ } catch (error: any) {
148
+ // Handle known error types
149
+ if (error.code) {
150
+ return {
151
+ ok: 0,
152
+ errmsg: error.message,
153
+ code: error.code,
154
+ codeName: error.codeName || 'UnknownError',
155
+ };
156
+ }
157
+
158
+ // Generic error
159
+ return {
160
+ ok: 0,
161
+ errmsg: error.message || 'Internal error',
162
+ code: 1,
163
+ codeName: 'InternalError',
164
+ };
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Cursor state for multi-batch queries
171
+ */
172
+ export interface ICursorState {
173
+ id: bigint;
174
+ database: string;
175
+ collection: string;
176
+ documents: plugins.bson.Document[];
177
+ position: number;
178
+ batchSize: number;
179
+ createdAt: Date;
180
+ }
@@ -0,0 +1,298 @@
1
+ import * as net from 'net';
2
+ import * as plugins from '../congodb.plugins.js';
3
+ import { WireProtocol, OP_QUERY } from './WireProtocol.js';
4
+ import { CommandRouter } from './CommandRouter.js';
5
+ import { MemoryStorageAdapter } from '../storage/MemoryStorageAdapter.js';
6
+ import { FileStorageAdapter } from '../storage/FileStorageAdapter.js';
7
+ import type { IStorageAdapter } from '../storage/IStorageAdapter.js';
8
+
9
+ /**
10
+ * Server configuration options
11
+ */
12
+ export interface ICongoServerOptions {
13
+ /** Port to listen on (default: 27017) */
14
+ port?: number;
15
+ /** Host to bind to (default: 127.0.0.1) */
16
+ host?: string;
17
+ /** Storage type: 'memory' or 'file' (default: 'memory') */
18
+ storage?: 'memory' | 'file';
19
+ /** Path for file storage (required if storage is 'file') */
20
+ storagePath?: string;
21
+ /** Enable persistence for memory storage */
22
+ persistPath?: string;
23
+ /** Persistence interval in ms (default: 60000) */
24
+ persistIntervalMs?: number;
25
+ }
26
+
27
+ /**
28
+ * Connection state for each client
29
+ */
30
+ interface IConnectionState {
31
+ id: number;
32
+ socket: net.Socket;
33
+ buffer: Buffer;
34
+ authenticated: boolean;
35
+ database: string;
36
+ }
37
+
38
+ /**
39
+ * CongoServer - MongoDB Wire Protocol compatible server
40
+ *
41
+ * This server implements the MongoDB wire protocol (OP_MSG) to allow
42
+ * official MongoDB drivers to connect and perform operations.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { CongoServer } from '@push.rocks/smartmongo/congodb';
47
+ * import { MongoClient } from 'mongodb';
48
+ *
49
+ * const server = new CongoServer({ port: 27017 });
50
+ * await server.start();
51
+ *
52
+ * const client = new MongoClient('mongodb://127.0.0.1:27017');
53
+ * await client.connect();
54
+ * ```
55
+ */
56
+ export class CongoServer {
57
+ private options: Required<ICongoServerOptions>;
58
+ private server: net.Server | null = null;
59
+ private storage: IStorageAdapter;
60
+ private commandRouter: CommandRouter;
61
+ private connections: Map<number, IConnectionState> = new Map();
62
+ private connectionIdCounter = 0;
63
+ private isRunning = false;
64
+ private startTime: Date = new Date();
65
+
66
+ constructor(options: ICongoServerOptions = {}) {
67
+ this.options = {
68
+ port: options.port ?? 27017,
69
+ host: options.host ?? '127.0.0.1',
70
+ storage: options.storage ?? 'memory',
71
+ storagePath: options.storagePath ?? './data',
72
+ persistPath: options.persistPath ?? '',
73
+ persistIntervalMs: options.persistIntervalMs ?? 60000,
74
+ };
75
+
76
+ // Create storage adapter
77
+ if (this.options.storage === 'file') {
78
+ this.storage = new FileStorageAdapter(this.options.storagePath);
79
+ } else {
80
+ this.storage = new MemoryStorageAdapter({
81
+ persistPath: this.options.persistPath || undefined,
82
+ persistIntervalMs: this.options.persistPath ? this.options.persistIntervalMs : undefined,
83
+ });
84
+ }
85
+
86
+ // Create command router
87
+ this.commandRouter = new CommandRouter(this.storage, this);
88
+ }
89
+
90
+ /**
91
+ * Get the storage adapter (for testing/debugging)
92
+ */
93
+ getStorage(): IStorageAdapter {
94
+ return this.storage;
95
+ }
96
+
97
+ /**
98
+ * Get server uptime in seconds
99
+ */
100
+ getUptime(): number {
101
+ return Math.floor((Date.now() - this.startTime.getTime()) / 1000);
102
+ }
103
+
104
+ /**
105
+ * Get current connection count
106
+ */
107
+ getConnectionCount(): number {
108
+ return this.connections.size;
109
+ }
110
+
111
+ /**
112
+ * Start the server
113
+ */
114
+ async start(): Promise<void> {
115
+ if (this.isRunning) {
116
+ throw new Error('Server is already running');
117
+ }
118
+
119
+ // Initialize storage
120
+ await this.storage.initialize();
121
+
122
+ return new Promise((resolve, reject) => {
123
+ this.server = net.createServer((socket) => {
124
+ this.handleConnection(socket);
125
+ });
126
+
127
+ this.server.on('error', (err) => {
128
+ if (!this.isRunning) {
129
+ reject(err);
130
+ } else {
131
+ console.error('Server error:', err);
132
+ }
133
+ });
134
+
135
+ this.server.listen(this.options.port, this.options.host, () => {
136
+ this.isRunning = true;
137
+ this.startTime = new Date();
138
+ resolve();
139
+ });
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Stop the server
145
+ */
146
+ async stop(): Promise<void> {
147
+ if (!this.isRunning || !this.server) {
148
+ return;
149
+ }
150
+
151
+ // Close all connections
152
+ for (const conn of this.connections.values()) {
153
+ conn.socket.destroy();
154
+ }
155
+ this.connections.clear();
156
+
157
+ // Close storage
158
+ await this.storage.close();
159
+
160
+ return new Promise((resolve) => {
161
+ this.server!.close(() => {
162
+ this.isRunning = false;
163
+ this.server = null;
164
+ resolve();
165
+ });
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Handle a new client connection
171
+ */
172
+ private handleConnection(socket: net.Socket): void {
173
+ const connectionId = ++this.connectionIdCounter;
174
+
175
+ const state: IConnectionState = {
176
+ id: connectionId,
177
+ socket,
178
+ buffer: Buffer.alloc(0),
179
+ authenticated: true, // No auth required for now
180
+ database: 'test',
181
+ };
182
+
183
+ this.connections.set(connectionId, state);
184
+
185
+ socket.on('data', (data) => {
186
+ this.handleData(state, Buffer.isBuffer(data) ? data : Buffer.from(data));
187
+ });
188
+
189
+ socket.on('close', () => {
190
+ this.connections.delete(connectionId);
191
+ });
192
+
193
+ socket.on('error', (err) => {
194
+ // Connection errors are expected when clients disconnect
195
+ this.connections.delete(connectionId);
196
+ });
197
+ }
198
+
199
+ /**
200
+ * Handle incoming data from a client
201
+ */
202
+ private handleData(state: IConnectionState, data: Buffer): void {
203
+ // Append new data to buffer
204
+ state.buffer = Buffer.concat([state.buffer, data]);
205
+
206
+ // Process messages from buffer
207
+ this.processMessages(state);
208
+ }
209
+
210
+ /**
211
+ * Process complete messages from the buffer
212
+ */
213
+ private async processMessages(state: IConnectionState): Promise<void> {
214
+ while (state.buffer.length >= 16) {
215
+ try {
216
+ const result = WireProtocol.parseMessage(state.buffer);
217
+
218
+ if (!result) {
219
+ // Not enough data for a complete message
220
+ break;
221
+ }
222
+
223
+ const { command, bytesConsumed } = result;
224
+
225
+ // Remove processed bytes from buffer
226
+ state.buffer = state.buffer.subarray(bytesConsumed);
227
+
228
+ // Process the command
229
+ const response = await this.commandRouter.route(command);
230
+
231
+ // Encode and send response
232
+ let responseBuffer: Buffer;
233
+ if (command.opCode === OP_QUERY) {
234
+ // Legacy OP_QUERY gets OP_REPLY response
235
+ responseBuffer = WireProtocol.encodeOpReplyResponse(
236
+ command.requestID,
237
+ [response]
238
+ );
239
+ } else {
240
+ // OP_MSG gets OP_MSG response
241
+ responseBuffer = WireProtocol.encodeOpMsgResponse(
242
+ command.requestID,
243
+ response
244
+ );
245
+ }
246
+
247
+ if (!state.socket.destroyed) {
248
+ state.socket.write(responseBuffer);
249
+ }
250
+ } catch (error: any) {
251
+ // Send error response
252
+ const errorResponse = WireProtocol.encodeErrorResponse(
253
+ 0, // We don't have the requestID at this point
254
+ 1,
255
+ error.message || 'Internal error'
256
+ );
257
+
258
+ if (!state.socket.destroyed) {
259
+ state.socket.write(errorResponse);
260
+ }
261
+
262
+ // Clear buffer on parse errors to avoid infinite loops
263
+ if (error.message?.includes('opCode') || error.message?.includes('section')) {
264
+ state.buffer = Buffer.alloc(0);
265
+ }
266
+ break;
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Get the connection URI for this server
273
+ */
274
+ getConnectionUri(): string {
275
+ return `mongodb://${this.options.host}:${this.options.port}`;
276
+ }
277
+
278
+ /**
279
+ * Check if the server is running
280
+ */
281
+ get running(): boolean {
282
+ return this.isRunning;
283
+ }
284
+
285
+ /**
286
+ * Get the port the server is listening on
287
+ */
288
+ get port(): number {
289
+ return this.options.port;
290
+ }
291
+
292
+ /**
293
+ * Get the host the server is bound to
294
+ */
295
+ get host(): string {
296
+ return this.options.host;
297
+ }
298
+ }