nx-mongo 3.5.0 → 3.8.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.
@@ -195,13 +195,14 @@ class ProgressAPIImpl {
195
195
  * Ensures the progress collection and unique index exist.
196
196
  * Called lazily on first use.
197
197
  */
198
- async ensureProgressIndex(session) {
198
+ async ensureProgressIndex(session, database, ref, type) {
199
199
  if (this.indexEnsured) {
200
200
  return;
201
201
  }
202
202
  this.helper.ensureInitialized();
203
203
  try {
204
- const collection = this.helper.getDb().collection(this.config.collection);
204
+ const db = this.helper.getDatabaseByName(database, ref, type);
205
+ const collection = db.collection(this.config.collection);
205
206
  const indexes = await collection.indexes();
206
207
  // Build index spec from uniqueIndexKeys
207
208
  const indexSpec = {};
@@ -253,12 +254,16 @@ class ProgressAPIImpl {
253
254
  return filter;
254
255
  }
255
256
  async isCompleted(key, options) {
256
- await this.ensureProgressIndex(options?.session);
257
+ const database = options?.database;
258
+ const ref = options?.ref;
259
+ const type = options?.type;
260
+ await this.ensureProgressIndex(options?.session, database, ref, type);
257
261
  this.helper.ensureInitialized();
258
262
  const provider = this.resolveProvider(options);
259
263
  const process = options?.process;
260
264
  const filter = this.buildFilter(key, process, provider);
261
- const collection = this.helper.getDb().collection(this.config.collection);
265
+ const db = this.helper.getDatabaseByName(database, ref, type);
266
+ const collection = db.collection(this.config.collection);
262
267
  const findOptions = {};
263
268
  if (options?.session) {
264
269
  findOptions.session = options.session;
@@ -267,12 +272,16 @@ class ProgressAPIImpl {
267
272
  return record?.completed === true;
268
273
  }
269
274
  async start(identity, options) {
270
- await this.ensureProgressIndex(options?.session);
275
+ const database = options?.database;
276
+ const ref = options?.ref;
277
+ const type = options?.type;
278
+ await this.ensureProgressIndex(options?.session, database, ref, type);
271
279
  this.helper.ensureInitialized();
272
280
  const provider = this.resolveProvider({ provider: identity.provider });
273
281
  const process = identity.process;
274
282
  const filter = this.buildFilter(identity.key, process, provider);
275
- const collection = this.helper.getDb().collection(this.config.collection);
283
+ const db = this.helper.getDatabaseByName(database, ref, type);
284
+ const collection = db.collection(this.config.collection);
276
285
  const update = {
277
286
  $set: {
278
287
  key: identity.key,
@@ -296,12 +305,16 @@ class ProgressAPIImpl {
296
305
  await collection.updateOne(filter, update, updateOptions);
297
306
  }
298
307
  async complete(identity, options) {
299
- await this.ensureProgressIndex(options?.session);
308
+ const database = options?.database;
309
+ const ref = options?.ref;
310
+ const type = options?.type;
311
+ await this.ensureProgressIndex(options?.session, database, ref, type);
300
312
  this.helper.ensureInitialized();
301
313
  const provider = this.resolveProvider({ provider: identity.provider });
302
314
  const process = identity.process;
303
315
  const filter = this.buildFilter(identity.key, process, provider);
304
- const collection = this.helper.getDb().collection(this.config.collection);
316
+ const db = this.helper.getDatabaseByName(database, ref, type);
317
+ const collection = db.collection(this.config.collection);
305
318
  const update = {
306
319
  $set: {
307
320
  key: identity.key,
@@ -329,7 +342,10 @@ class ProgressAPIImpl {
329
342
  await collection.updateOne(filter, update, updateOptions);
330
343
  }
331
344
  async getCompleted(options) {
332
- await this.ensureProgressIndex(options?.session);
345
+ const database = options?.database;
346
+ const ref = options?.ref;
347
+ const type = options?.type;
348
+ await this.ensureProgressIndex(options?.session, database, ref, type);
333
349
  this.helper.ensureInitialized();
334
350
  const provider = this.resolveProvider(options);
335
351
  const process = options?.process;
@@ -340,7 +356,8 @@ class ProgressAPIImpl {
340
356
  if (provider !== undefined) {
341
357
  filter.provider = provider;
342
358
  }
343
- const collection = this.helper.getDb().collection(this.config.collection);
359
+ const db = this.helper.getDatabaseByName(database, ref, type);
360
+ const collection = db.collection(this.config.collection);
344
361
  const findOptions = { projection: { key: 1, name: 1, completedAt: 1 } };
345
362
  if (options?.session) {
346
363
  findOptions.session = options.session;
@@ -353,7 +370,10 @@ class ProgressAPIImpl {
353
370
  }));
354
371
  }
355
372
  async getProgress(options) {
356
- await this.ensureProgressIndex(options?.session);
373
+ const database = options?.database;
374
+ const ref = options?.ref;
375
+ const type = options?.type;
376
+ await this.ensureProgressIndex(options?.session, database, ref, type);
357
377
  this.helper.ensureInitialized();
358
378
  const provider = this.resolveProvider(options);
359
379
  const process = options?.process;
@@ -364,7 +384,8 @@ class ProgressAPIImpl {
364
384
  if (provider !== undefined) {
365
385
  filter.provider = provider;
366
386
  }
367
- const collection = this.helper.getDb().collection(this.config.collection);
387
+ const db = this.helper.getDatabaseByName(database, ref, type);
388
+ const collection = db.collection(this.config.collection);
368
389
  const findOptions = {};
369
390
  if (options?.session) {
370
391
  findOptions.session = options.session;
@@ -372,12 +393,16 @@ class ProgressAPIImpl {
372
393
  return await collection.find(filter, findOptions).toArray();
373
394
  }
374
395
  async reset(key, options) {
375
- await this.ensureProgressIndex(options?.session);
396
+ const database = options?.database;
397
+ const ref = options?.ref;
398
+ const type = options?.type;
399
+ await this.ensureProgressIndex(options?.session, database, ref, type);
376
400
  this.helper.ensureInitialized();
377
401
  const provider = this.resolveProvider(options);
378
402
  const process = options?.process;
379
403
  const filter = this.buildFilter(key, process, provider);
380
- const collection = this.helper.getDb().collection(this.config.collection);
404
+ const db = this.helper.getDatabaseByName(database, ref, type);
405
+ const collection = db.collection(this.config.collection);
381
406
  const update = {
382
407
  $set: {
383
408
  completed: false,
@@ -400,7 +425,10 @@ class SimpleMongoHelper {
400
425
  this.db = null;
401
426
  this.isInitialized = false;
402
427
  this.config = null;
403
- this.connectionString = connectionString;
428
+ this.cleanupRegistered = false;
429
+ this.isDisconnecting = false;
430
+ // Strip database name from connection string if present
431
+ this.connectionString = this.stripDatabaseFromConnectionString(connectionString);
404
432
  this.retryOptions = {
405
433
  maxRetries: retryOptions?.maxRetries ?? 3,
406
434
  retryDelay: retryOptions?.retryDelay ?? 1000,
@@ -408,6 +436,8 @@ class SimpleMongoHelper {
408
436
  };
409
437
  this.config = config || null;
410
438
  this.progress = new ProgressAPIImpl(this, config?.progress);
439
+ // Register automatic cleanup on process exit
440
+ this.registerCleanup();
411
441
  }
412
442
  /**
413
443
  * Sets or updates the configuration for ref-based operations.
@@ -501,9 +531,8 @@ class SimpleMongoHelper {
501
531
  connectTimeoutMS: 5000
502
532
  });
503
533
  await testClient.connect();
504
- // Try to ping the server
505
- const dbName = this.extractDatabaseName(this.connectionString);
506
- const testDb = testClient.db(dbName);
534
+ // Try to ping the server using 'admin' database (default)
535
+ const testDb = testClient.db('admin');
507
536
  await testDb.admin().ping();
508
537
  // Test basic operation (list collections)
509
538
  try {
@@ -623,9 +652,8 @@ class SimpleMongoHelper {
623
652
  try {
624
653
  this.client = new mongodb_1.MongoClient(this.connectionString);
625
654
  await this.client.connect();
626
- // Extract database name from connection string or use default
627
- const dbName = this.extractDatabaseName(this.connectionString);
628
- this.db = this.client.db(dbName);
655
+ // Default to 'admin' database for initial connection (not used for operations)
656
+ this.db = this.client.db('admin');
629
657
  this.isInitialized = true;
630
658
  return;
631
659
  }
@@ -651,10 +679,11 @@ class SimpleMongoHelper {
651
679
  * @returns Array of documents matching the query or paginated result
652
680
  * @throws Error if not initialized or if query fails
653
681
  */
654
- async loadCollection(collectionName, query, options) {
682
+ async loadCollection(collectionName, query, options, database, ref, type) {
655
683
  this.ensureInitialized();
656
684
  try {
657
- const collection = this.db.collection(collectionName);
685
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
686
+ const collection = db.collection(collectionName);
658
687
  const filter = query || {};
659
688
  let cursor = collection.find(filter);
660
689
  // Apply sorting if provided
@@ -697,10 +726,11 @@ class SimpleMongoHelper {
697
726
  * @returns Single document matching the query or null
698
727
  * @throws Error if not initialized or if query fails
699
728
  */
700
- async findOne(collectionName, query, options) {
729
+ async findOne(collectionName, query, options, database, ref, type) {
701
730
  this.ensureInitialized();
702
731
  try {
703
- const collection = this.db.collection(collectionName);
732
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
733
+ const collection = db.collection(collectionName);
704
734
  let findOptions = {};
705
735
  if (options?.sort) {
706
736
  findOptions.sort = options.sort;
@@ -723,10 +753,11 @@ class SimpleMongoHelper {
723
753
  * @returns Insert result(s)
724
754
  * @throws Error if not initialized or if insert fails
725
755
  */
726
- async insert(collectionName, data, options) {
756
+ async insert(collectionName, data, options, database, ref, type) {
727
757
  this.ensureInitialized();
728
758
  try {
729
- const collection = this.db.collection(collectionName);
759
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
760
+ const collection = db.collection(collectionName);
730
761
  const insertOptions = options?.session ? { session: options.session } : {};
731
762
  if (Array.isArray(data)) {
732
763
  const result = await collection.insertMany(data, insertOptions);
@@ -750,10 +781,11 @@ class SimpleMongoHelper {
750
781
  * @returns Update result
751
782
  * @throws Error if not initialized or if update fails
752
783
  */
753
- async update(collectionName, filter, updateData, options) {
784
+ async update(collectionName, filter, updateData, options, database, ref, type) {
754
785
  this.ensureInitialized();
755
786
  try {
756
- const collection = this.db.collection(collectionName);
787
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
788
+ const collection = db.collection(collectionName);
757
789
  const updateOptions = {};
758
790
  if (options?.upsert !== undefined)
759
791
  updateOptions.upsert = options.upsert;
@@ -780,10 +812,11 @@ class SimpleMongoHelper {
780
812
  * @returns Delete result
781
813
  * @throws Error if not initialized or if delete fails
782
814
  */
783
- async delete(collectionName, filter, options) {
815
+ async delete(collectionName, filter, options, database, ref, type) {
784
816
  this.ensureInitialized();
785
817
  try {
786
- const collection = this.db.collection(collectionName);
818
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
819
+ const collection = db.collection(collectionName);
787
820
  if (options?.multi) {
788
821
  const result = await collection.deleteMany(filter);
789
822
  return result;
@@ -804,10 +837,11 @@ class SimpleMongoHelper {
804
837
  * @returns Number of documents matching the query
805
838
  * @throws Error if not initialized or if count fails
806
839
  */
807
- async countDocuments(collectionName, query) {
840
+ async countDocuments(collectionName, query, database, ref, type) {
808
841
  this.ensureInitialized();
809
842
  try {
810
- const collection = this.db.collection(collectionName);
843
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
844
+ const collection = db.collection(collectionName);
811
845
  const count = await collection.countDocuments(query || {});
812
846
  return count;
813
847
  }
@@ -821,10 +855,11 @@ class SimpleMongoHelper {
821
855
  * @returns Estimated number of documents
822
856
  * @throws Error if not initialized or if count fails
823
857
  */
824
- async estimatedDocumentCount(collectionName) {
858
+ async estimatedDocumentCount(collectionName, database, ref, type) {
825
859
  this.ensureInitialized();
826
860
  try {
827
- const collection = this.db.collection(collectionName);
861
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
862
+ const collection = db.collection(collectionName);
828
863
  const count = await collection.estimatedDocumentCount();
829
864
  return count;
830
865
  }
@@ -839,10 +874,11 @@ class SimpleMongoHelper {
839
874
  * @returns Array of aggregated results
840
875
  * @throws Error if not initialized or if aggregation fails
841
876
  */
842
- async aggregate(collectionName, pipeline) {
877
+ async aggregate(collectionName, pipeline, database, ref, type) {
843
878
  this.ensureInitialized();
844
879
  try {
845
- const collection = this.db.collection(collectionName);
880
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
881
+ const collection = db.collection(collectionName);
846
882
  const results = await collection.aggregate(pipeline).toArray();
847
883
  return results;
848
884
  }
@@ -858,10 +894,11 @@ class SimpleMongoHelper {
858
894
  * @returns Index name
859
895
  * @throws Error if not initialized or if index creation fails
860
896
  */
861
- async createIndex(collectionName, indexSpec, options) {
897
+ async createIndex(collectionName, indexSpec, options, database, ref, type) {
862
898
  this.ensureInitialized();
863
899
  try {
864
- const collection = this.db.collection(collectionName);
900
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
901
+ const collection = db.collection(collectionName);
865
902
  const indexName = await collection.createIndex(indexSpec, options);
866
903
  return indexName;
867
904
  }
@@ -876,10 +913,11 @@ class SimpleMongoHelper {
876
913
  * @returns Result object
877
914
  * @throws Error if not initialized or if index drop fails
878
915
  */
879
- async dropIndex(collectionName, indexName) {
916
+ async dropIndex(collectionName, indexName, database, ref, type) {
880
917
  this.ensureInitialized();
881
918
  try {
882
- const collection = this.db.collection(collectionName);
919
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
920
+ const collection = db.collection(collectionName);
883
921
  const result = await collection.dropIndex(indexName);
884
922
  return result;
885
923
  }
@@ -893,10 +931,11 @@ class SimpleMongoHelper {
893
931
  * @returns Array of index information
894
932
  * @throws Error if not initialized or if listing fails
895
933
  */
896
- async listIndexes(collectionName) {
934
+ async listIndexes(collectionName, database, ref, type) {
897
935
  this.ensureInitialized();
898
936
  try {
899
- const collection = this.db.collection(collectionName);
937
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
938
+ const collection = db.collection(collectionName);
900
939
  const indexes = await collection.indexes();
901
940
  return indexes;
902
941
  }
@@ -958,9 +997,13 @@ class SimpleMongoHelper {
958
997
  if (!inputConfig) {
959
998
  throw new Error(`Ref '${ref}' not found in configuration inputs.`);
960
999
  }
1000
+ const database = options?.database;
1001
+ const dbRef = options?.ref;
1002
+ const dbType = options?.type;
1003
+ const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
961
1004
  // Note: loadCollection doesn't support session yet, but we'll pass it through options
962
1005
  // For now, we'll use the collection directly with session support
963
- const collection = this.db.collection(inputConfig.collection);
1006
+ const collection = db.collection(inputConfig.collection);
964
1007
  const filter = inputConfig.query || {};
965
1008
  const session = options?.session;
966
1009
  try {
@@ -1015,8 +1058,12 @@ class SimpleMongoHelper {
1015
1058
  this.ensureInitialized();
1016
1059
  const fieldName = options?.fieldName || '_sig';
1017
1060
  const unique = options?.unique !== false; // Default to true
1061
+ const database = options?.database;
1062
+ const ref = options?.ref;
1063
+ const type = options?.type;
1018
1064
  try {
1019
- const collection = this.db.collection(collectionName);
1065
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
1066
+ const collection = db.collection(collectionName);
1020
1067
  const indexes = await collection.indexes();
1021
1068
  // Find existing index on the signature field
1022
1069
  const existingIndex = indexes.find(idx => {
@@ -1086,6 +1133,9 @@ class SimpleMongoHelper {
1086
1133
  const mode = outputConfig.mode || this.config.output?.mode || 'append';
1087
1134
  const ensureIndex = options?.ensureIndex !== false; // Default to true
1088
1135
  const session = options?.session;
1136
+ const database = options?.database;
1137
+ const dbRef = options?.ref;
1138
+ const dbType = options?.type;
1089
1139
  const result = {
1090
1140
  inserted: 0,
1091
1141
  updated: 0,
@@ -1093,7 +1143,8 @@ class SimpleMongoHelper {
1093
1143
  indexCreated: false,
1094
1144
  };
1095
1145
  try {
1096
- const collection = this.db.collection(collectionName);
1146
+ const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
1147
+ const collection = db.collection(collectionName);
1097
1148
  // Handle replace mode: clear collection first
1098
1149
  if (mode === 'replace') {
1099
1150
  const deleteOptions = session ? { session } : {};
@@ -1247,6 +1298,9 @@ class SimpleMongoHelper {
1247
1298
  const writeResult = await this.writeByRef(ref, documents, {
1248
1299
  session: options?.session,
1249
1300
  ensureIndex: options?.ensureIndex,
1301
+ database: options?.database,
1302
+ ref: options?.ref,
1303
+ type: options?.type,
1250
1304
  });
1251
1305
  const result = {
1252
1306
  ...writeResult,
@@ -1261,7 +1315,7 @@ class SimpleMongoHelper {
1261
1315
  name: options.complete.name,
1262
1316
  provider: options.complete.provider,
1263
1317
  metadata: options.complete.metadata,
1264
- }, { session: options.session });
1318
+ }, { session: options.session, database: options?.database, ref: options?.ref, type: options?.type });
1265
1319
  result.completed = true;
1266
1320
  }
1267
1321
  catch (error) {
@@ -1282,7 +1336,7 @@ class SimpleMongoHelper {
1282
1336
  */
1283
1337
  async mergeCollections(options) {
1284
1338
  this.ensureInitialized();
1285
- const { sourceCollection1, sourceCollection2, targetCollection, strategy, key, compositeKeys, joinType, fieldPrefix1 = 'record', fieldPrefix2 = 'assessment', includeIndex = strategy === 'index', onUnmatched1 = 'include', onUnmatched2 = 'include', session, } = options;
1339
+ const { sourceCollection1, sourceCollection2, targetCollection, strategy, key, compositeKeys, joinType, fieldPrefix1 = 'record', fieldPrefix2 = 'assessment', includeIndex = strategy === 'index', onUnmatched1 = 'include', onUnmatched2 = 'include', session, database, ref, type, } = options;
1286
1340
  // Determine join behavior from joinType or fallback to onUnmatched flags
1287
1341
  let includeUnmatched1;
1288
1342
  let includeUnmatched2;
@@ -1322,9 +1376,10 @@ class SimpleMongoHelper {
1322
1376
  errors: [],
1323
1377
  };
1324
1378
  try {
1325
- const coll1 = this.db.collection(sourceCollection1);
1326
- const coll2 = this.db.collection(sourceCollection2);
1327
- const targetColl = this.db.collection(targetCollection);
1379
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
1380
+ const coll1 = db.collection(sourceCollection1);
1381
+ const coll2 = db.collection(sourceCollection2);
1382
+ const targetColl = db.collection(targetCollection);
1328
1383
  const findOptions = {};
1329
1384
  if (session) {
1330
1385
  findOptions.session = session;
@@ -1607,14 +1662,84 @@ class SimpleMongoHelper {
1607
1662
  throw new Error(`Failed to merge collections: ${error instanceof Error ? error.message : String(error)}`);
1608
1663
  }
1609
1664
  }
1665
+ /**
1666
+ * Registers automatic cleanup handlers for process exit events.
1667
+ * This ensures connections are closed gracefully when the application terminates.
1668
+ * Uses a global registry to handle multiple instances without conflicts.
1669
+ * @internal
1670
+ */
1671
+ registerCleanup() {
1672
+ if (this.cleanupRegistered) {
1673
+ return;
1674
+ }
1675
+ this.cleanupRegistered = true;
1676
+ // Global registry for all SimpleMongoHelper instances
1677
+ const globalRegistry = global.__nxMongoHelperInstances || new Set();
1678
+ global.__nxMongoHelperInstances = globalRegistry;
1679
+ globalRegistry.add(this);
1680
+ // Register global cleanup handlers only once
1681
+ if (!global.__nxMongoCleanupRegistered) {
1682
+ global.__nxMongoCleanupRegistered = true;
1683
+ if (typeof process !== 'undefined') {
1684
+ const cleanupAll = async (signal) => {
1685
+ const instances = global.__nxMongoHelperInstances;
1686
+ if (!instances || instances.size === 0) {
1687
+ return;
1688
+ }
1689
+ // Cleanup all instances in parallel with timeout
1690
+ const cleanupPromises = Array.from(instances).map(async (instance) => {
1691
+ try {
1692
+ // Use the public disconnect method, but with timeout protection
1693
+ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Disconnect timeout')), 5000));
1694
+ await Promise.race([
1695
+ instance.disconnect(),
1696
+ timeout
1697
+ ]).catch(() => {
1698
+ // Ignore timeout errors during automatic cleanup
1699
+ });
1700
+ }
1701
+ catch (error) {
1702
+ // Silently ignore errors during automatic cleanup
1703
+ }
1704
+ });
1705
+ await Promise.all(cleanupPromises).catch(() => {
1706
+ // Ignore errors during cleanup
1707
+ });
1708
+ };
1709
+ // Graceful shutdown signals
1710
+ process.once('SIGINT', () => {
1711
+ cleanupAll('SIGINT').finally(() => {
1712
+ process.exit(0);
1713
+ });
1714
+ });
1715
+ process.once('SIGTERM', () => {
1716
+ cleanupAll('SIGTERM').finally(() => {
1717
+ process.exit(0);
1718
+ });
1719
+ });
1720
+ // Before exit (allows async cleanup)
1721
+ process.once('beforeExit', () => {
1722
+ cleanupAll('beforeExit');
1723
+ });
1724
+ // Note: We don't handle uncaughtException/unhandledRejection here
1725
+ // as those should be handled by the application, not the library
1726
+ // The beforeExit handler will still clean up connections
1727
+ }
1728
+ }
1729
+ }
1610
1730
  /**
1611
1731
  * Closes the MongoDB connection and cleans up resources.
1612
- * @throws Error if disconnect fails
1732
+ * This method is called automatically on process exit, but can also be called manually.
1733
+ * @throws Error if disconnect fails (only when called manually, not during automatic cleanup)
1613
1734
  */
1614
1735
  async disconnect() {
1736
+ if (this.isDisconnecting) {
1737
+ return;
1738
+ }
1615
1739
  if (!this.isInitialized || !this.client) {
1616
1740
  return;
1617
1741
  }
1742
+ this.isDisconnecting = true;
1618
1743
  try {
1619
1744
  await this.client.close();
1620
1745
  this.client = null;
@@ -1622,8 +1747,12 @@ class SimpleMongoHelper {
1622
1747
  this.isInitialized = false;
1623
1748
  }
1624
1749
  catch (error) {
1750
+ this.isDisconnecting = false;
1625
1751
  throw new Error(`Failed to disconnect: ${error instanceof Error ? error.message : String(error)}`);
1626
1752
  }
1753
+ finally {
1754
+ this.isDisconnecting = false;
1755
+ }
1627
1756
  }
1628
1757
  /**
1629
1758
  * Ensures the helper is initialized before performing operations.
@@ -1646,22 +1775,111 @@ class SimpleMongoHelper {
1646
1775
  return this.db;
1647
1776
  }
1648
1777
  /**
1649
- * Extracts database name from MongoDB connection string.
1650
- * @param connectionString - MongoDB connection string
1651
- * @returns Database name or 'test' as default
1778
+ * Strips database name from MongoDB connection string, returning base connection string.
1779
+ * @param connectionString - MongoDB connection string (may include database name)
1780
+ * @returns Base connection string without database name
1781
+ * @example
1782
+ * stripDatabaseFromConnectionString('mongodb://localhost:27017/admin')
1783
+ * // Returns: 'mongodb://localhost:27017/'
1652
1784
  */
1653
- extractDatabaseName(connectionString) {
1785
+ stripDatabaseFromConnectionString(connectionString) {
1654
1786
  try {
1655
1787
  const url = new URL(connectionString);
1656
- const dbName = url.pathname.slice(1); // Remove leading '/'
1657
- return dbName || 'test';
1788
+ // Remove pathname (database name) but keep trailing slash
1789
+ url.pathname = '/';
1790
+ return url.toString();
1658
1791
  }
1659
1792
  catch {
1660
- // If URL parsing fails, try to extract from connection string pattern
1661
- const match = connectionString.match(/\/([^\/\?]+)(\?|$)/);
1662
- return match ? match[1] : 'test';
1793
+ // If URL parsing fails, try regex pattern matching
1794
+ // Match: mongodb://host:port/database?options or mongodb://host:port/database
1795
+ const match = connectionString.match(/^([^\/]+\/\/[^\/]+)\/([^\/\?]+)(\?.*)?$/);
1796
+ if (match) {
1797
+ // Return base URL with trailing slash
1798
+ return match[1] + '/';
1799
+ }
1800
+ // If no database found, return as-is (should already be base URL)
1801
+ return connectionString.endsWith('/') ? connectionString : connectionString + '/';
1663
1802
  }
1664
1803
  }
1804
+ /**
1805
+ * Resolves the database name from provided options using the databases config map.
1806
+ * Priority: database > ref+type > ref > type
1807
+ * @param options - Options containing database, ref, and/or type
1808
+ * @returns Resolved database name or undefined (will default to 'admin')
1809
+ * @throws Error if no match found or multiple matches found
1810
+ * @internal
1811
+ */
1812
+ resolveDatabase(options) {
1813
+ // Priority 1: If database is provided directly, use it
1814
+ if (options?.database) {
1815
+ return options.database;
1816
+ }
1817
+ // If no config or no databases map, return undefined (will default to 'admin')
1818
+ if (!this.config || !this.config.databases || this.config.databases.length === 0) {
1819
+ return undefined;
1820
+ }
1821
+ const databases = this.config.databases;
1822
+ // Priority 2: If both ref and type are provided, find exact match
1823
+ if (options?.ref && options?.type) {
1824
+ const matches = databases.filter(db => db.ref === options.ref && db.type === options.type);
1825
+ if (matches.length === 0) {
1826
+ throw new Error(`No database found for ref: ${options.ref} and type: ${options.type}`);
1827
+ }
1828
+ if (matches.length > 1) {
1829
+ throw new Error(`Multiple databases found for ref: ${options.ref} and type: ${options.type}`);
1830
+ }
1831
+ return matches[0].database;
1832
+ }
1833
+ // Priority 3: If ref is provided, find by ref
1834
+ if (options?.ref) {
1835
+ const matches = databases.filter(db => db.ref === options.ref);
1836
+ if (matches.length === 0) {
1837
+ throw new Error(`No database found for ref: ${options.ref}`);
1838
+ }
1839
+ if (matches.length > 1) {
1840
+ throw new Error(`Multiple databases found for ref: ${options.ref}. Use 'type' parameter to narrow down.`);
1841
+ }
1842
+ return matches[0].database;
1843
+ }
1844
+ // Priority 4: If type is provided, find by type
1845
+ if (options?.type) {
1846
+ const matches = databases.filter(db => db.type === options.type);
1847
+ if (matches.length === 0) {
1848
+ throw new Error(`No database found for type: ${options.type}`);
1849
+ }
1850
+ if (matches.length > 1) {
1851
+ throw new Error(`Multiple databases found for type: ${options.type}. Use 'ref' parameter to narrow down.`);
1852
+ }
1853
+ return matches[0].database;
1854
+ }
1855
+ // No options provided, return undefined (will default to 'admin')
1856
+ return undefined;
1857
+ }
1858
+ /**
1859
+ * Gets a database instance by name. Defaults to 'admin' if no name provided.
1860
+ * @param databaseName - Optional database name (defaults to 'admin')
1861
+ * @returns Database instance
1862
+ * @throws Error if client is not initialized
1863
+ * @internal
1864
+ */
1865
+ getDatabase(databaseName) {
1866
+ if (!this.client) {
1867
+ throw new Error('MongoDB client not initialized. Call initialize() first.');
1868
+ }
1869
+ return this.client.db(databaseName || 'admin');
1870
+ }
1871
+ /**
1872
+ * Gets a database instance by name. Defaults to 'admin' if no name provided.
1873
+ * Public method for use by ProgressAPIImpl.
1874
+ * @param databaseName - Optional database name (defaults to 'admin')
1875
+ * @param ref - Optional ref for database resolution
1876
+ * @param type - Optional type for database resolution
1877
+ * @returns Database instance
1878
+ * @throws Error if client is not initialized
1879
+ */
1880
+ getDatabaseByName(databaseName, ref, type) {
1881
+ return this.getDatabase(this.resolveDatabase({ database: databaseName, ref, type }));
1882
+ }
1665
1883
  }
1666
1884
  exports.SimpleMongoHelper = SimpleMongoHelper;
1667
1885
  //# sourceMappingURL=simpleMongoHelper.js.map