monastery 3.5.0 → 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 +2 -0
- package/lib/index.js +1 -19
- package/lib/manager.js +55 -41
- package/lib/model.js +6 -2
- package/package.json +1 -1
- package/test/manager.js +15 -1
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.1](https://github.com/boycce/monastery/compare/3.5.0...3.5.1) (2024-12-23)
|
|
6
|
+
|
|
5
7
|
## [3.5.0](https://github.com/boycce/monastery/compare/3.4.3...3.5.0) (2024-12-20)
|
|
6
8
|
|
|
7
9
|
### [3.4.3](https://github.com/boycce/monastery/compare/3.4.2...3.4.3) (2024-08-27)
|
package/lib/index.js
CHANGED
|
@@ -1,26 +1,8 @@
|
|
|
1
1
|
const Manager = require('./manager')
|
|
2
|
-
const inherits = require('util').inherits
|
|
3
2
|
const rules = require('./rules.js')
|
|
4
3
|
|
|
5
|
-
function Monastery() {
|
|
6
|
-
let hasDefaultManager = false
|
|
7
|
-
|
|
8
|
-
this.manager = function(uri, opts) {
|
|
9
|
-
const manager = new Manager(uri, opts)
|
|
10
|
-
// Inherit the default manager onto Monastery once
|
|
11
|
-
if (!hasDefaultManager) {
|
|
12
|
-
hasDefaultManager = true
|
|
13
|
-
Object.assign(this, manager)
|
|
14
|
-
}
|
|
15
|
-
return manager
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Inherit Manager prototypes onto Monastery
|
|
20
|
-
inherits(Monastery, Manager)
|
|
21
|
-
|
|
22
4
|
// Exports
|
|
23
|
-
module.exports = new
|
|
5
|
+
module.exports = new Manager('init')
|
|
24
6
|
module.exports.arrayWithSchema = Manager.prototype.arrayWithSchema
|
|
25
7
|
module.exports.getSignedUrl = Manager.prototype.getSignedUrl
|
|
26
8
|
module.exports.id = Manager.prototype.id
|
package/lib/manager.js
CHANGED
|
@@ -2,14 +2,15 @@ const fs = require('fs')
|
|
|
2
2
|
const debug = require('debug')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const EventEmitter = require('events').EventEmitter
|
|
5
|
-
const inherits = require('util').inherits
|
|
6
5
|
const { MongoClient } = require('mongodb')
|
|
7
6
|
const Collection = require('./collection.js')
|
|
8
7
|
const imagePluginFile = require('../plugins/images/index.js')
|
|
9
8
|
const Model = require('./model.js')
|
|
10
9
|
const util = require('./util.js')
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
let hasDefaultManager = false
|
|
12
|
+
|
|
13
|
+
function Manager(uri, opts, parent) {
|
|
13
14
|
/**
|
|
14
15
|
* Constructs a new manager which contains a new MongoDB client
|
|
15
16
|
*
|
|
@@ -22,8 +23,11 @@ function Manager(uri, opts) {
|
|
|
22
23
|
* @link https://www.mongodb.com/docs/drivers/node/v5.9/quick-reference/
|
|
23
24
|
* @link https://mongodb.github.io/node-mongodb-native/ (all versions)
|
|
24
25
|
*/
|
|
26
|
+
const that = (!hasDefaultManager && parent) ? parent : this
|
|
27
|
+
|
|
25
28
|
if (!opts) opts = {}
|
|
26
29
|
if (!(this instanceof Manager)) return new Manager(uri, opts)
|
|
30
|
+
if (uri == 'init') return
|
|
27
31
|
|
|
28
32
|
// opts: timestamps
|
|
29
33
|
if (typeof opts.defaultFields != 'undefined') throw Error('opts.defaultFields has been depreciated, use opts.timestamps')
|
|
@@ -34,51 +38,51 @@ function Manager(uri, opts) {
|
|
|
34
38
|
|
|
35
39
|
// opts: separate out monastery options
|
|
36
40
|
const mongoOpts = Object.keys(opts||{}).reduce((acc, key) => {
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
]
|
|
42
|
-
acc[key] = opts[key]
|
|
41
|
+
if (![
|
|
42
|
+
'databaseName', 'defaultObjects', 'logLevel', 'imagePlugin', 'limit', 'noDefaults', 'nullObjects',
|
|
43
|
+
'promise', 'timestamps', 'useMilliseconds',
|
|
44
|
+
].includes(key)) {
|
|
45
|
+
acc[key] = opts[key]
|
|
43
46
|
}
|
|
44
47
|
return acc
|
|
45
48
|
}, {})
|
|
46
49
|
|
|
47
50
|
// Add properties
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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()
|
|
56
60
|
|
|
57
61
|
// If there is no DEBUG= environment variable, we will need to force the debugs on (due to bug on debug@4.3.4)
|
|
58
62
|
if (!debug.namespaces) {
|
|
59
|
-
if (opts.logLevel
|
|
60
|
-
if (opts.logLevel
|
|
61
|
-
if (opts.logLevel
|
|
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
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
// Create a new MongoDB Client
|
|
65
|
-
if (typeof
|
|
66
|
-
|
|
67
|
-
|
|
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)
|
|
68
72
|
} else {
|
|
69
|
-
|
|
73
|
+
that.client = that.uri
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
// Listen to MongoDB events
|
|
73
77
|
for (let eventName of ['close', 'error', 'timeout']) {
|
|
74
|
-
|
|
78
|
+
that.client.on(eventName, (e) => that.emitter.emit(eventName, e))
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
// Listen to custom open event
|
|
78
|
-
|
|
82
|
+
that.emitter.on('open', () => {
|
|
79
83
|
// wait for all the on('open') events to get called first
|
|
80
84
|
setTimeout(() => {
|
|
81
|
-
for (let item of
|
|
85
|
+
for (let item of that._openQueue) {
|
|
82
86
|
item()
|
|
83
87
|
}
|
|
84
88
|
})
|
|
@@ -86,12 +90,17 @@ function Manager(uri, opts) {
|
|
|
86
90
|
|
|
87
91
|
// Initiate any plugins
|
|
88
92
|
if (opts.imagePlugin) {
|
|
89
|
-
imagePluginFile.setup(
|
|
93
|
+
imagePluginFile.setup(that, util.isObject(opts.imagePlugin) ? opts.imagePlugin : {})
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
//
|
|
93
|
-
if (
|
|
94
|
-
|
|
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)
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
Manager.prototype.arrayWithSchema = function(array, schema) {
|
|
@@ -104,9 +113,9 @@ Manager.prototype.close = async function() {
|
|
|
104
113
|
* Close the database connection, gracefully
|
|
105
114
|
*/
|
|
106
115
|
const _close = async () => {
|
|
107
|
-
this.emit(this._state = 'closing')
|
|
116
|
+
this.emitter.emit(this._state = 'closing')
|
|
108
117
|
await this.client.close()
|
|
109
|
-
this.emit(this._state = 'close')
|
|
118
|
+
this.emitter.emit(this._state = 'close')
|
|
110
119
|
}
|
|
111
120
|
if (this._state == 'open') {
|
|
112
121
|
return await _close()
|
|
@@ -185,6 +194,7 @@ Manager.prototype.models = async function(pathname, opts) {
|
|
|
185
194
|
* @param {string} pathname
|
|
186
195
|
* @param {object} opts:
|
|
187
196
|
* @param {boolean} opts.waitForIndexes - Wait for indexes to be created?
|
|
197
|
+
* @param {boolean} opts.skipIfExists - Skip if the model already exists?
|
|
188
198
|
* @return {Promise(object)} - e.g. { user: , article: , .. }
|
|
189
199
|
* @this Manager
|
|
190
200
|
*/
|
|
@@ -193,12 +203,18 @@ Manager.prototype.models = async function(pathname, opts) {
|
|
|
193
203
|
if (!pathname || typeof pathname !== 'string') {
|
|
194
204
|
throw 'The path must be a valid pathname'
|
|
195
205
|
}
|
|
206
|
+
if (!fs.existsSync(pathname)) {
|
|
207
|
+
this.warn(`The models path ${pathname} does not exist`)
|
|
208
|
+
return out
|
|
209
|
+
}
|
|
196
210
|
let filenames = fs.readdirSync(pathname)
|
|
197
211
|
for (let filename of filenames) {
|
|
198
212
|
// Ignore folders
|
|
199
213
|
if (!filename.match(/\.[cm]?js$/)) continue
|
|
200
214
|
let name = filename.replace('.js', '')
|
|
201
215
|
let filepath = path.join(pathname, filename)
|
|
216
|
+
// Skip
|
|
217
|
+
if (opts.skipIfExists && this.models[name]) continue
|
|
202
218
|
// We can't use require() here since we need to be able to import both CJS and ES6 modules
|
|
203
219
|
let definition = ((await import(filepath))||{}).default
|
|
204
220
|
// When a commonJS project uses babel (e.g. `nodemon -r @babel/register`, similar to `-r esm`), import()
|
|
@@ -219,7 +235,7 @@ Manager.prototype.onError = function(fn) {
|
|
|
219
235
|
* @return {Promise}
|
|
220
236
|
*/
|
|
221
237
|
return new Promise((resolve, reject) => {
|
|
222
|
-
this.on('error', (err) => {
|
|
238
|
+
this.emitter.on('error', (err) => {
|
|
223
239
|
resolve(err)
|
|
224
240
|
})
|
|
225
241
|
}).then((err) => {
|
|
@@ -237,10 +253,10 @@ Manager.prototype.onOpen = function(fn) {
|
|
|
237
253
|
if (this._state == 'open') {
|
|
238
254
|
resolve(this)
|
|
239
255
|
}
|
|
240
|
-
this.on('open', () => {
|
|
256
|
+
this.emitter.on('open', () => {
|
|
241
257
|
resolve(this)
|
|
242
258
|
})
|
|
243
|
-
this.on('error', (err) => {
|
|
259
|
+
this.emitter.on('error', (err) => {
|
|
244
260
|
reject(err)
|
|
245
261
|
})
|
|
246
262
|
}).then((err) => { // If `then` is not chained, the error will be thrown, detached!
|
|
@@ -257,16 +273,16 @@ Manager.prototype.open = async function() {
|
|
|
257
273
|
this._state = 'opening'
|
|
258
274
|
this.db = this.client.db()
|
|
259
275
|
await this.client.connect() // now optional since db().command() will auto-connect
|
|
260
|
-
this.emit(this._state = 'open', this)
|
|
276
|
+
this.emitter.emit(this._state = 'open', this)
|
|
261
277
|
return this
|
|
262
278
|
|
|
263
279
|
} catch (err) {
|
|
264
280
|
this._state = 'closed'
|
|
265
281
|
// Only emit the error if there are listeners, since it will throw an unhandled error
|
|
266
|
-
if (this.listeners('error').length > 0) {
|
|
267
|
-
this.emit('error', err)
|
|
282
|
+
if (this.emitter.listeners('error').length > 0) {
|
|
283
|
+
this.emitter.emit('error', err)
|
|
268
284
|
}
|
|
269
|
-
if (this.opts.promise || this.listeners('error').length == 0) {
|
|
285
|
+
if (this.opts.promise || this.emitter.listeners('error').length == 0) {
|
|
270
286
|
throw new Error(err)
|
|
271
287
|
}
|
|
272
288
|
}
|
|
@@ -284,6 +300,4 @@ Manager.prototype._getSignedUrl = () => {
|
|
|
284
300
|
throw new Error('monastery._getSignedUrl() has been moved to monastery.getSignedUrl()')
|
|
285
301
|
}
|
|
286
302
|
|
|
287
|
-
inherits(Manager, EventEmitter)
|
|
288
|
-
|
|
289
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
|
|
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.5.
|
|
5
|
+
"version": "3.5.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/test/manager.js
CHANGED
|
@@ -83,7 +83,7 @@ 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
|
|
@@ -108,3 +108,17 @@ test('Manager > get collection', async () => {
|
|
|
108
108
|
expect(await db.get('non-collection').find({})).toEqual([])
|
|
109
109
|
db.close()
|
|
110
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()
|
|
124
|
+
})
|