monastery 3.5.9 → 3.5.10

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.
package/changelog.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [3.5.10](https://github.com/boycce/monastery/compare/3.5.9...3.5.10) (2026-02-02)
6
+
5
7
  ### [3.5.9](https://github.com/boycce/monastery/compare/3.5.8...3.5.9) (2025-12-23)
6
8
 
7
9
  ### [3.5.8](https://github.com/boycce/monastery/compare/3.5.6...3.5.8) (2025-12-23)
package/lib/manager.js CHANGED
@@ -37,10 +37,10 @@ function Manager(uri, opts, parent) {
37
37
  if (typeof opts.logLevel == 'undefined') opts.logLevel = 2
38
38
 
39
39
  // opts: separate out monastery options
40
- const mongoOpts = Object.keys(opts||{}).reduce((acc, key) => {
40
+ const mongoOpts = Object.keys(opts || {}).reduce((acc, key) => {
41
41
  if (![
42
42
  'databaseName', 'defaultObjects', 'logLevel', 'imagePlugin', 'limit', 'noDefaults', 'nullObjects',
43
- 'promise', 'timestamps', 'useMilliseconds',
43
+ 'promise', 'timestamps', 'useMilliseconds', 'forJest',
44
44
  ].includes(key)) {
45
45
  acc[key] = opts[key]
46
46
  }
@@ -57,6 +57,7 @@ function Manager(uri, opts, parent) {
57
57
  that.collections = {}
58
58
  that._openQueue = []
59
59
  that.emitter = new EventEmitter()
60
+ that.forJest = opts.forJest || false
60
61
 
61
62
  // If there is no DEBUG= environment variable, we will need to force the debugs on (due to bug on debug@4.3.4)
62
63
  if (!debug.namespaces) {
@@ -283,9 +284,11 @@ Manager.prototype.open = async function() {
283
284
  * @return {Promise(manager)}
284
285
  */
285
286
  try {
287
+ this.openTimestamp = Date.now()
286
288
  this._state = 'opening'
287
289
  this.db = this.client.db()
288
290
  await this.client.connect() // now optional since db().command() will auto-connect
291
+ if (!this.forJest) this.info(`Connected to MongoDB in ${Date.now() - this.openTimestamp}ms`)
289
292
  this.emitter.emit(this._state = 'open', this)
290
293
  return this
291
294
 
package/lib/model.js CHANGED
@@ -346,7 +346,25 @@ Model.prototype._setupIndexes = async function(fields, opts={}) {
346
346
  const collection = this.collection
347
347
 
348
348
  // Get the collections indexes
349
- const existingIndexes = await collection.indexes() // returns [] if collection doesn't exist
349
+ let existingIndexes = []
350
+ try {
351
+ existingIndexes = await collection.indexes() // returns [] if collection doesn't exist
352
+ } catch (err) {
353
+ // Show a succinct error message for timeout errors, which is due to onOpen() timing out.
354
+ if (err.message.match(/Server selection timed out after/)) {
355
+ const msg = `Unable to create an index on the '${model.name||''}' model, the server selection has timed out already.`
356
+ if (model.manager.forJest) throw new Error(msg)
357
+ else this.error(msg)
358
+ return []
359
+ } else if (err.message.match(/Topology is closed/)) {
360
+ const msg = `Unable to create an index on the '${model.name||''}' model, the 'Topology is closed'. Bad connection URL?`
361
+ if (model.manager.forJest) throw new Error(msg)
362
+ else this.error(msg)
363
+ return []
364
+ } else {
365
+ throw err
366
+ }
367
+ }
350
368
 
351
369
  // Remove any existing text index that has different options as createIndexes will throws error about this
352
370
  if (existingIndexes.length) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "monastery",
3
3
  "description": "⛪ A simple, straightforward MongoDB ODM",
4
4
  "author": "Ricky Boyce",
5
- "version": "3.5.9",
5
+ "version": "3.5.10",
6
6
  "license": "MIT",
7
7
  "repository": "github:boycce/monastery",
8
8
  "homepage": "https://boycce.github.io/monastery/",
package/test/manager.js CHANGED
@@ -59,6 +59,17 @@ test('manager > onOpen error', async () => {
59
59
  db.close()
60
60
  })
61
61
 
62
+ test('manager > onOpen timeout error', async () => {
63
+ // Bad connection url, which will hang and timeout after 100ms
64
+ // Related: test/model.js > model index timeout topology closed
65
+ let manager
66
+ const db = monastery.manager('google.com', { serverSelectionTimeoutMS:100, logLevel: 3 }) // should hang
67
+ await expect(db.onOpen((res) => { manager = res })).rejects.toThrow('Server selection timed out after 100 ms')
68
+ expect(manager).toEqual(undefined)
69
+ expect(db).toEqual(expect.any(Object))
70
+ db.close()
71
+ })
72
+
62
73
  test('manager > return a promise', async () => {
63
74
  const db = await monastery.manager('localhost/monastery', { serverSelectionTimeoutMS: 500, promise: true })
64
75
  expect(db).toEqual(expect.any(Object))
@@ -110,9 +121,9 @@ test('Manager > get collection', async () => {
110
121
  })
111
122
 
112
123
  test('Manager > multiple managers', async () => {
113
- const db1 = monastery.manager('localhost/monastery', { logLevel: 5 })
114
- const db2 = monastery.manager('localhost/monastery', { logLevel: 6 })
115
- const db3 = monastery.manager('localhost/monastery', { logLevel: 7 })
124
+ const db1 = monastery.manager('localhost/monastery', { logLevel: 5, forJest: true })
125
+ const db2 = monastery.manager('localhost/monastery', { logLevel: 6, forJest: true })
126
+ const db3 = monastery.manager('localhost/monastery', { logLevel: 7, forJest: true })
116
127
 
117
128
  expect(monastery.opts.logLevel).not.toEqual(6) // default manager
118
129
  expect(db2.opts.logLevel).not.toEqual(db1.opts.logLevel)
package/test/model.js CHANGED
@@ -495,6 +495,27 @@ test('model indexes basic', async () => {
495
495
  }])
496
496
  })
497
497
 
498
+ test('model index timeout topology closed', async () => {
499
+ // 1. For a invalid URL, while a connection is still opening, we should expect a
500
+ // 'topology is closed' error when trying to create an index.
501
+ // 2. For valid connection URL, we should expect a 'server selection timed out' error (which we can't reproduce in tests)
502
+ const db2 = monastery.manager('google.com', { logLevel: 3, serverSelectionTimeoutMS: 50, forJest: true })
503
+ // Attempt to use the collection while the connection is still opening.
504
+ const userIndexErrModel = db2.model('userIndexErr', {
505
+ fields: {
506
+ age: { type: 'number' },
507
+ name: { type: 'string' },
508
+ },
509
+ })
510
+ expect(
511
+ userIndexErrModel._setupIndexes({ age: { type: 'number', index: 'text' }, name: { type: 'string', index: 'text' } })
512
+ ).rejects.toThrow('Unable to create an index on the \'userIndexErr\' model, the \'Topology is closed\'. Bad connection URL?')
513
+ await expect(
514
+ db2.onOpen((res) => { })
515
+ ).rejects.toThrow('Server selection timed out after 50 ms')
516
+ db2.close()
517
+ })
518
+
498
519
  test('model indexes unique', async () => {
499
520
  // Setup: Drop previously tested collections
500
521
  if ((await db.db.listCollections().toArray()).find(o => o.name == 'userUniqueIndex')) {