nx-mongo 3.6.0 → 3.8.1

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,
@@ -402,7 +427,8 @@ class SimpleMongoHelper {
402
427
  this.config = null;
403
428
  this.cleanupRegistered = false;
404
429
  this.isDisconnecting = false;
405
- this.connectionString = connectionString;
430
+ // Strip database name from connection string if present
431
+ this.connectionString = this.stripDatabaseFromConnectionString(connectionString);
406
432
  this.retryOptions = {
407
433
  maxRetries: retryOptions?.maxRetries ?? 3,
408
434
  retryDelay: retryOptions?.retryDelay ?? 1000,
@@ -505,9 +531,8 @@ class SimpleMongoHelper {
505
531
  connectTimeoutMS: 5000
506
532
  });
507
533
  await testClient.connect();
508
- // Try to ping the server
509
- const dbName = this.extractDatabaseName(this.connectionString);
510
- const testDb = testClient.db(dbName);
534
+ // Try to ping the server using 'admin' database (default)
535
+ const testDb = testClient.db('admin');
511
536
  await testDb.admin().ping();
512
537
  // Test basic operation (list collections)
513
538
  try {
@@ -540,75 +565,115 @@ class SimpleMongoHelper {
540
565
  // Ignore close errors
541
566
  }
542
567
  }
543
- const errorMessage = error instanceof Error ? error.message : String(error);
544
- const errorString = String(error).toLowerCase();
568
+ // Extract error information more comprehensively
569
+ let errorMessage = 'Unknown error';
570
+ let errorName = 'Unknown';
571
+ let errorCode;
572
+ if (error instanceof Error) {
573
+ errorMessage = error.message || 'Unknown error';
574
+ errorName = error.name || 'Error';
575
+ // Check for MongoDB-specific error properties
576
+ const mongoError = error;
577
+ if (mongoError.code) {
578
+ errorCode = mongoError.code;
579
+ }
580
+ if (mongoError.codeName) {
581
+ errorName = mongoError.codeName;
582
+ }
583
+ if (mongoError.errmsg) {
584
+ errorMessage = mongoError.errmsg;
585
+ }
586
+ }
587
+ else if (error && typeof error === 'object') {
588
+ // Try to extract meaningful information from error object
589
+ const errObj = error;
590
+ errorMessage = errObj.message || errObj.errmsg || errObj.error || JSON.stringify(error);
591
+ errorName = errObj.name || errObj.codeName || 'Error';
592
+ errorCode = errObj.code;
593
+ }
594
+ else {
595
+ errorMessage = String(error);
596
+ }
597
+ const errorMessageLower = errorMessage.toLowerCase();
598
+ // Check for Windows-specific localhost resolution issues
599
+ const isLocalhost = this.connectionString.includes('localhost');
600
+ const windowsHint = isLocalhost && process.platform === 'win32'
601
+ ? ' On Windows, try using 127.0.0.1 instead of localhost to avoid IPv6 resolution issues.'
602
+ : '';
545
603
  // Analyze error type
546
- if (errorString.includes('authentication failed') ||
547
- errorString.includes('auth failed') ||
548
- errorString.includes('invalid credentials') ||
549
- errorMessage.includes('Authentication failed')) {
604
+ if (errorMessageLower.includes('authentication failed') ||
605
+ errorMessageLower.includes('auth failed') ||
606
+ errorMessageLower.includes('invalid credentials') ||
607
+ errorMessageLower.includes('authentication failed') ||
608
+ errorCode === 18 || // MongoDB error code for authentication failed
609
+ errorName === 'MongoAuthenticationError') {
550
610
  return {
551
611
  success: false,
552
612
  error: {
553
613
  type: 'authentication_failed',
554
614
  message: 'Authentication failed',
555
- details: `Invalid username or password. Error: ${errorMessage}. Verify credentials in connection string.`
615
+ details: `Invalid username or password. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Verify credentials in connection string.`
556
616
  }
557
617
  };
558
618
  }
559
- if (errorString.includes('timeout') ||
560
- errorMessage.includes('timeout') ||
561
- errorMessage.includes('ETIMEDOUT')) {
619
+ if (errorMessageLower.includes('timeout') ||
620
+ errorMessageLower.includes('etimedout') ||
621
+ errorCode === 'ETIMEDOUT' ||
622
+ errorName === 'MongoServerSelectionError') {
562
623
  return {
563
624
  success: false,
564
625
  error: {
565
626
  type: 'connection_failed',
566
627
  message: 'Connection timeout',
567
- details: `Cannot reach MongoDB server. Error: ${errorMessage}. Check if server is running, host/port is correct, and firewall allows connections.`
628
+ details: `Cannot reach MongoDB server. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check if server is running, host/port is correct, and firewall allows connections.${windowsHint}`
568
629
  }
569
630
  };
570
631
  }
571
- if (errorString.includes('dns') ||
572
- errorMessage.includes('ENOTFOUND') ||
573
- errorMessage.includes('getaddrinfo')) {
632
+ if (errorMessageLower.includes('dns') ||
633
+ errorMessageLower.includes('enotfound') ||
634
+ errorMessageLower.includes('getaddrinfo') ||
635
+ errorCode === 'ENOTFOUND') {
574
636
  return {
575
637
  success: false,
576
638
  error: {
577
639
  type: 'connection_failed',
578
640
  message: 'DNS resolution failed',
579
- details: `Cannot resolve hostname. Error: ${errorMessage}. Check if hostname is correct and DNS is configured properly.`
641
+ details: `Cannot resolve hostname. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check if hostname is correct and DNS is configured properly.${windowsHint}`
580
642
  }
581
643
  };
582
644
  }
583
- if (errorString.includes('refused') ||
584
- errorMessage.includes('ECONNREFUSED')) {
645
+ if (errorMessageLower.includes('refused') ||
646
+ errorMessageLower.includes('econnrefused') ||
647
+ errorCode === 'ECONNREFUSED') {
585
648
  return {
586
649
  success: false,
587
650
  error: {
588
651
  type: 'connection_failed',
589
652
  message: 'Connection refused',
590
- details: `MongoDB server refused connection. Error: ${errorMessage}. Check if MongoDB is running on the specified host and port.`
653
+ details: `MongoDB server refused connection. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check if MongoDB is running on the specified host and port.${windowsHint}`
591
654
  }
592
655
  };
593
656
  }
594
- if (errorString.includes('network') ||
595
- errorMessage.includes('network')) {
657
+ if (errorMessageLower.includes('network') ||
658
+ errorMessageLower.includes('network') ||
659
+ errorName === 'MongoNetworkError') {
596
660
  return {
597
661
  success: false,
598
662
  error: {
599
663
  type: 'connection_failed',
600
664
  message: 'Network error',
601
- details: `Network connectivity issue. Error: ${errorMessage}. Check network connection and firewall settings.`
665
+ details: `Network connectivity issue. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check network connection and firewall settings.${windowsHint}`
602
666
  }
603
667
  };
604
668
  }
605
- // Generic connection error
669
+ // Generic connection error with full details
670
+ const details = `Failed to connect to MongoDB. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}${errorName !== 'Error' ? ` (Type: ${errorName})` : ''}. Verify connection string, server status, and network connectivity.${windowsHint}`;
606
671
  return {
607
672
  success: false,
608
673
  error: {
609
674
  type: 'connection_failed',
610
675
  message: 'Connection failed',
611
- details: `Failed to connect to MongoDB. Error: ${errorMessage}. Verify connection string, server status, and network connectivity.`
676
+ details
612
677
  }
613
678
  };
614
679
  }
@@ -627,9 +692,8 @@ class SimpleMongoHelper {
627
692
  try {
628
693
  this.client = new mongodb_1.MongoClient(this.connectionString);
629
694
  await this.client.connect();
630
- // Extract database name from connection string or use default
631
- const dbName = this.extractDatabaseName(this.connectionString);
632
- this.db = this.client.db(dbName);
695
+ // Default to 'admin' database for initial connection (not used for operations)
696
+ this.db = this.client.db('admin');
633
697
  this.isInitialized = true;
634
698
  return;
635
699
  }
@@ -655,10 +719,11 @@ class SimpleMongoHelper {
655
719
  * @returns Array of documents matching the query or paginated result
656
720
  * @throws Error if not initialized or if query fails
657
721
  */
658
- async loadCollection(collectionName, query, options) {
722
+ async loadCollection(collectionName, query, options, database, ref, type) {
659
723
  this.ensureInitialized();
660
724
  try {
661
- const collection = this.db.collection(collectionName);
725
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
726
+ const collection = db.collection(collectionName);
662
727
  const filter = query || {};
663
728
  let cursor = collection.find(filter);
664
729
  // Apply sorting if provided
@@ -701,10 +766,11 @@ class SimpleMongoHelper {
701
766
  * @returns Single document matching the query or null
702
767
  * @throws Error if not initialized or if query fails
703
768
  */
704
- async findOne(collectionName, query, options) {
769
+ async findOne(collectionName, query, options, database, ref, type) {
705
770
  this.ensureInitialized();
706
771
  try {
707
- const collection = this.db.collection(collectionName);
772
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
773
+ const collection = db.collection(collectionName);
708
774
  let findOptions = {};
709
775
  if (options?.sort) {
710
776
  findOptions.sort = options.sort;
@@ -727,10 +793,11 @@ class SimpleMongoHelper {
727
793
  * @returns Insert result(s)
728
794
  * @throws Error if not initialized or if insert fails
729
795
  */
730
- async insert(collectionName, data, options) {
796
+ async insert(collectionName, data, options, database, ref, type) {
731
797
  this.ensureInitialized();
732
798
  try {
733
- const collection = this.db.collection(collectionName);
799
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
800
+ const collection = db.collection(collectionName);
734
801
  const insertOptions = options?.session ? { session: options.session } : {};
735
802
  if (Array.isArray(data)) {
736
803
  const result = await collection.insertMany(data, insertOptions);
@@ -754,10 +821,11 @@ class SimpleMongoHelper {
754
821
  * @returns Update result
755
822
  * @throws Error if not initialized or if update fails
756
823
  */
757
- async update(collectionName, filter, updateData, options) {
824
+ async update(collectionName, filter, updateData, options, database, ref, type) {
758
825
  this.ensureInitialized();
759
826
  try {
760
- const collection = this.db.collection(collectionName);
827
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
828
+ const collection = db.collection(collectionName);
761
829
  const updateOptions = {};
762
830
  if (options?.upsert !== undefined)
763
831
  updateOptions.upsert = options.upsert;
@@ -784,10 +852,11 @@ class SimpleMongoHelper {
784
852
  * @returns Delete result
785
853
  * @throws Error if not initialized or if delete fails
786
854
  */
787
- async delete(collectionName, filter, options) {
855
+ async delete(collectionName, filter, options, database, ref, type) {
788
856
  this.ensureInitialized();
789
857
  try {
790
- const collection = this.db.collection(collectionName);
858
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
859
+ const collection = db.collection(collectionName);
791
860
  if (options?.multi) {
792
861
  const result = await collection.deleteMany(filter);
793
862
  return result;
@@ -808,10 +877,11 @@ class SimpleMongoHelper {
808
877
  * @returns Number of documents matching the query
809
878
  * @throws Error if not initialized or if count fails
810
879
  */
811
- async countDocuments(collectionName, query) {
880
+ async countDocuments(collectionName, query, database, ref, type) {
812
881
  this.ensureInitialized();
813
882
  try {
814
- const collection = this.db.collection(collectionName);
883
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
884
+ const collection = db.collection(collectionName);
815
885
  const count = await collection.countDocuments(query || {});
816
886
  return count;
817
887
  }
@@ -825,10 +895,11 @@ class SimpleMongoHelper {
825
895
  * @returns Estimated number of documents
826
896
  * @throws Error if not initialized or if count fails
827
897
  */
828
- async estimatedDocumentCount(collectionName) {
898
+ async estimatedDocumentCount(collectionName, database, ref, type) {
829
899
  this.ensureInitialized();
830
900
  try {
831
- const collection = this.db.collection(collectionName);
901
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
902
+ const collection = db.collection(collectionName);
832
903
  const count = await collection.estimatedDocumentCount();
833
904
  return count;
834
905
  }
@@ -843,10 +914,11 @@ class SimpleMongoHelper {
843
914
  * @returns Array of aggregated results
844
915
  * @throws Error if not initialized or if aggregation fails
845
916
  */
846
- async aggregate(collectionName, pipeline) {
917
+ async aggregate(collectionName, pipeline, database, ref, type) {
847
918
  this.ensureInitialized();
848
919
  try {
849
- const collection = this.db.collection(collectionName);
920
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
921
+ const collection = db.collection(collectionName);
850
922
  const results = await collection.aggregate(pipeline).toArray();
851
923
  return results;
852
924
  }
@@ -862,10 +934,11 @@ class SimpleMongoHelper {
862
934
  * @returns Index name
863
935
  * @throws Error if not initialized or if index creation fails
864
936
  */
865
- async createIndex(collectionName, indexSpec, options) {
937
+ async createIndex(collectionName, indexSpec, options, database, ref, type) {
866
938
  this.ensureInitialized();
867
939
  try {
868
- const collection = this.db.collection(collectionName);
940
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
941
+ const collection = db.collection(collectionName);
869
942
  const indexName = await collection.createIndex(indexSpec, options);
870
943
  return indexName;
871
944
  }
@@ -880,10 +953,11 @@ class SimpleMongoHelper {
880
953
  * @returns Result object
881
954
  * @throws Error if not initialized or if index drop fails
882
955
  */
883
- async dropIndex(collectionName, indexName) {
956
+ async dropIndex(collectionName, indexName, database, ref, type) {
884
957
  this.ensureInitialized();
885
958
  try {
886
- const collection = this.db.collection(collectionName);
959
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
960
+ const collection = db.collection(collectionName);
887
961
  const result = await collection.dropIndex(indexName);
888
962
  return result;
889
963
  }
@@ -897,10 +971,11 @@ class SimpleMongoHelper {
897
971
  * @returns Array of index information
898
972
  * @throws Error if not initialized or if listing fails
899
973
  */
900
- async listIndexes(collectionName) {
974
+ async listIndexes(collectionName, database, ref, type) {
901
975
  this.ensureInitialized();
902
976
  try {
903
- const collection = this.db.collection(collectionName);
977
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
978
+ const collection = db.collection(collectionName);
904
979
  const indexes = await collection.indexes();
905
980
  return indexes;
906
981
  }
@@ -962,9 +1037,13 @@ class SimpleMongoHelper {
962
1037
  if (!inputConfig) {
963
1038
  throw new Error(`Ref '${ref}' not found in configuration inputs.`);
964
1039
  }
1040
+ const database = options?.database;
1041
+ const dbRef = options?.ref;
1042
+ const dbType = options?.type;
1043
+ const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
965
1044
  // Note: loadCollection doesn't support session yet, but we'll pass it through options
966
1045
  // For now, we'll use the collection directly with session support
967
- const collection = this.db.collection(inputConfig.collection);
1046
+ const collection = db.collection(inputConfig.collection);
968
1047
  const filter = inputConfig.query || {};
969
1048
  const session = options?.session;
970
1049
  try {
@@ -1019,8 +1098,12 @@ class SimpleMongoHelper {
1019
1098
  this.ensureInitialized();
1020
1099
  const fieldName = options?.fieldName || '_sig';
1021
1100
  const unique = options?.unique !== false; // Default to true
1101
+ const database = options?.database;
1102
+ const ref = options?.ref;
1103
+ const type = options?.type;
1022
1104
  try {
1023
- const collection = this.db.collection(collectionName);
1105
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
1106
+ const collection = db.collection(collectionName);
1024
1107
  const indexes = await collection.indexes();
1025
1108
  // Find existing index on the signature field
1026
1109
  const existingIndex = indexes.find(idx => {
@@ -1090,6 +1173,9 @@ class SimpleMongoHelper {
1090
1173
  const mode = outputConfig.mode || this.config.output?.mode || 'append';
1091
1174
  const ensureIndex = options?.ensureIndex !== false; // Default to true
1092
1175
  const session = options?.session;
1176
+ const database = options?.database;
1177
+ const dbRef = options?.ref;
1178
+ const dbType = options?.type;
1093
1179
  const result = {
1094
1180
  inserted: 0,
1095
1181
  updated: 0,
@@ -1097,7 +1183,8 @@ class SimpleMongoHelper {
1097
1183
  indexCreated: false,
1098
1184
  };
1099
1185
  try {
1100
- const collection = this.db.collection(collectionName);
1186
+ const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
1187
+ const collection = db.collection(collectionName);
1101
1188
  // Handle replace mode: clear collection first
1102
1189
  if (mode === 'replace') {
1103
1190
  const deleteOptions = session ? { session } : {};
@@ -1251,6 +1338,9 @@ class SimpleMongoHelper {
1251
1338
  const writeResult = await this.writeByRef(ref, documents, {
1252
1339
  session: options?.session,
1253
1340
  ensureIndex: options?.ensureIndex,
1341
+ database: options?.database,
1342
+ ref: options?.ref,
1343
+ type: options?.type,
1254
1344
  });
1255
1345
  const result = {
1256
1346
  ...writeResult,
@@ -1265,7 +1355,7 @@ class SimpleMongoHelper {
1265
1355
  name: options.complete.name,
1266
1356
  provider: options.complete.provider,
1267
1357
  metadata: options.complete.metadata,
1268
- }, { session: options.session });
1358
+ }, { session: options.session, database: options?.database, ref: options?.ref, type: options?.type });
1269
1359
  result.completed = true;
1270
1360
  }
1271
1361
  catch (error) {
@@ -1286,7 +1376,7 @@ class SimpleMongoHelper {
1286
1376
  */
1287
1377
  async mergeCollections(options) {
1288
1378
  this.ensureInitialized();
1289
- const { sourceCollection1, sourceCollection2, targetCollection, strategy, key, compositeKeys, joinType, fieldPrefix1 = 'record', fieldPrefix2 = 'assessment', includeIndex = strategy === 'index', onUnmatched1 = 'include', onUnmatched2 = 'include', session, } = options;
1379
+ const { sourceCollection1, sourceCollection2, targetCollection, strategy, key, compositeKeys, joinType, fieldPrefix1 = 'record', fieldPrefix2 = 'assessment', includeIndex = strategy === 'index', onUnmatched1 = 'include', onUnmatched2 = 'include', session, database, ref, type, } = options;
1290
1380
  // Determine join behavior from joinType or fallback to onUnmatched flags
1291
1381
  let includeUnmatched1;
1292
1382
  let includeUnmatched2;
@@ -1326,9 +1416,10 @@ class SimpleMongoHelper {
1326
1416
  errors: [],
1327
1417
  };
1328
1418
  try {
1329
- const coll1 = this.db.collection(sourceCollection1);
1330
- const coll2 = this.db.collection(sourceCollection2);
1331
- const targetColl = this.db.collection(targetCollection);
1419
+ const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
1420
+ const coll1 = db.collection(sourceCollection1);
1421
+ const coll2 = db.collection(sourceCollection2);
1422
+ const targetColl = db.collection(targetCollection);
1332
1423
  const findOptions = {};
1333
1424
  if (session) {
1334
1425
  findOptions.session = session;
@@ -1724,21 +1815,110 @@ class SimpleMongoHelper {
1724
1815
  return this.db;
1725
1816
  }
1726
1817
  /**
1727
- * Extracts database name from MongoDB connection string.
1728
- * @param connectionString - MongoDB connection string
1729
- * @returns Database name or 'test' as default
1818
+ * Strips database name from MongoDB connection string, returning base connection string.
1819
+ * @param connectionString - MongoDB connection string (may include database name)
1820
+ * @returns Base connection string without database name
1821
+ * @example
1822
+ * stripDatabaseFromConnectionString('mongodb://localhost:27017/admin')
1823
+ * // Returns: 'mongodb://localhost:27017/'
1730
1824
  */
1731
- extractDatabaseName(connectionString) {
1825
+ stripDatabaseFromConnectionString(connectionString) {
1732
1826
  try {
1733
1827
  const url = new URL(connectionString);
1734
- const dbName = url.pathname.slice(1); // Remove leading '/'
1735
- return dbName || 'test';
1828
+ // Remove pathname (database name) but keep trailing slash
1829
+ url.pathname = '/';
1830
+ return url.toString();
1736
1831
  }
1737
1832
  catch {
1738
- // If URL parsing fails, try to extract from connection string pattern
1739
- const match = connectionString.match(/\/([^\/\?]+)(\?|$)/);
1740
- return match ? match[1] : 'test';
1833
+ // If URL parsing fails, try regex pattern matching
1834
+ // Match: mongodb://host:port/database?options or mongodb://host:port/database
1835
+ const match = connectionString.match(/^([^\/]+\/\/[^\/]+)\/([^\/\?]+)(\?.*)?$/);
1836
+ if (match) {
1837
+ // Return base URL with trailing slash
1838
+ return match[1] + '/';
1839
+ }
1840
+ // If no database found, return as-is (should already be base URL)
1841
+ return connectionString.endsWith('/') ? connectionString : connectionString + '/';
1842
+ }
1843
+ }
1844
+ /**
1845
+ * Resolves the database name from provided options using the databases config map.
1846
+ * Priority: database > ref+type > ref > type
1847
+ * @param options - Options containing database, ref, and/or type
1848
+ * @returns Resolved database name or undefined (will default to 'admin')
1849
+ * @throws Error if no match found or multiple matches found
1850
+ * @internal
1851
+ */
1852
+ resolveDatabase(options) {
1853
+ // Priority 1: If database is provided directly, use it
1854
+ if (options?.database) {
1855
+ return options.database;
1856
+ }
1857
+ // If no config or no databases map, return undefined (will default to 'admin')
1858
+ if (!this.config || !this.config.databases || this.config.databases.length === 0) {
1859
+ return undefined;
1860
+ }
1861
+ const databases = this.config.databases;
1862
+ // Priority 2: If both ref and type are provided, find exact match
1863
+ if (options?.ref && options?.type) {
1864
+ const matches = databases.filter(db => db.ref === options.ref && db.type === options.type);
1865
+ if (matches.length === 0) {
1866
+ throw new Error(`No database found for ref: ${options.ref} and type: ${options.type}`);
1867
+ }
1868
+ if (matches.length > 1) {
1869
+ throw new Error(`Multiple databases found for ref: ${options.ref} and type: ${options.type}`);
1870
+ }
1871
+ return matches[0].database;
1741
1872
  }
1873
+ // Priority 3: If ref is provided, find by ref
1874
+ if (options?.ref) {
1875
+ const matches = databases.filter(db => db.ref === options.ref);
1876
+ if (matches.length === 0) {
1877
+ throw new Error(`No database found for ref: ${options.ref}`);
1878
+ }
1879
+ if (matches.length > 1) {
1880
+ throw new Error(`Multiple databases found for ref: ${options.ref}. Use 'type' parameter to narrow down.`);
1881
+ }
1882
+ return matches[0].database;
1883
+ }
1884
+ // Priority 4: If type is provided, find by type
1885
+ if (options?.type) {
1886
+ const matches = databases.filter(db => db.type === options.type);
1887
+ if (matches.length === 0) {
1888
+ throw new Error(`No database found for type: ${options.type}`);
1889
+ }
1890
+ if (matches.length > 1) {
1891
+ throw new Error(`Multiple databases found for type: ${options.type}. Use 'ref' parameter to narrow down.`);
1892
+ }
1893
+ return matches[0].database;
1894
+ }
1895
+ // No options provided, return undefined (will default to 'admin')
1896
+ return undefined;
1897
+ }
1898
+ /**
1899
+ * Gets a database instance by name. Defaults to 'admin' if no name provided.
1900
+ * @param databaseName - Optional database name (defaults to 'admin')
1901
+ * @returns Database instance
1902
+ * @throws Error if client is not initialized
1903
+ * @internal
1904
+ */
1905
+ getDatabase(databaseName) {
1906
+ if (!this.client) {
1907
+ throw new Error('MongoDB client not initialized. Call initialize() first.');
1908
+ }
1909
+ return this.client.db(databaseName || 'admin');
1910
+ }
1911
+ /**
1912
+ * Gets a database instance by name. Defaults to 'admin' if no name provided.
1913
+ * Public method for use by ProgressAPIImpl.
1914
+ * @param databaseName - Optional database name (defaults to 'admin')
1915
+ * @param ref - Optional ref for database resolution
1916
+ * @param type - Optional type for database resolution
1917
+ * @returns Database instance
1918
+ * @throws Error if client is not initialized
1919
+ */
1920
+ getDatabaseByName(databaseName, ref, type) {
1921
+ return this.getDatabase(this.resolveDatabase({ database: databaseName, ref, type }));
1742
1922
  }
1743
1923
  }
1744
1924
  exports.SimpleMongoHelper = SimpleMongoHelper;