monastery 1.28.2 → 1.29.0

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/.eslintrc.json CHANGED
@@ -1,12 +1,38 @@
1
1
  {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true,
5
+ "node": true
6
+ },
7
+ "extends": [
8
+ "eslint:recommended"
9
+ ],
10
+ "globals": {
11
+ "test": true,
12
+ "expect": true
13
+ },
2
14
  "parserOptions": {
3
- "ecmaVersion": 2018,
4
- "sourceType": "module",
5
15
  "ecmaFeatures": {
6
16
  "jsx": true
7
- }
17
+ },
18
+ "ecmaVersion": "latest",
19
+ "sourceType": "module"
8
20
  },
21
+ "plugins": [],
9
22
  "rules": {
10
- //"quotes": ["error", "double"]
23
+ "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
24
+ "max-len": ["error", { "code": 120, "ignorePattern": "^\\s*<(rect|path|line)\\s" }],
25
+ "no-prototype-builtins": "off",
26
+ "no-unused-vars": ["error", { "args": "none" }],
27
+ "object-shorthand": ["error", "consistent"],
28
+ // "no-restricted-syntax": [
29
+ // "error",
30
+ // {
31
+ // "selector": "ObjectPattern > Property[shorthand=false]",
32
+ // "message": "Renaming properties within object deconstructions is not allowed."
33
+ // }
34
+ // ],
35
+ "quotes": ["error", "single"],
36
+ "semi": ["error", "never"]
11
37
  }
12
38
  }
package/docs/Gemfile CHANGED
@@ -1,20 +1,10 @@
1
- # Gemfile is only used in development
1
+ # Old
2
+ # source 'https://rubygems.org'
3
+ # gem 'github-pages', group: :jekyll_plugins
2
4
 
3
- # Old, doesn't pull the latest version
5
+ # Below pulls the latest remote_theme in development
4
6
  source 'https://rubygems.org'
5
- gem 'github-pages', group: :jekyll_plugins
6
-
7
- # # Below pulls the latest remote_theme in development
8
- # source "https://rubygems.org"
9
-
10
- # # Same as github-docs
11
- # gem "bundler"
12
- # gem "jekyll", "~> 3.9.0"
13
- # gem "jekyll-github-metadata", "~> 2.13.0"
14
- # gem "jekyll-seo-tag", "~> 2.7.1"
15
- # gem "kramdown-parser-gfm", "~> 1.1.0"
16
- # gem "github-docs", git: "https://github.com/boycce/github-docs"
17
-
18
- # group :jekyll_plugins do
19
- # gem "jekyll-remote-theme", "~> 0.4.2"
20
- # end
7
+ gem "github-docs", git: "https://github.com/boycce/github-docs"
8
+ group :jekyll_plugins do
9
+ gem "jekyll-remote-theme", "~> 0.4.2"
10
+ end
package/docs/_config.yml CHANGED
@@ -2,16 +2,13 @@ remote_theme: boycce/github-docs
2
2
  title: Monastery
3
3
  description: A straight forward MongoDB ODM built upon MonkJS
4
4
  github_url: "https://github.com/boycce/monastery"
5
- basedir: ""
5
+ basedir: "docs"
6
6
 
7
7
  # Aux links for the naviation.
8
8
  aux_links:
9
9
  "Monastery on GitHub":
10
10
  - "//github.com/boycce/monastery"
11
11
 
12
- plugins:
13
- - jekyll-remote-theme
14
-
15
12
  defaults:
16
13
  -
17
14
  scope:
package/docs/errors.md CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/docs/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ![](./assets/imgs/monastery.jpg)
2
2
 
3
- [![NPM](https://img.shields.io/npm/v/monastery.svg)](https://www.npmjs.com/package/monastery) [![Build Status](https://travis-ci.com/boycce/monastery.svg?branch=master)](https://travis-ci.com/boycce/monastery)
3
+ [![NPM](https://img.shields.io/npm/v/monastery.svg)](https://www.npmjs.com/package/monastery) [![Build Status](https://travis-ci.com/boycce/monastery.svg?branch=master)](https://app.travis-ci.com/github/boycce/monastery)
4
4
 
5
5
  ## Features
6
6
 
package/docs/rules.md CHANGED
File without changes
package/lib/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  let util = require('./util')
2
- let imagePlugin = require('./util')
3
2
  let monk = require('monk')
4
3
  let debug = require('debug')
5
4
 
package/lib/model-crud.js CHANGED
@@ -9,7 +9,8 @@ module.exports = {
9
9
  * @param {object|array} <opts.data> - documents to insert
10
10
  * @param {array|string|false} <opts.blacklist> - augment schema.insertBL, `false` will remove all blacklisting
11
11
  * @param {boolean} <opts.respond> - automatically call res.json/error (requires opts.req)
12
- * @param {array|string|true} ignoreUndefined - ignore all required fields during insert, or undefined subdocument required fields that have a defined parent/grandparent during update
12
+ * @param {array|string|true} ignoreUndefined - ignore all required fields during insert, or
13
+ * undefined subdocument required fields that have a defined parent/grandparent during update
13
14
  * @param {array|string|true} <opts.skipValidation> - skip validation for this field name(s)
14
15
  * @param {boolean} <opts.timestamps> - whether `createdAt` and `updatedAt` are automatically inserted
15
16
  * @param {any} <opts.any> - any mongodb option
@@ -21,7 +22,9 @@ module.exports = {
21
22
  opts.insert = true
22
23
  opts.model = this
23
24
  let data = opts.data = opts.data || (opts.req? opts.req.body : {})
24
- let options = util.omit(opts, ['data', 'insert', 'model', 'respond', 'ignoreUndefined', 'skipValidation', 'blacklist'])
25
+ let options = util.omit(opts, [
26
+ 'data', 'insert', 'model', 'respond', 'ignoreUndefined', 'skipValidation', 'blacklist'
27
+ ])
25
28
  if (cb && !util.isFunction(cb)) {
26
29
  throw new Error(`The callback passed to ${this.name}.insert() is not a function`)
27
30
  }
@@ -75,7 +78,7 @@ module.exports = {
75
78
  opts.one = one || opts.one
76
79
  // Operation options
77
80
  options = util.omit(opts, ['blacklist', 'one', 'populate', 'project', 'query', 'respond'])
78
- options.sort = options.sort || { "createdAt": -1 }
81
+ options.sort = options.sort || { 'createdAt': -1 }
79
82
  options.skip = Math.max(0, options.skip || 0)
80
83
  options.limit = opts.one? 1 : parseInt(options.limit || this.manager.limit || 0)
81
84
  options.addFields = options.addFields || {}
@@ -99,8 +102,8 @@ module.exports = {
99
102
  }
100
103
  // Has text search?
101
104
  // if (opts.query.$text) {
102
- // options.projection.score = { $meta: "textScore" }
103
- // options.sort = { score: { $meta: "textScore" }}
105
+ // options.projection.score = { $meta: 'textScore' }
106
+ // options.sort = { score: { $meta: 'textScore' }}
104
107
  // }
105
108
  // Sort string passed
106
109
  if (util.isString(options.sort)) {
@@ -130,7 +133,7 @@ module.exports = {
130
133
  continue
131
134
  }
132
135
  // Populate model (convert array into document & create lookup)
133
- options.addFields[path] = { "$arrayElemAt": [ "$" + path, 0 ] }
136
+ options.addFields[path] = { '$arrayElemAt': [ '$' + path, 0 ] }
134
137
  lookups.push({ $lookup: {
135
138
  from: modelName,
136
139
  localField: path,
@@ -191,7 +194,8 @@ module.exports = {
191
194
  * @param {object} <opts.query> - mongodb query object
192
195
  * @param {object|array} <opts.data> - mongodb document update object(s)
193
196
  * @param {boolean} <opts.respond> - automatically call res.json/error (requires opts.req)
194
- * @param {array|string|true} ignoreUndefined - ignore all required fields during insert, or undefined subdocument required fields that have a defined parent/grandparent during update
197
+ * @param {array|string|true} ignoreUndefined - ignore all required fields during insert, or
198
+ * undefined subdocument required fields that have a defined parent/grandparent during update
195
199
  * @param {array|string|true} <opts.skipValidation> - skip validation for this field name(s)
196
200
  * @param {boolean} <opts.timestamps> - whether `updatedAt` is automatically updated
197
201
  * @param {array|string|false} <opts.blacklist> - augment schema.updateBL, `false` will remove all blacklisting
@@ -212,7 +216,7 @@ module.exports = {
212
216
  operators = util.pluck(opts, [/^\$/])
213
217
  // Operation options
214
218
  options = util.omit(opts, ['data', 'query', 'respond', 'ignoreUndefined', 'skipValidation', 'blacklist'])
215
- options.sort = options.sort || { "createdAt": -1 }
219
+ options.sort = options.sort || { 'createdAt': -1 }
216
220
  options.limit = parseInt(options.limit || 0)
217
221
  // Sort string passed
218
222
  if (util.isString(options.sort)) {
@@ -283,7 +287,7 @@ module.exports = {
283
287
  if (util.isEmpty(opts.query)) throw new Error('Please specify opts.query')
284
288
  // Operation options
285
289
  options = util.omit(opts, ['query', 'respond'])
286
- options.sort = options.sort || { "createdAt": -1 }
290
+ options.sort = options.sort || { 'createdAt': -1 }
287
291
  options.limit = parseInt(options.limit || 1)
288
292
  // Sort string passed
289
293
  if (util.isString(options.sort)) {
@@ -397,7 +401,7 @@ module.exports = {
397
401
  let paths = (populate||[]).map(o => o && o.as? o.as : o)
398
402
 
399
403
  if (!paths.length) return blacklistProjection
400
- this._recurseFields(model.fields, "", function(path, field) {
404
+ this._recurseFields(model.fields, '', function(path, field) {
401
405
  // Remove array indexes from the path e.g. '0.'
402
406
  path = path.replace(/(\.[0-9]+)(\.|$)/, '$2')
403
407
  if (!field.model || !paths.includes(path)) return
@@ -420,7 +424,8 @@ module.exports = {
420
424
  /**
421
425
  * Merge blacklist in
422
426
  * @param {object} blacklistProjection
423
- * @param {array} paths - e.g. ['password', '-email'] - email will be whitelisted / removed from exlcusion projection
427
+ * @param {array} paths - e.g. ['password', '-email'] - email will be whitelisted / removed from
428
+ * exlcusion projection
424
429
  * @return {object} exclusion blacklist
425
430
  * @this model
426
431
  */
@@ -461,7 +466,7 @@ module.exports = {
461
466
  let findWL = [ '_id', ...this.findWL ]
462
467
  let model = this.manager.model
463
468
 
464
- this._recurseFields(this.fields, "", function(path, field) {
469
+ this._recurseFields(this.fields, '', function(path, field) {
465
470
  // Remove array indexes from the path e.g. '0.'
466
471
  path = path.replace(/(\.[0-9]+)(\.|$)/, '$2')
467
472
  //if (field.type == 'any') findWL.push(path) //exclude.push(path)
@@ -529,7 +534,7 @@ module.exports = {
529
534
  }
530
535
 
531
536
  for (let doc of util.toArray(data)) {
532
- recurseAndDeleteData(doc, "")
537
+ recurseAndDeleteData(doc, '')
533
538
  }
534
539
  return data
535
540
  },
@@ -586,7 +591,11 @@ module.exports = {
586
591
  if (!data) return
587
592
 
588
593
  // Valid model object field.
589
- if (((util.isArray(field) && field[0].model) || field.model) && data[fieldName] && (util.isObjectAndNotID(data[fieldName]))) {
594
+ if (
595
+ ((util.isArray(field) && field[0].model) || field.model) &&
596
+ data[fieldName] &&
597
+ util.isObjectAndNotID(data[fieldName])
598
+ ) {
590
599
  // Note that sometimes a single model is passed instead of an array of models via a custom populate $lookup
591
600
  out.push({
592
601
  dataRef: data[fieldName],
@@ -595,11 +604,18 @@ module.exports = {
595
604
  })
596
605
 
597
606
  // Recurse through fields that are sub-documents
598
- } else if (util.isSubdocument(field) && util.isObjectAndNotID(data[fieldName])) {
607
+ } else if (
608
+ util.isSubdocument(field) &&
609
+ util.isObjectAndNotID(data[fieldName])
610
+ ) {
599
611
  out = [...out, ...this._recurseAndFindModels(field, data[fieldName])]
600
612
 
601
613
  // Array of sub-documents or models
602
- } else if ((util.isArray(field) || field.model) && data[fieldName] && util.isObjectAndNotID(data[fieldName][0])) {
614
+ } else if (
615
+ (util.isArray(field) || field.model) &&
616
+ data[fieldName] &&
617
+ util.isObjectAndNotID(data[fieldName][0])
618
+ ) {
603
619
  // Valid model object found in array
604
620
  // Note that sometimes an array of models are passed instead of single object via a custom populate $lookup
605
621
  if (field.model || field[0].model) {
@@ -12,9 +12,11 @@ module.exports = {
12
12
  * @param {boolean(false)} update - are we validating for insert or update?
13
13
  * @param {array|string|false} blacklist - augment schema blacklist, `false` will remove all blacklisting
14
14
  * @param {array|string} projection - only return these fields, ignores blacklist
15
- * @param {array|string|true} ignoreUndefined - ignore all required fields during insert, or undefined subdocument required fields that have a defined parent/grandparent during update
15
+ * @param {array|string|true} ignoreUndefined - ignore all required fields during insert, or undefined
16
+ * subdocument required fields that have a defined parent/grandparent during update
16
17
  * @param {array|string|true} skipValidation - skip validation on these fields
17
- * @param {boolean} timestamps - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is updated, depending on the `options.update` value
18
+ * @param {boolean} timestamps - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
19
+ * updated, depending on the `options.update` value
18
20
  * @param {function} <cb> - instead of returning a promise
19
21
  * @this model
20
22
 
@@ -56,7 +58,7 @@ module.exports = {
56
58
  else continue
57
59
  if (whitelist.includes(split.join())) {
58
60
  blacklist.splice(i, 1)
59
- break;
61
+ break
60
62
  }
61
63
  }
62
64
  }
@@ -189,11 +191,11 @@ module.exports = {
189
191
  errors.push(...(verrors = this._validateRules(dataRoot, schema, value, opts, path2)))
190
192
  // Data value is array too
191
193
  if (util.isArray(value)) {
192
- var res = this._validateFields(dataRoot, field, value, opts, path2)
193
- errors.push(...res[0])
194
+ var res2 = this._validateFields(dataRoot, field, value, opts, path2)
195
+ errors.push(...res2[0])
194
196
  }
195
197
  if (util.isDefined(value) && !verrors.length) {
196
- data2[indexOrFieldName] = res? res[1] : value
198
+ data2[indexOrFieldName] = res2? res2[1] : value
197
199
  }
198
200
  }
199
201
  }, this)
@@ -211,6 +213,7 @@ module.exports = {
211
213
  * @param {object} dataRoot - data
212
214
  * @param {object} field - field schema
213
215
  * @param {string} path - full field path
216
+ * @param {object} opts - original validate() options
214
217
  * @this model
215
218
  * @return {array} errors
216
219
  */
@@ -234,45 +237,38 @@ module.exports = {
234
237
  for (let i=0, l=skippedFieldChunks.length; i<l; i++) {
235
238
  if (skippedFieldChunks[i] == '$') skippedFieldChunks[i] = '[0-9]+'
236
239
  }
237
- if (path.match(new RegExp('^' + skippedFieldChunks.join('.') + '(\.|$)'))) return []
240
+ if (path.match(new RegExp('^' + skippedFieldChunks.join('.') + '(.|$)'))) return []
238
241
  }
239
242
  }
240
243
 
241
244
  for (let ruleName in field) {
242
245
  if (this._ignoredRules.indexOf(ruleName) > -1) continue
243
- // ignore undefined updated root properties, or
244
- if (util.isUndefined(value) && ((opts.update && !path.match(/\./)) || opts.ignoreUndefined)) continue
245
- let error = this._validateRule(dataRoot, ruleName, field, field[ruleName], value, path)
246
+ let error = this._validateRule(dataRoot, ruleName, field, field[ruleName], value, opts, path)
246
247
  if (error && ruleName == 'required') return [error] // only show the required error
247
248
  if (error) errors.push(error)
248
249
  }
249
250
  return errors
250
251
  },
251
252
 
252
- _validateRule: function(dataRoot, ruleName, field, ruleArg, value, path) {
253
- //this.debug(path, field, ruleName, ruleArg, value)
253
+ _validateRule: function(dataRoot, ruleName, field, ruleArg, value, opts, path) {
254
+ // this.debug(path, field, ruleName, ruleArg, value)
254
255
  // Remove [] from the message path, and simply ignore non-numeric children to test for all array items
255
256
  ruleArg = ruleArg === true? undefined : ruleArg
256
257
  let rule = this.rules[ruleName] || rules[ruleName]
257
- let fieldName = path.match(/[^\.]+$/)[0]
258
+ let fieldName = path.match(/[^.]+$/)[0]
258
259
  let ruleMessageKey = this._getMostSpecificKeyMatchingPath(this.messages, path)
259
260
  let ruleMessage = ruleMessageKey && this.messages[ruleMessageKey][ruleName]
261
+ let ignoreUndefined = util.isDefined(opts.ignoreUndefined) ? opts.ignoreUndefined : rule.ignoreUndefined
260
262
  if (!ruleMessage) ruleMessage = rule.message
261
263
 
264
+ // Ignore undefined (if updated root property, or ignoring)
265
+ if ((ignoreUndefined || (opts.update && !path.match(/\./))) && typeof value === 'undefined') return
262
266
 
263
- if (ruleName !== 'required') {
264
- // Ignore undefined when not testing 'required'
265
- if (typeof value === 'undefined') return ////////////////////////////////////////
266
-
267
- // Ignore null if not testing required
268
- if (value === null && !field.isObject && !field.isArray) return
269
-
270
- // Ignore null if nullObject is set on objects or arrays
271
- if (value === null && field.nullObject) return
272
- }
267
+ // Ignore null (if nullObject is set on objects or arrays) (todo: change to ignoreNull)
268
+ if (field.nullObject && (field.isObject || field.isArray) && value === null) return
273
269
 
274
270
  // Ignore empty strings
275
- if (value === '' && rule.ignoreEmptyString) return
271
+ if (rule.ignoreEmptyString && value === '') return
276
272
 
277
273
  // Rule failed
278
274
  if (!rule.fn.call(dataRoot, value, ruleArg, path, this)) return {
package/lib/model.js CHANGED
@@ -61,6 +61,7 @@ let Model = module.exports = function(name, opts, manager) {
61
61
  // Update with formatted rule
62
62
  let formattedRule = util.isObject(rule)? rule : { fn: rule }
63
63
  if (!formattedRule.message) formattedRule.message = `Invalid data property for rule "${ruleName}".`
64
+ if (typeof formattedRule.ignoreUndefined == 'undefined') formattedRule.ignoreUndefined = true
64
65
  this.rules[ruleName] = formattedRule
65
66
  }
66
67
  }, this)
@@ -203,7 +204,12 @@ Model.prototype._setupFields = function(fields) {
203
204
  let nullObject = this.manager.nullObjects
204
205
  let virtual = field.length == 1 && (field[0]||{}).virtual ? true : undefined
205
206
  field.schema = {
206
- type: 'array', isArray: true, default: arrayDefault, nullObject: nullObject, virtual: virtual, ...(field.schema || {})
207
+ type: 'array',
208
+ isArray: true,
209
+ default: arrayDefault,
210
+ nullObject: nullObject,
211
+ virtual: virtual,
212
+ ...(field.schema || {})
207
213
  }
208
214
  this._setupFields(field)
209
215
 
@@ -262,7 +268,10 @@ Model.prototype._setupIndexes = function(fields) {
262
268
 
263
269
  // No db defined
264
270
  if (!(model.manager._state || '').match(/^open/)) {
265
- let error = { type: 'info', detail: `Skipping createIndex on the '${model.name}' model, no mongodb connection found.` }
271
+ let error = {
272
+ type: 'info',
273
+ detail: `Skipping createIndex on the '${model.name}' model, no mongodb connection found.`
274
+ }
266
275
  return Promise.reject(error)
267
276
  }
268
277
 
@@ -361,10 +370,10 @@ Model.prototype._timestampFields = {
361
370
  }
362
371
  }
363
372
 
364
- for (var key in crud) {
373
+ for (let key in crud) {
365
374
  Model.prototype[key] = crud[key]
366
375
  }
367
376
 
368
- for (var key in validate) {
377
+ for (let key in validate) {
369
378
  Model.prototype[key] = validate[key]
370
379
  }