monastery 3.5.0 → 3.5.2
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 +4 -0
- package/lib/index.js +1 -19
- package/lib/manager.js +65 -43
- package/lib/model.js +6 -2
- package/package.json +1 -1
- package/test/manager.js +15 -1
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.2](https://github.com/boycce/monastery/compare/3.5.1...3.5.2) (2025-01-22)
|
|
6
|
+
|
|
7
|
+
### [3.5.1](https://github.com/boycce/monastery/compare/3.5.0...3.5.1) (2024-12-23)
|
|
8
|
+
|
|
5
9
|
## [3.5.0](https://github.com/boycce/monastery/compare/3.4.3...3.5.0) (2024-12-20)
|
|
6
10
|
|
|
7
11
|
### [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()
|
|
@@ -179,26 +188,33 @@ Manager.prototype.isId = function(value) {
|
|
|
179
188
|
return util.isId(value)
|
|
180
189
|
}
|
|
181
190
|
|
|
182
|
-
Manager.prototype.models = async function(pathname, opts) {
|
|
191
|
+
Manager.prototype.models = async function(pathname, opts={}) {
|
|
183
192
|
/**
|
|
184
193
|
* Setup model definitions from a folder location
|
|
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
|
*/
|
|
191
201
|
let out = {}
|
|
192
|
-
let { waitForIndexes } = opts
|
|
202
|
+
let { waitForIndexes } = 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,11 @@ Manager.prototype.onError = function(fn) {
|
|
|
219
235
|
* @return {Promise}
|
|
220
236
|
*/
|
|
221
237
|
return new Promise((resolve, reject) => {
|
|
222
|
-
this.
|
|
238
|
+
if (!this.emitter) {
|
|
239
|
+
reject(new Error('Emitter not found! This can happen if two seperate monastery packages are imported into the ' +
|
|
240
|
+
'same project. E.g. the first package initializes the manager, and the second package tries to use it.'))
|
|
241
|
+
}
|
|
242
|
+
this.emitter.on('error', (err) => {
|
|
223
243
|
resolve(err)
|
|
224
244
|
})
|
|
225
245
|
}).then((err) => {
|
|
@@ -234,13 +254,17 @@ Manager.prototype.onOpen = function(fn) {
|
|
|
234
254
|
* @return {Promise(manager)}
|
|
235
255
|
*/
|
|
236
256
|
return new Promise((resolve, reject) => {
|
|
257
|
+
if (!this.emitter) {
|
|
258
|
+
reject(new Error('Emitter not found! This can happen if two seperate monastery packages are imported into the ' +
|
|
259
|
+
'same project. E.g. the first package initializes the manager, and the second package tries to use it.'))
|
|
260
|
+
}
|
|
237
261
|
if (this._state == 'open') {
|
|
238
262
|
resolve(this)
|
|
239
263
|
}
|
|
240
|
-
this.on('open', () => {
|
|
264
|
+
this.emitter.on('open', () => {
|
|
241
265
|
resolve(this)
|
|
242
266
|
})
|
|
243
|
-
this.on('error', (err) => {
|
|
267
|
+
this.emitter.on('error', (err) => {
|
|
244
268
|
reject(err)
|
|
245
269
|
})
|
|
246
270
|
}).then((err) => { // If `then` is not chained, the error will be thrown, detached!
|
|
@@ -257,16 +281,16 @@ Manager.prototype.open = async function() {
|
|
|
257
281
|
this._state = 'opening'
|
|
258
282
|
this.db = this.client.db()
|
|
259
283
|
await this.client.connect() // now optional since db().command() will auto-connect
|
|
260
|
-
this.emit(this._state = 'open', this)
|
|
284
|
+
this.emitter.emit(this._state = 'open', this)
|
|
261
285
|
return this
|
|
262
286
|
|
|
263
287
|
} catch (err) {
|
|
264
288
|
this._state = 'closed'
|
|
265
289
|
// 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)
|
|
290
|
+
if (this.emitter.listeners('error').length > 0) {
|
|
291
|
+
this.emitter.emit('error', err)
|
|
268
292
|
}
|
|
269
|
-
if (this.opts.promise || this.listeners('error').length == 0) {
|
|
293
|
+
if (this.opts.promise || this.emitter.listeners('error').length == 0) {
|
|
270
294
|
throw new Error(err)
|
|
271
295
|
}
|
|
272
296
|
}
|
|
@@ -284,6 +308,4 @@ Manager.prototype._getSignedUrl = () => {
|
|
|
284
308
|
throw new Error('monastery._getSignedUrl() has been moved to monastery.getSignedUrl()')
|
|
285
309
|
}
|
|
286
310
|
|
|
287
|
-
inherits(Manager, EventEmitter)
|
|
288
|
-
|
|
289
311
|
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.2",
|
|
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
|
+
})
|