monastery 3.4.3 → 3.5.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.
package/changelog.md CHANGED
@@ -2,6 +2,10 @@
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.1](https://github.com/boycce/monastery/compare/3.5.0...3.5.1) (2024-12-23)
6
+
7
+ ## [3.5.0](https://github.com/boycce/monastery/compare/3.4.3...3.5.0) (2024-12-20)
8
+
5
9
  ### [3.4.3](https://github.com/boycce/monastery/compare/3.4.2...3.4.3) (2024-08-27)
6
10
 
7
11
  ### [3.4.2](https://github.com/boycce/monastery/compare/3.4.1...3.4.2) (2024-08-24)
@@ -304,7 +304,7 @@ You are able to define custom error messages for each field rule.
304
304
 
305
305
  ### Operation hooks
306
306
 
307
- You are able provide an array of callbacks to these model operation hooks. If you need to throw an error asynchronously, please pass an error as the first argument to `next()`, e.g. `next(new Error('Your error here'))`. You can also access the operation details via `this` in each callback.
307
+ You are able provide an array of promises to the following model operation hooks. `this` is the operation details.
308
308
 
309
309
  ```js
310
310
  {
@@ -6,7 +6,7 @@ has_children: true
6
6
 
7
7
  # Manager
8
8
 
9
- Monastery manager constructor.
9
+ Creates a new manager instance and makes the first instance available globally via the `db`.
10
10
 
11
11
  ### Arguments
12
12
 
@@ -24,26 +24,27 @@ Monastery manager constructor.
24
24
 
25
25
  ### Returns
26
26
 
27
- A manager instance.
27
+ A new manager instance.
28
28
 
29
29
  ### Example
30
30
 
31
31
  ```js
32
- import monastery from 'monastery'
32
+ import db from 'monastery'
33
33
 
34
- const db = monastery('localhost/mydb', options)
34
+ // this manager instance is now available via the `db` object on subsequent imports
35
+ const manager = db.manager('localhost/mydb', options)
35
36
  // replica set
36
- const db = monastery('localhost/mydb,192.168.1.1')
37
+ const manager = db.manager('localhost/mydb,192.168.1.1')
37
38
  // you can wait for the connection (which is not required before calling methods)
38
- const db = await monastery('localhost/mydb,192.168.1.1', { promise: true })
39
+ const manager = await db.manager('localhost/mydb,192.168.1.1', { promise: true })
39
40
  ```
40
41
 
41
42
  You can also listen for errors or successful connection using these hooks
42
43
  ```js
43
- db.onOpen((manager) => {
44
+ manager.onOpen((manager) => {
44
45
  // manager.client is connected...
45
46
  })
46
- db.onError((err) => {
47
+ manager.onError((err) => {
47
48
  // connection error
48
49
  })
49
50
  ```
package/docs/readme.md CHANGED
@@ -35,10 +35,10 @@ $ npm install --save monastery
35
35
  ## Usage
36
36
 
37
37
  ```javascript
38
- import monastery from 'monastery'
38
+ import db from 'monastery'
39
39
 
40
40
  // Initialize a monastery manager
41
- const db = monastery('localhost/mydb')
41
+ db.manager('localhost/mydb')
42
42
 
43
43
  // Define a model
44
44
  db.model('user', {
@@ -96,6 +96,7 @@ You can view MongoDB's [compatibility table here](https://www.mongodb.com/docs/d
96
96
  - db.catch/then() moved to db.onError/db.onOpen()
97
97
  - next() is now redundant when returning promises from hooks, e.g. `afterFind: [async (data) => {...}]`
98
98
  - deep paths in data, e.g. `books[].title` are now validated, and don't replace the whole object, e.g. `books`
99
+ - `db()` moved to `db.manager()`
99
100
 
100
101
  ## v2 Breaking Changes
101
102
 
package/lib/index.js CHANGED
@@ -1,301 +1,14 @@
1
- /*eslint-disable max-len*/
2
- const fs = require('fs')
3
- const debug = require('debug')
4
- const path = require('path')
5
- const EventEmitter = require('events').EventEmitter
6
- const inherits = require('util').inherits
7
- const { MongoClient } = require('mongodb')
8
- const Collection = require('./collection.js')
9
- const imagePluginFile = require('../plugins/images/index.js')
10
- const Model = require('./model.js')
1
+ const Manager = require('./manager')
11
2
  const rules = require('./rules.js')
12
- const util = require('./util.js')
13
3
 
14
- function Manager(uri, opts) {
15
- /**
16
- * Constructs a new manager which contains a new MongoDB client
17
- *
18
- * @param {String|Array|Object} uri - MongoDB connection URI string / replica set, or reuse a MongoClient instance
19
- * @param {Object} opts -
20
- * {String} <opts.databaseName> - the name of the database
21
- * {Boolean} <opts.promise(manager)> - return a promise
22
- *
23
- * @return {Connection|Promise}
24
- * @link https://www.mongodb.com/docs/drivers/node/v5.9/quick-reference/
25
- * @link https://mongodb.github.io/node-mongodb-native/ (all versions)
26
- */
27
- if (!opts) opts = {}
28
- if (!(this instanceof Manager)) return new Manager(uri, opts)
29
-
30
- // opts: timestamps
31
- if (typeof opts.defaultFields != 'undefined') throw Error('opts.defaultFields has been depreciated, use opts.timestamps')
32
- if (typeof opts.timestamps == 'undefined') opts.timestamps = true
33
-
34
- // opts: logLevel
35
- if (typeof opts.logLevel == 'undefined') opts.logLevel = 2
36
-
37
- // opts: separate out monastery options
38
- const mongoOpts = Object.keys(opts||{}).reduce((acc, key) => {
39
- if (
40
- ![
41
- 'databaseName', 'defaultObjects', 'logLevel', 'imagePlugin', 'limit', 'noDefaults', 'nullObjects',
42
- 'promise', 'timestamps', 'useMilliseconds',
43
- ].includes(key)) {
44
- acc[key] = opts[key]
45
- }
46
- return acc
47
- }, {})
48
-
49
- // Add properties
50
- this.uri = uri
51
- this.error = debug('monastery:error' + (opts.logLevel > 0 ? '*' : ''))
52
- this.warn = debug('monastery:warn' + (opts.logLevel > 1 ? '*' : ''))
53
- this.info = debug('monastery:info' + (opts.logLevel > 2 ? '*' : ''))
54
- this.opts = opts
55
- this.beforeModel = []
56
- this.collections = {}
57
- this._openQueue = []
58
-
59
- // If there is no DEBUG= environment variable, we will need to force the debugs on (due to bug on debug@4.3.4)
60
- if (!debug.namespaces) {
61
- if (opts.logLevel > 0) this.error.enabled = true
62
- if (opts.logLevel > 1) this.warn.enabled = true
63
- if (opts.logLevel > 2) this.info.enabled = true
64
- }
65
-
66
- // Create a new MongoDB Client
67
- if (typeof this.uri == 'string' || Array.isArray(this.uri)) {
68
- this.uri = this.connectionString(this.uri, opts.databaseName)
69
- this.client = new MongoClient(this.uri, mongoOpts)
70
- } else {
71
- this.client = this.uri
72
- }
73
-
74
- // Listen to MongoDB events
75
- for (let eventName of ['close', 'error', 'timeout']) {
76
- this.client.on(eventName, (e) => this.emit(eventName, e))
77
- }
78
-
79
- // Listen to custom open event
80
- this.on('open', () => {
81
- // wait for all the on('open') events to get called first
82
- setTimeout(() => {
83
- for (let item of this._openQueue) {
84
- item()
85
- }
86
- })
87
- })
88
-
89
- // Initiate any plugins
90
- if (opts.imagePlugin) {
91
- imagePluginFile.setup(this, util.isObject(opts.imagePlugin) ? opts.imagePlugin : {})
92
- }
93
-
94
- // Expose the last manager
95
- module.exports.manager = this
96
-
97
- // Connect to the database
98
- if (opts.promise) return this.open()
99
- else this.open()
100
- }
101
-
102
- Manager.prototype.arrayWithSchema = function(array, schema) {
103
- array.schema = schema
104
- return array
105
- }
106
-
107
- Manager.prototype.close = async function() {
108
- /**
109
- * Close the database connection, gracefully
110
- */
111
- const _close = async () => {
112
- this.emit(this._state = 'closing')
113
- await this.client.close()
114
- this.emit(this._state = 'close')
115
- }
116
- if (this._state == 'open') {
117
- return await _close()
118
-
119
- } else if (this._state == 'opening') {
120
- return new Promise((resolve) => {
121
- this._openQueue.push(() => _close().then(resolve))
122
- })
123
-
124
- } else if (this._state == 'close' || this._state == 'closing') {
125
- return
126
- }
127
- }
128
-
129
- Manager.prototype.command = function(...args) {
130
- /**
131
- * Run a raw MongoDB command
132
- */
133
- return this.db.command(...args)
134
- }
135
-
136
- Manager.prototype.connectionString = function(uri, databaseName) {
137
- /**
138
- * get the connection string
139
- * @param {string|array} uri
140
- * @param {string} <databaseName>
141
- * @return {string}
142
- */
143
- if (!uri) {
144
- throw Error('No connection URI provided.')
145
- }
146
- // uri: array, sort out the connection string
147
- if (util.isArray(uri)) {
148
- if (!databaseName) {
149
- for (var i=0, l=uri.length; i<l; i++) {
150
- if (!databaseName) databaseName = uri[i].replace(/([^\/])+\/?/, '') // eslint-disable-line
151
- uri[i] = uri[i].replace(/\/.*/, '')
152
- }
153
- }
154
- uri = uri.join(',') + '/' + databaseName
155
- }
156
- // uri: string, sort out the connection string
157
- if (typeof uri === 'string') {
158
- if (!/^mongodb(\+srv)?:\/\//.test(uri)) {
159
- uri = 'mongodb://' + uri
160
- }
161
- }
162
- return uri
163
- }
164
-
165
- Manager.prototype.get = function(name, options) {
166
- /**
167
- * Returns a MongoDB collection
168
- * @param {String} name
169
- * @param {Object} [options] - options to pass to the collection
170
- * @return {MongoDB Collection}
171
- */
172
- if (!this.collections[name] || (options||{}).cache === false) {
173
- delete (options||{}).cache
174
- this.collections[name] = new Collection(this, name, options)
175
- }
176
- return this.collections[name]
177
- }
178
-
179
- Manager.prototype.id = function(str) {
180
- return util.id(str)
181
- }
182
-
183
- Manager.prototype.isId = function(value) {
184
- return util.isId(value)
185
- }
186
-
187
- Manager.prototype.models = async function(pathname, opts) {
188
- /**
189
- * Setup model definitions from a folder location
190
- * @param {string} pathname
191
- * @param {object} opts:
192
- * @param {boolean} opts.waitForIndexes - Wait for indexes to be created?
193
- * @return {Promise(object)} - e.g. { user: , article: , .. }
194
- * @this Manager
195
- */
196
- let out = {}
197
- let { waitForIndexes } = opts || {}
198
- if (!pathname || typeof pathname !== 'string') {
199
- throw 'The path must be a valid pathname'
200
- }
201
- let filenames = fs.readdirSync(pathname)
202
- for (let filename of filenames) {
203
- // Ignore folders
204
- if (!filename.match(/\.[cm]?js$/)) continue
205
- let name = filename.replace('.js', '')
206
- let filepath = path.join(pathname, filename)
207
- // We can't use require() here since we need to be able to import both CJS and ES6 modules
208
- let definition = ((await import(filepath))||{}).default
209
- // When a commonJS project uses babel (e.g. `nodemon -r @babel/register`, similar to `-r esm`), import()
210
- // will return ES6 model definitions in another nested `default` object
211
- if (definition && definition.default) definition = definition.default
212
- if (!definition) throw new Error(`The model definition for '${name}' must export a default object`)
213
- // Wait for indexes to be created?
214
- if (waitForIndexes) out[name] = await this.model(name, definition, waitForIndexes)
215
- else out[name] = this.model(name, definition)
216
- }
217
- return out
218
- }
219
-
220
- Manager.prototype.onError = function(fn) {
221
- /**
222
- * Called when an error occurs when trying to connect to the MongoClient.
223
- * @param {Function} fn
224
- * @return {Promise}
225
- */
226
- return new Promise((resolve, reject) => {
227
- this.on('error', (err) => {
228
- resolve(err)
229
- })
230
- }).then((err) => {
231
- return fn(err)
232
- })
233
- }
234
-
235
- Manager.prototype.onOpen = function(fn) {
236
- /**
237
- * Called when a successful MongoClient connection has been made.
238
- * @param {Function} fn
239
- * @return {Promise(manager)}
240
- */
241
- return new Promise((resolve, reject) => {
242
- if (this._state == 'open') {
243
- resolve(this)
244
- }
245
- this.on('open', () => {
246
- resolve(this)
247
- })
248
- this.on('error', (err) => {
249
- reject(err)
250
- })
251
- }).then((err) => { // If `then` is not chained, the error will be thrown, detached!
252
- return fn(err)
253
- })
254
- }
255
-
256
- Manager.prototype.open = async function() {
257
- /**
258
- * Connect to the database
259
- * @return {Promise(manager)}
260
- */
261
- try {
262
- this._state = 'opening'
263
- this.db = this.client.db()
264
- await this.client.connect() // now optional since db().command() will auto-connect
265
- this.emit(this._state = 'open', this)
266
- return this
267
-
268
- } catch (err) {
269
- this._state = 'closed'
270
- // Only emit the error if there are listeners, since it will throw an unhandled error
271
- if (this.listeners('error').length > 0) {
272
- this.emit('error', err)
273
- }
274
- if (this.opts.promise || this.listeners('error').length == 0) {
275
- throw new Error(err)
276
- }
277
- }
278
- }
279
-
280
- Manager.prototype.parseData = function(obj, parseBracketToDotNotation, parseDotNotation) {
281
- return util.parseData(obj, parseBracketToDotNotation, parseDotNotation)
282
- }
283
-
284
- Manager.prototype.model = Model
285
- Manager.prototype.getSignedUrl = imagePluginFile.getSignedUrl
286
- Manager.prototype._getSignedUrl = () => {
287
- throw new Error('monastery._getSignedUrl() has been moved to monastery.getSignedUrl()')
288
- }
289
-
290
- inherits(Manager, EventEmitter)
291
- module.exports = Manager
4
+ // Exports
5
+ module.exports = new Manager('init')
6
+ module.exports.arrayWithSchema = Manager.prototype.arrayWithSchema
7
+ module.exports.getSignedUrl = Manager.prototype.getSignedUrl
292
8
  module.exports.id = Manager.prototype.id
293
9
  module.exports.isId = Manager.prototype.isId
294
- module.exports.arrayWithSchema = Manager.prototype.arrayWithSchema
295
- module.exports.parseData = Manager.prototype.parseData
296
10
  module.exports.parseBracketNotation = Manager.prototype.parseBracketNotation
297
11
  module.exports.parseBracketToDotNotation = Manager.prototype.parseBracketToDotNotation
12
+ module.exports.parseData = Manager.prototype.parseData
298
13
  module.exports.parseDotNotation = Manager.prototype.parseDotNotation
299
- module.exports.getSignedUrl = Manager.prototype.getSignedUrl
300
- module.exports.manager = null
301
14
  module.exports.rules = rules
package/lib/manager.js ADDED
@@ -0,0 +1,303 @@
1
+ const fs = require('fs')
2
+ const debug = require('debug')
3
+ const path = require('path')
4
+ const EventEmitter = require('events').EventEmitter
5
+ const { MongoClient } = require('mongodb')
6
+ const Collection = require('./collection.js')
7
+ const imagePluginFile = require('../plugins/images/index.js')
8
+ const Model = require('./model.js')
9
+ const util = require('./util.js')
10
+
11
+ let hasDefaultManager = false
12
+
13
+ function Manager(uri, opts, parent) {
14
+ /**
15
+ * Constructs a new manager which contains a new MongoDB client
16
+ *
17
+ * @param {String|Array|Object} uri - MongoDB connection URI string / replica set, or reuse a MongoClient instance
18
+ * @param {Object} opts -
19
+ * {String} <opts.databaseName> - the name of the database
20
+ * {Boolean} <opts.promise(manager)> - return a promise
21
+ *
22
+ * @return {Connection|Promise}
23
+ * @link https://www.mongodb.com/docs/drivers/node/v5.9/quick-reference/
24
+ * @link https://mongodb.github.io/node-mongodb-native/ (all versions)
25
+ */
26
+ const that = (!hasDefaultManager && parent) ? parent : this
27
+
28
+ if (!opts) opts = {}
29
+ if (!(this instanceof Manager)) return new Manager(uri, opts)
30
+ if (uri == 'init') return
31
+
32
+ // opts: timestamps
33
+ if (typeof opts.defaultFields != 'undefined') throw Error('opts.defaultFields has been depreciated, use opts.timestamps')
34
+ if (typeof opts.timestamps == 'undefined') opts.timestamps = true
35
+
36
+ // opts: logLevel
37
+ if (typeof opts.logLevel == 'undefined') opts.logLevel = 2
38
+
39
+ // opts: separate out monastery options
40
+ const mongoOpts = Object.keys(opts||{}).reduce((acc, key) => {
41
+ if (![
42
+ 'databaseName', 'defaultObjects', 'logLevel', 'imagePlugin', 'limit', 'noDefaults', 'nullObjects',
43
+ 'promise', 'timestamps', 'useMilliseconds',
44
+ ].includes(key)) {
45
+ acc[key] = opts[key]
46
+ }
47
+ return acc
48
+ }, {})
49
+
50
+ // Add properties
51
+ that.uri = uri
52
+ that.error = debug('monastery:error' + (opts.logLevel >= 1 ? '*' : ''))
53
+ that.warn = debug('monastery:warn' + (opts.logLevel >= 2 ? '*' : ''))
54
+ that.info = debug('monastery:info' + (opts.logLevel >= 3 ? '*' : ''))
55
+ that.opts = opts
56
+ that.beforeModel = []
57
+ that.collections = {}
58
+ that._openQueue = []
59
+ that.emitter = new EventEmitter()
60
+
61
+ // If there is no DEBUG= environment variable, we will need to force the debugs on (due to bug on debug@4.3.4)
62
+ if (!debug.namespaces) {
63
+ if (opts.logLevel >= 1) that.error.enabled = true
64
+ if (opts.logLevel >= 2) that.warn.enabled = true
65
+ if (opts.logLevel >= 3) that.info.enabled = true
66
+ }
67
+
68
+ // Create a new MongoDB Client
69
+ if (typeof that.uri == 'string' || Array.isArray(that.uri)) {
70
+ that.uri = that.connectionString(that.uri, opts.databaseName)
71
+ that.client = new MongoClient(that.uri, mongoOpts)
72
+ } else {
73
+ that.client = that.uri
74
+ }
75
+
76
+ // Listen to MongoDB events
77
+ for (let eventName of ['close', 'error', 'timeout']) {
78
+ that.client.on(eventName, (e) => that.emitter.emit(eventName, e))
79
+ }
80
+
81
+ // Listen to custom open event
82
+ that.emitter.on('open', () => {
83
+ // wait for all the on('open') events to get called first
84
+ setTimeout(() => {
85
+ for (let item of that._openQueue) {
86
+ item()
87
+ }
88
+ })
89
+ })
90
+
91
+ // Initiate any plugins
92
+ if (opts.imagePlugin) {
93
+ imagePluginFile.setup(that, util.isObject(opts.imagePlugin) ? opts.imagePlugin : {})
94
+ }
95
+
96
+ // Update the parent manager with the new manager
97
+ if (!hasDefaultManager && parent) hasDefaultManager = true
98
+ if (opts.promise) return that.open()
99
+ else that.open()
100
+ }
101
+
102
+ Manager.prototype.manager = function(uri, opts) {
103
+ return new Manager(uri, opts, this)
104
+ }
105
+
106
+ Manager.prototype.arrayWithSchema = function(array, schema) {
107
+ array.schema = schema
108
+ return array
109
+ }
110
+
111
+ Manager.prototype.close = async function() {
112
+ /**
113
+ * Close the database connection, gracefully
114
+ */
115
+ const _close = async () => {
116
+ this.emitter.emit(this._state = 'closing')
117
+ await this.client.close()
118
+ this.emitter.emit(this._state = 'close')
119
+ }
120
+ if (this._state == 'open') {
121
+ return await _close()
122
+
123
+ } else if (this._state == 'opening') {
124
+ return new Promise((resolve) => {
125
+ this._openQueue.push(() => _close().then(resolve))
126
+ })
127
+
128
+ } else if (this._state == 'close' || this._state == 'closing') {
129
+ return
130
+ }
131
+ }
132
+
133
+ Manager.prototype.command = function(...args) {
134
+ /**
135
+ * Run a raw MongoDB command
136
+ */
137
+ return this.db.command(...args)
138
+ }
139
+
140
+ Manager.prototype.connectionString = function(uri, databaseName) {
141
+ /**
142
+ * get the connection string
143
+ * @param {string|array} uri
144
+ * @param {string} <databaseName>
145
+ * @return {string}
146
+ */
147
+ if (!uri) {
148
+ throw Error('No connection URI provided.')
149
+ }
150
+ // uri: array, sort out the connection string
151
+ if (util.isArray(uri)) {
152
+ if (!databaseName) {
153
+ for (var i=0, l=uri.length; i<l; i++) {
154
+ if (!databaseName) databaseName = uri[i].replace(/([^\/])+\/?/, '') // eslint-disable-line
155
+ uri[i] = uri[i].replace(/\/.*/, '')
156
+ }
157
+ }
158
+ uri = uri.join(',') + '/' + databaseName
159
+ }
160
+ // uri: string, sort out the connection string
161
+ if (typeof uri === 'string') {
162
+ if (!/^mongodb(\+srv)?:\/\//.test(uri)) {
163
+ uri = 'mongodb://' + uri
164
+ }
165
+ }
166
+ return uri
167
+ }
168
+
169
+ Manager.prototype.get = function(name, options) {
170
+ /**
171
+ * Returns a MongoDB collection
172
+ * @param {String} name
173
+ * @param {Object} [options] - options to pass to the collection
174
+ * @return {MongoDB Collection}
175
+ */
176
+ if (!this.collections[name] || (options||{}).cache === false) {
177
+ delete (options||{}).cache
178
+ this.collections[name] = new Collection(this, name, options)
179
+ }
180
+ return this.collections[name]
181
+ }
182
+
183
+ Manager.prototype.id = function(str) {
184
+ return util.id(str)
185
+ }
186
+
187
+ Manager.prototype.isId = function(value) {
188
+ return util.isId(value)
189
+ }
190
+
191
+ Manager.prototype.models = async function(pathname, opts) {
192
+ /**
193
+ * Setup model definitions from a folder location
194
+ * @param {string} pathname
195
+ * @param {object} opts:
196
+ * @param {boolean} opts.waitForIndexes - Wait for indexes to be created?
197
+ * @param {boolean} opts.skipIfExists - Skip if the model already exists?
198
+ * @return {Promise(object)} - e.g. { user: , article: , .. }
199
+ * @this Manager
200
+ */
201
+ let out = {}
202
+ let { waitForIndexes } = opts || {}
203
+ if (!pathname || typeof pathname !== 'string') {
204
+ throw 'The path must be a valid pathname'
205
+ }
206
+ if (!fs.existsSync(pathname)) {
207
+ this.warn(`The models path ${pathname} does not exist`)
208
+ return out
209
+ }
210
+ let filenames = fs.readdirSync(pathname)
211
+ for (let filename of filenames) {
212
+ // Ignore folders
213
+ if (!filename.match(/\.[cm]?js$/)) continue
214
+ let name = filename.replace('.js', '')
215
+ let filepath = path.join(pathname, filename)
216
+ // Skip
217
+ if (opts.skipIfExists && this.models[name]) continue
218
+ // We can't use require() here since we need to be able to import both CJS and ES6 modules
219
+ let definition = ((await import(filepath))||{}).default
220
+ // When a commonJS project uses babel (e.g. `nodemon -r @babel/register`, similar to `-r esm`), import()
221
+ // will return ES6 model definitions in another nested `default` object
222
+ if (definition && definition.default) definition = definition.default
223
+ if (!definition) throw new Error(`The model definition for '${name}' must export a default object`)
224
+ // Wait for indexes to be created?
225
+ if (waitForIndexes) out[name] = await this.model(name, definition, waitForIndexes)
226
+ else out[name] = this.model(name, definition)
227
+ }
228
+ return out
229
+ }
230
+
231
+ Manager.prototype.onError = function(fn) {
232
+ /**
233
+ * Called when an error occurs when trying to connect to the MongoClient.
234
+ * @param {Function} fn
235
+ * @return {Promise}
236
+ */
237
+ return new Promise((resolve, reject) => {
238
+ this.emitter.on('error', (err) => {
239
+ resolve(err)
240
+ })
241
+ }).then((err) => {
242
+ return fn(err)
243
+ })
244
+ }
245
+
246
+ Manager.prototype.onOpen = function(fn) {
247
+ /**
248
+ * Called when a successful MongoClient connection has been made.
249
+ * @param {Function} fn
250
+ * @return {Promise(manager)}
251
+ */
252
+ return new Promise((resolve, reject) => {
253
+ if (this._state == 'open') {
254
+ resolve(this)
255
+ }
256
+ this.emitter.on('open', () => {
257
+ resolve(this)
258
+ })
259
+ this.emitter.on('error', (err) => {
260
+ reject(err)
261
+ })
262
+ }).then((err) => { // If `then` is not chained, the error will be thrown, detached!
263
+ return fn(err)
264
+ })
265
+ }
266
+
267
+ Manager.prototype.open = async function() {
268
+ /**
269
+ * Connect to the database
270
+ * @return {Promise(manager)}
271
+ */
272
+ try {
273
+ this._state = 'opening'
274
+ this.db = this.client.db()
275
+ await this.client.connect() // now optional since db().command() will auto-connect
276
+ this.emitter.emit(this._state = 'open', this)
277
+ return this
278
+
279
+ } catch (err) {
280
+ this._state = 'closed'
281
+ // Only emit the error if there are listeners, since it will throw an unhandled error
282
+ if (this.emitter.listeners('error').length > 0) {
283
+ this.emitter.emit('error', err)
284
+ }
285
+ if (this.opts.promise || this.emitter.listeners('error').length == 0) {
286
+ throw new Error(err)
287
+ }
288
+ }
289
+ }
290
+
291
+ Manager.prototype.parseData = function(obj, parseBracketToDotNotation, parseDotNotation) {
292
+ return util.parseData(obj, parseBracketToDotNotation, parseDotNotation)
293
+ }
294
+
295
+ Manager.prototype.model = Model
296
+
297
+ Manager.prototype.getSignedUrl = imagePluginFile.getSignedUrl
298
+
299
+ Manager.prototype._getSignedUrl = () => {
300
+ throw new Error('monastery._getSignedUrl() has been moved to monastery.getSignedUrl()')
301
+ }
302
+
303
+ module.exports = Manager
package/lib/model.js CHANGED
@@ -9,7 +9,7 @@ function Model(name, definition, waitForIndexes, manager) {
9
9
  * @param {boolean} waitForIndexes - wait for indexes to be created before returning (returns a promise)
10
10
  *
11
11
  * @return model or Promise(model)
12
- * @this manager
12
+ * @this Manager | Model
13
13
  */
14
14
  if (!(this instanceof Model)) {
15
15
  return new Model(name, definition, waitForIndexes, this)
@@ -27,7 +27,11 @@ function Model(name, definition, waitForIndexes, manager) {
27
27
  ', e.g. { schema: { strict: false }}'
28
28
  } else if (!util.isSubdocument(definition.fields) && !util.isEmpty(definition.fields)) {
29
29
  throw `The ${name}.fields object should be a valid document, e.g. { name: { type: 'string' }}`
30
- }
30
+ }
31
+ // else if (manager.models[name]) {
32
+ // manager.warn(`The model '${name}' already exists, skipping model setup.`)
33
+ // return manager.models[name]
34
+ // }
31
35
 
32
36
  // Add schema options
33
37
  Object.assign(this, {
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.4.3",
5
+ "version": "3.5.1",
6
6
  "license": "MIT",
7
7
  "repository": "github:boycce/monastery",
8
8
  "homepage": "https://boycce.github.io/monastery/",
@@ -4,7 +4,7 @@ const bird = require('./mock/blacklisting.js').bird
4
4
  const user = require('./mock/blacklisting.js').user
5
5
 
6
6
  let db
7
- beforeAll(async () => { db = monastery('127.0.0.1/monastery', { timestamps: false }) })
7
+ beforeAll(async () => { db = monastery.manager('127.0.0.1/monastery', { timestamps: false }) })
8
8
  afterAll(async () => { db.close() })
9
9
 
10
10
  test('find blacklisting basic', async () => {
@@ -5,7 +5,7 @@ const util = require('../lib/util.js')
5
5
  // Setup/destroy
6
6
  let db, userCol, indexCol, indexDropCol
7
7
  beforeAll(async () => {
8
- db = monastery('127.0.0.1/monastery')
8
+ db = monastery.manager('127.0.0.1/monastery')
9
9
  userCol = db.get('users-' + Date.now())
10
10
  indexCol = db.get('index-' + Date.now())
11
11
  indexDropCol = db.get('index-' + Date.now())
@@ -2,7 +2,7 @@ const monastery = require('../lib/index.js')
2
2
  const mongoose = require('mongoose') // very slow initialisation
3
3
 
4
4
  test('comparison insert', async () => {
5
- const db = monastery('127.0.0.1/monastery', { timestamps: false })
5
+ const db = monastery.manager('127.0.0.1/monastery', { timestamps: false })
6
6
  const Test1 = db.model('Test1', { fields: {
7
7
  raw: {
8
8
  words: [{
@@ -70,7 +70,7 @@ test('comparison validate', async () => {
70
70
  })),
71
71
  }
72
72
  }
73
- const db = monastery('127.0.0.1/monastery', { timestamps: false })
73
+ const db = monastery.manager('127.0.0.1/monastery', { timestamps: false })
74
74
  const Test1 = db.model('Test1', { fields: {
75
75
  raw: {
76
76
  words: [{
package/test/crud.js CHANGED
@@ -2,11 +2,11 @@
2
2
  const monastery = require('../lib/index.js')
3
3
 
4
4
  let db
5
- beforeAll(async () => { db = monastery('127.0.0.1/monastery', { timestamps: false }) })
5
+ beforeAll(async () => { db = monastery.manager('127.0.0.1/monastery', { timestamps: false }) })
6
6
  afterAll(async () => { db.close() })
7
7
 
8
8
  test('insert basics', async () => {
9
- let db2 = monastery('127.0.0.1/monastery', { timestamps: false })
9
+ let db2 = monastery.manager('127.0.0.1/monastery', { timestamps: false })
10
10
  let user = db2.model('user', { fields: {
11
11
  name: { type: 'string' },
12
12
  names: [{ type: 'string' }],
@@ -52,7 +52,7 @@ test('insert basics', async () => {
52
52
  })
53
53
 
54
54
  test('insert option defaultObjects', async () => {
55
- let db2 = monastery('127.0.0.1/monastery', {
55
+ let db2 = monastery.manager('127.0.0.1/monastery', {
56
56
  defaultObjects: true,
57
57
  timestamps: false,
58
58
  imagePlugin: {
@@ -97,7 +97,7 @@ test('insert option defaultObjects', async () => {
97
97
  })
98
98
 
99
99
  test('insert option timestamps', async () => {
100
- let db2 = monastery('127.0.0.1/monastery', { timestamps: true })
100
+ let db2 = monastery.manager('127.0.0.1/monastery', { timestamps: true })
101
101
  let schema = {
102
102
  fields: {
103
103
  name: { type: 'string' },
@@ -355,7 +355,7 @@ test('find default field population with option noDefaults', async () => {
355
355
  async function setup(noDefaults) {
356
356
  // @returns {Object} {dog, user, dog1Doc, dog2Doc, user1Doc}
357
357
  // similar to "find default field population"
358
- const db = monastery('127.0.0.1/monastery', { noDefaults: noDefaults, timestamps: false })
358
+ const db = monastery.manager('127.0.0.1/monastery', { noDefaults: noDefaults, timestamps: false })
359
359
  const userDefinition = {
360
360
  fields: {
361
361
  name: { type: 'string', default: 'Martin Luther' },
@@ -577,7 +577,7 @@ test('update general', async () => {
577
577
  })
578
578
 
579
579
  test('update defaults', async () => {
580
- const db2 = monastery('127.0.0.1/monastery', { useMilliseconds: true })
580
+ const db2 = monastery.manager('127.0.0.1/monastery', { useMilliseconds: true })
581
581
  let user = db2.model('user', {
582
582
  fields: {
583
583
  name: { type: 'string' },
@@ -1276,7 +1276,7 @@ test('hooks > async', async () => {
1276
1276
  })
1277
1277
 
1278
1278
  test('hooks > async and next conflict', async () => {
1279
- const db2 = monastery('127.0.0.1/monastery', { timestamps: false })
1279
+ const db2 = monastery.manager('127.0.0.1/monastery', { timestamps: false })
1280
1280
  let user1 = db2.model('user', {
1281
1281
  fields: { age: { type: 'number'} },
1282
1282
  afterFind: [
@@ -1441,7 +1441,7 @@ test('hooks > option skipHooks', async () => {
1441
1441
  })
1442
1442
 
1443
1443
  test('update set and unset with option skipValidation', async () => {
1444
- const db2 = monastery('127.0.0.1/monastery', { timestamps: false })
1444
+ const db2 = monastery.manager('127.0.0.1/monastery', { timestamps: false })
1445
1445
  let user = db2.model('user_set_and_unset', {
1446
1446
  fields: {
1447
1447
  profile: {
package/test/manager.js CHANGED
@@ -2,23 +2,23 @@ const { MongoClient } = require('mongodb')
2
2
  const monastery = require('../lib/index.js')
3
3
 
4
4
  test('manager > basics', async () => {
5
- const manager = monastery('localhost/monastery', { serverSelectionTimeoutMS: 2000 })
5
+ const db = monastery.manager('localhost/monastery', { serverSelectionTimeoutMS: 2000 })
6
6
  // Manager is exposed
7
- expect(manager).toEqual(monastery.manager)
7
+ expect(monastery.uri).toEqual(expect.any(String))
8
8
  // Basic find command
9
- expect(await manager.db.collection('non-collection').findOne({})).toEqual(null)
9
+ expect(await db.db.collection('non-collection').findOne({})).toEqual(null)
10
10
  // Raw MongoDB ping command
11
- expect(await manager.command({ ping: 1 })).toMatchObject({ ok: 1 }) // cluster connections return extra fields
12
- manager.close()
11
+ expect(await db.command({ ping: 1 })).toMatchObject({ ok: 1 }) // cluster connections return extra fields
12
+ db.close()
13
13
  })
14
14
 
15
15
  test('manager > uri error', async () => {
16
- expect(() => monastery('', {})).toThrow('No connection URI provided.')
16
+ expect(() => monastery.manager('', {})).toThrow('No connection URI provided.')
17
17
  })
18
18
 
19
19
  test('manager > onError', async () => {
20
20
  // Bad port (thrown by MongoDB)
21
- const db = monastery('localhost:1234/monastery', { serverSelectionTimeoutMS: 500 })
21
+ const db = monastery.manager('localhost:1234/monastery', { serverSelectionTimeoutMS: 500 })
22
22
  let error, isAPromise
23
23
  await db.onError((res) => { error = res.message }).then(() => { isAPromise = true })
24
24
  expect(error).toEqual('connect ECONNREFUSED 127.0.0.1:1234')
@@ -27,7 +27,7 @@ test('manager > onError', async () => {
27
27
  })
28
28
 
29
29
  test('manager > onOpen', async () => {
30
- const db = monastery('localhost/monastery', { serverSelectionTimeoutMS: 500 })
30
+ const db = monastery.manager('localhost/monastery', { serverSelectionTimeoutMS: 500 })
31
31
 
32
32
  let manager1
33
33
  await db.onOpen((res) => { manager1 = res })
@@ -52,7 +52,7 @@ test('manager > onOpen', async () => {
52
52
  test('manager > onOpen error', async () => {
53
53
  // Bad port (thrown by MongoDB)
54
54
  let manager
55
- const db = monastery('localhost:1234/monastery', { serverSelectionTimeoutMS: 500 })
55
+ const db = monastery.manager('localhost:1234/monastery', { serverSelectionTimeoutMS: 500 })
56
56
  await expect(db.onOpen((res) => { manager = res })).rejects.toThrow('connect ECONNREFUSED 127.0.0.1:1234')
57
57
  expect(manager).toEqual(undefined)
58
58
  expect(db).toEqual(expect.any(Object))
@@ -60,19 +60,19 @@ test('manager > onOpen error', async () => {
60
60
  })
61
61
 
62
62
  test('manager > return a promise', async () => {
63
- const db = await monastery('localhost/monastery', { serverSelectionTimeoutMS: 500, promise: true })
63
+ const db = await monastery.manager('localhost/monastery', { serverSelectionTimeoutMS: 500, promise: true })
64
64
  expect(db).toEqual(expect.any(Object))
65
65
  db.close()
66
66
  })
67
67
 
68
68
  test('manager > return a promise with uri error', async () => {
69
- await expect(monastery('badlocalhost/monastery', { serverSelectionTimeoutMS: 500, promise: true }))
69
+ await expect(monastery.manager('badlocalhost/monastery', { serverSelectionTimeoutMS: 500, promise: true }))
70
70
  .rejects.toThrow('getaddrinfo EAI_AGAIN badlocalhost')
71
71
  })
72
72
 
73
73
  test('manager > reuse MongoDB Client', async () => {
74
74
  const mongoClient = new MongoClient('mongodb://localhost/monastery', {})
75
- const db = await monastery(mongoClient, { serverSelectionTimeoutMS: 500, promise: true })
75
+ const db = await monastery.manager(mongoClient, { serverSelectionTimeoutMS: 500, promise: true })
76
76
  expect(db).toEqual(expect.any(Object))
77
77
  expect(db.client).toEqual(expect.any(Object))
78
78
  expect(db.isId).toEqual(expect.any(Function))
@@ -83,11 +83,11 @@ test('manager > events', async () => {
83
83
  // note: jests tests only wait for 5s
84
84
  function eventTester(db, eventName) {
85
85
  return new Promise((resolve) => {
86
- db.on(eventName, () => resolve(true))
86
+ db.emitter.on(eventName, () => resolve(true))
87
87
  })
88
88
  }
89
89
  // Triggers on opening/open
90
- const db = monastery('localhost/monastery', { serverSelectionTimeoutMS: 2000 })
90
+ const db = monastery.manager('localhost/monastery', { serverSelectionTimeoutMS: 2000 })
91
91
  expect(db._state).toEqual('opening')
92
92
  // Start waiting for the following events
93
93
  const promises = Promise.all([
@@ -101,10 +101,24 @@ test('manager > events', async () => {
101
101
  })
102
102
 
103
103
  test('Manager > get collection', async () => {
104
- const manager = monastery('localhost/monastery', { serverSelectionTimeoutMS: 500 })
104
+ const db = monastery.manager('localhost/monastery', { serverSelectionTimeoutMS: 500 })
105
105
  // Basic aggregate command
106
- expect(await manager.get('non-collection').aggregate([], {})).toEqual([])
106
+ expect(await db.get('non-collection').aggregate([], {})).toEqual([])
107
107
  // Basic find command
108
- expect(await manager.get('non-collection').find({})).toEqual([])
109
- manager.close()
108
+ expect(await db.get('non-collection').find({})).toEqual([])
109
+ db.close()
110
+ })
111
+
112
+ 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 })
116
+
117
+ expect(monastery.opts.logLevel).not.toEqual(6) // default manager
118
+ expect(db2.opts.logLevel).not.toEqual(db1.opts.logLevel)
119
+ expect(db3.opts.logLevel).not.toEqual(db2.opts.logLevel)
120
+
121
+ db1.close()
122
+ db2.close()
123
+ db3.close()
110
124
  })
package/test/model.js CHANGED
@@ -3,11 +3,11 @@ const Model = require('../lib/model.js')
3
3
  const monastery = require('../lib/index.js')
4
4
 
5
5
  let db
6
- beforeAll(async () => { db = monastery('127.0.0.1/monastery') })
6
+ beforeAll(async () => { db = monastery.manager('127.0.0.1/monastery') })
7
7
  afterAll(async () => { db.close() })
8
8
 
9
9
  test('model > model on manager', async () => {
10
- const db2 = monastery('127.0.0.1', { logLevel: 0 })
10
+ const db2 = monastery.manager('127.0.0.1', { logLevel: 0 })
11
11
  db2.model('user', { fields: {} })
12
12
  let modelNamedConflict = db2.model('open', { fields: {} })
13
13
 
@@ -132,7 +132,7 @@ test('model setup basics', async () => {
132
132
  })
133
133
 
134
134
  test('model setup with default objects', async () => {
135
- const db2 = monastery('127.0.0.1/monastery', { defaultObjects: true })
135
+ const db2 = monastery.manager('127.0.0.1/monastery', { defaultObjects: true })
136
136
  let user = db2.model('user', { fields: {
137
137
  name: { type: 'string' },
138
138
  pets: [{ type: 'string' }],
@@ -376,7 +376,7 @@ test('model setup with messages', async () => {
376
376
 
377
377
  test('model setup with reserved and invalid rules', async () => {
378
378
  // Setup
379
- const db2 = monastery('127.0.0.1/monastery', { logLevel: 0 })
379
+ const db2 = monastery.manager('127.0.0.1/monastery', { logLevel: 0 })
380
380
  let user = db2.model('user-model', {
381
381
  fields: {
382
382
  name: {
@@ -7,14 +7,14 @@ const imagePluginFakeOpts = { awsBucket: 'fake', awsRegion: 'fake', awsAccessKey
7
7
  let db
8
8
  afterAll(async () => { db.close() })
9
9
  beforeAll(async () => {
10
- db = monastery('127.0.0.1/monastery', {
10
+ db = monastery.manager('127.0.0.1/monastery', {
11
11
  timestamps: false,
12
12
  imagePlugin: imagePluginFakeOpts,
13
13
  })
14
14
  })
15
15
 
16
16
  test('images no initialisation', async () => {
17
- const db2 = monastery('127.0.0.1/monastery', { timestamps: false })
17
+ const db2 = monastery.manager('127.0.0.1/monastery', { timestamps: false })
18
18
  db2.model('company', {
19
19
  fields: {
20
20
  logo: { type: 'image' },
@@ -666,7 +666,7 @@ test('images option defaults', async () => {
666
666
  })
667
667
 
668
668
  test('images options formats & filesizes', async () => {
669
- const db3 = monastery('127.0.0.1/monastery', {
669
+ const db3 = monastery.manager('127.0.0.1/monastery', {
670
670
  timestamps: false,
671
671
  imagePlugin: {
672
672
  ...imagePluginFakeOpts,
@@ -745,7 +745,7 @@ test('images options formats & filesizes', async () => {
745
745
 
746
746
  test('images option getSignedUrls', async () => {
747
747
  // latest (2022.02)
748
- const db3 = monastery('127.0.0.1/monastery', {
748
+ const db3 = monastery.manager('127.0.0.1/monastery', {
749
749
  timestamps: false,
750
750
  imagePlugin: {
751
751
  ...imagePluginFakeOpts,
@@ -802,7 +802,7 @@ test('images option getSignedUrls', async () => {
802
802
  })
803
803
 
804
804
  test('images options awsAcl, awsBucket, metadata, params, path', async () => {
805
- const db3 = monastery('127.0.0.1/monastery', {
805
+ const db3 = monastery.manager('127.0.0.1/monastery', {
806
806
  timestamps: false,
807
807
  imagePlugin: {
808
808
  ...imagePluginFakeOpts,
@@ -902,7 +902,7 @@ test('images options awsAcl, awsBucket, metadata, params, path', async () => {
902
902
 
903
903
  test('images option depreciations', async () => {
904
904
  // testing (filename bucketDir)
905
- const db3 = monastery('127.0.0.1/monastery', {
905
+ const db3 = monastery.manager('127.0.0.1/monastery', {
906
906
  logLevel: 1,
907
907
  timestamps: false,
908
908
  imagePlugin: {
package/test/populate.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const monastery = require('../lib/index.js')
2
2
 
3
3
  let db
4
- beforeAll(async () => { db = monastery('127.0.0.1/monastery', { timestamps: false }) })
4
+ beforeAll(async () => { db = monastery.manager('127.0.0.1/monastery', { timestamps: false }) })
5
5
  afterAll(async () => { db.close() })
6
6
 
7
7
  test('model populate', async () => {
package/test/util.js CHANGED
@@ -254,7 +254,7 @@ test('util > isId', async () => {
254
254
  expect(util.isId({})).toEqual(false)
255
255
  expect(util.isId(['5ff50fe955da2c00170de734'])).toEqual(false)
256
256
  expect(util.isId('5ff50fe955da2c00170de734')).toEqual(true)
257
- expect(util.isId(monastery.prototype.id())).toEqual(true)
257
+ expect(util.isId(monastery.id())).toEqual(true)
258
258
  })
259
259
 
260
260
  test('util > isHex24', async () => {
@@ -265,12 +265,12 @@ test('util > isHex24', async () => {
265
265
  expect(util.isHex24({})).toEqual(false)
266
266
  expect(util.isHex24(['5ff50fe955da2c00170de734'])).toEqual(false)
267
267
  expect(util.isHex24('5ff50fe955da2c00170de734')).toEqual(true)
268
- expect(util.isHex24(monastery.prototype.id())).toEqual(true)
268
+ expect(util.isHex24(monastery.id())).toEqual(true)
269
269
  })
270
270
 
271
271
 
272
272
  test('util > arrayWithSchema', async () => {
273
- let res = monastery.prototype.arrayWithSchema([{ name: { type: 'string' }}], { minLength: 1 })
273
+ let res = monastery.arrayWithSchema([{ name: { type: 'string' }}], { minLength: 1 })
274
274
  expect(res).toContainEqual({ name: { type: 'string' }})
275
275
  expect(res.schema).toEqual({ minLength: 1 })
276
276
  })
package/test/validate.js CHANGED
@@ -3,7 +3,7 @@ const monastery = require('../lib/index.js')
3
3
  const Model = require('../lib/model.js')
4
4
 
5
5
  let db
6
- beforeAll(async () => { db = monastery('127.0.0.1/monastery', { timestamps: false }) })
6
+ beforeAll(async () => { db = monastery.manager('127.0.0.1/monastery', { timestamps: false }) })
7
7
  afterAll(async () => { db.close() })
8
8
 
9
9
  test('validation basic errors', async () => {
@@ -171,7 +171,7 @@ test('validation type any', async () => {
171
171
  })
172
172
 
173
173
  test('validation schema with reserved and invalid rules', async () => {
174
- const db2 = monastery('127.0.0.1/monastery', { logLevel: 0 })
174
+ const db2 = monastery.manager('127.0.0.1/monastery', { logLevel: 0 })
175
175
  let user = db2.model('user-model', {
176
176
  fields: {
177
177
  sub: {
@@ -972,7 +972,7 @@ test('schema options default', async () => {
972
972
  })
973
973
 
974
974
  test('schema options objects', async () => {
975
- const db2 = monastery('127.0.0.1/monastery', { nullObjects: true, timestamps: false })
975
+ const db2 = monastery.manager('127.0.0.1/monastery', { nullObjects: true, timestamps: false })
976
976
  let user = db2.model('user', {
977
977
  fields: {
978
978
  location: {
@@ -998,7 +998,7 @@ test('schema options objects', async () => {
998
998
  })
999
999
 
1000
1000
  test('validate defaultObjects', async () => {
1001
- const db2 = monastery('127.0.0.1/monastery', { defaultObjects: true, timestamps: false })
1001
+ const db2 = monastery.manager('127.0.0.1/monastery', { defaultObjects: true, timestamps: false })
1002
1002
  // let base = { names: [], animals: { dogs: [] }}
1003
1003
  let user = db2.model('user', { fields: {
1004
1004
  name: { type: 'string' },
@@ -1018,7 +1018,7 @@ test('validate defaultObjects', async () => {
1018
1018
  })
1019
1019
 
1020
1020
  test('validate nullObjects', async () => {
1021
- const db2 = monastery('127.0.0.1/monastery', { nullObjects: true, timestamps: false })
1021
+ const db2 = monastery.manager('127.0.0.1/monastery', { nullObjects: true, timestamps: false })
1022
1022
  let user = db2.model('user', { fields: {
1023
1023
  names: [{ type: 'string' }],
1024
1024
  animals: {
package/test/virtuals.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const monastery = require('../lib/index.js')
2
2
 
3
3
  let db
4
- beforeAll(async () => { db = monastery('127.0.0.1/monastery', { timestamps: false }) })
4
+ beforeAll(async () => { db = monastery.manager('127.0.0.1/monastery', { timestamps: false }) })
5
5
  afterAll(async () => { db.close() })
6
6
 
7
7
  test('virtuals', async () => {