@bedrockio/model 0.6.0 → 0.7.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/README.md CHANGED
@@ -19,6 +19,7 @@ Bedrock utilities for model creation.
19
19
  - [Delete Hooks](#delete-hooks)
20
20
  - [Access Control](#access-control)
21
21
  - [Assign](#assign)
22
+ - [Upsert](#upsert)
22
23
  - [Slugs](#slugs)
23
24
  - [Testing](#testing)
24
25
  - [Troubleshooting](#troubleshooting)
@@ -568,11 +569,55 @@ will be flattened to:
568
569
  }
569
570
  ```
570
571
 
572
+ #### Searching Arrays
573
+
574
+ Passing an array to `search` will perform a query using `$in`, which matches on
575
+ the intersection of any elements. For example,
576
+
577
+ ```json
578
+ tags: ["one", "two"]
579
+ ```
580
+
581
+ is effectively saying "match any documents whose `tags` array contains any of
582
+ `one` or `two`".
583
+
584
+ For this reason, passing an empty array here is ambiguous as it could be asking:
585
+
586
+ - Match any documents whose `tags` array is empty.
587
+ - Match any documents whose `tags` array contains any of `[]` (ie. no elements
588
+ passed so no match).
589
+
590
+ The difference here is subtle, but for example in a UI field where tags can be
591
+ chosen but none happen to be selected, it would be unexpected to return
592
+ documents simply because their `tags` array happens to be empty.
593
+
594
+ The `search` method takes the simpler approach here where an empty array will
595
+ simply be passed along to `$in`, which will never result in a match.
596
+
597
+ However, as a special case, `null` may instead be passed to `search` for array
598
+ fields to explicitly search for empty arrays:
599
+
600
+ ```js
601
+ await User.search({
602
+ tags: null,
603
+ });
604
+ // Equivalent to:
605
+ await User.find({
606
+ tags: [],
607
+ });
608
+ ```
609
+
610
+ This works because Mongoose will always initialize an array field in its
611
+ documents (ie. the field is always guarnateed to exist and be an array), so an
612
+ empty array can be thought of as equivalent to `{ $exists: false }` for array
613
+ fields. Thinking of it this way makes it more intuitive that `null` should
614
+ match.
615
+
571
616
  #### Range Based Search
572
617
 
573
618
  Additionally, date and number fields allow range queries in the form:
574
619
 
575
- ```
620
+ ```json
576
621
  age: {
577
622
  gt: 1
578
623
  lt: 2
@@ -653,7 +698,57 @@ schema.pre('save', function () {
653
698
  });
654
699
  ```
655
700
 
656
- ##### Syncing Cache Fields
701
+ #### Syncing Cached Fields
702
+
703
+ When a foreign document is updated the cached fields will be out of sync, for
704
+ example:
705
+
706
+ ```js
707
+ await shop.save();
708
+ console.log(shop.userName);
709
+ // The current user name
710
+
711
+ user.name = 'New Name';
712
+ await user.save();
713
+
714
+ shop = await Shop.findById(shop.id);
715
+ console.log(shop.userName);
716
+ // Cached userName is out of sync as the user has been updated
717
+ ```
718
+
719
+ A simple mechanism is provided via the `sync` key to keep the documents in sync:
720
+
721
+ ```jsonc
722
+ // In user.json
723
+ {
724
+ "attributes": {
725
+ "name": "String"
726
+ },
727
+ "search": {
728
+ "sync": [
729
+ {
730
+ "ref": "Shop",
731
+ "path": "user"
732
+ }
733
+ ]
734
+ }
735
+ }
736
+ ```
737
+
738
+ This is the equivalent of running the following in a post save hook:
739
+
740
+ ```js
741
+ const shops = await Shop.find({
742
+ user: user.id,
743
+ });
744
+ for (let shop of shops) {
745
+ await shop.save();
746
+ }
747
+ ```
748
+
749
+ This will run the hooks on each shop, synchronizing the cached fields.
750
+
751
+ ##### Initial Sync
657
752
 
658
753
  When first applying or making changes to defined cached search fields, existing
659
754
  documents will be out of sync. The static method `syncCacheFields` is provided
@@ -1484,11 +1579,17 @@ user.assign(ctx.request.body);
1484
1579
  Object.assign(user, ctx.request.body);
1485
1580
  ```
1486
1581
 
1487
- This is functionally identical to `Object.assign` with the exception that
1488
- `ObjectId` reference fields can be unset by passing falsy values. This method is
1489
- provided as `undefined` cannot be represented in JSON which requires using
1490
- either a `null` or empty string, both of which would be stored in the database
1491
- if naively assigned with `Object.assign`.
1582
+ This is functionally identical to `Object.assign` with the exception that fields
1583
+ can be unset by passing falsy values. This method is provided as `undefined`
1584
+ cannot be represented in JSON which requires using either a `null` or empty
1585
+ string, both of which would be stored in the database if naively assigned with
1586
+ `Object.assign`.
1587
+
1588
+ ### Upsert
1589
+
1590
+ This module adds a single `findOrCreate` convenience method that is easy to
1591
+ understand and avoids some of the gotchas that come with upserting documents in
1592
+ Mongoose.
1492
1593
 
1493
1594
  ### Slugs
1494
1595
 
@@ -12,7 +12,7 @@ function applyAssign(schema) {
12
12
  schema.method('assign', function assign(fields) {
13
13
  unsetReferenceFields(fields, schema.obj);
14
14
  for (let [path, value] of Object.entries(flattenObject(fields))) {
15
- if (value === null) {
15
+ if (value === null || value === '') {
16
16
  this.set(path, undefined);
17
17
  } else {
18
18
  this.set(path, value);
@@ -237,6 +237,8 @@ function normalizeQuery(query, schema, root = {}, rootPath = []) {
237
237
  root[path.join('.')] = {
238
238
  $in: value
239
239
  };
240
+ } else if (isEmptyArrayQuery(schema, key, value)) {
241
+ root[path.join('.')] = [];
240
242
  } else {
241
243
  root[path.join('.')] = value;
242
244
  }
@@ -256,12 +258,15 @@ function isNestedQuery(key, value) {
256
258
  function isArrayQuery(key, value) {
257
259
  return !isMongoOperator(key) && !isInclude(key) && Array.isArray(value);
258
260
  }
261
+ function isEmptyArrayQuery(schema, key, value) {
262
+ return !isMongoOperator(key) && (0, _utils.isArrayField)(schema, key) && value === null;
263
+ }
259
264
  function isRangeQuery(schema, key, value) {
260
265
  // Range queries only allowed on Date and Number fields.
261
266
  if (!(0, _utils.isDateField)(schema, key) && !(0, _utils.isNumberField)(schema, key)) {
262
267
  return false;
263
268
  }
264
- return typeof value === 'object';
269
+ return typeof value === 'object' && !!value;
265
270
  }
266
271
  function mapOperatorQuery(obj) {
267
272
  const query = {};
@@ -323,9 +328,7 @@ function applySearchCache(schema, definition) {
323
328
  if (!force) {
324
329
  const $or = Object.keys(cache).map(cachedField => {
325
330
  return {
326
- [cachedField]: {
327
- $exists: false
328
- }
331
+ [cachedField]: null
329
332
  };
330
333
  });
331
334
  query.$or = $or;
package/dist/cjs/utils.js CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getField = getField;
7
7
  exports.getInnerField = getInnerField;
8
+ exports.isArrayField = isArrayField;
8
9
  exports.isDateField = isDateField;
9
10
  exports.isEqual = isEqual;
10
11
  exports.isMongooseSchema = isMongooseSchema;
@@ -44,6 +45,10 @@ function isDateField(obj, path) {
44
45
  function isNumberField(obj, path) {
45
46
  return isType(obj, path, 'Number');
46
47
  }
48
+ function isArrayField(obj, path) {
49
+ const field = getField(obj, path);
50
+ return Array.isArray(field?.type);
51
+ }
47
52
  function isType(obj, path, test) {
48
53
  const {
49
54
  type
@@ -83,7 +83,7 @@ function applyValidation(schema, definition) {
83
83
  model: this,
84
84
  appendSchema,
85
85
  allowInclude,
86
- allowEmpty: true,
86
+ stripEmpty: true,
87
87
  stripDeleted: true,
88
88
  stripTimestamps: true,
89
89
  allowDefaultTags: true,
@@ -106,7 +106,7 @@ function applyValidation(schema, definition) {
106
106
  model: this,
107
107
  appendSchema,
108
108
  allowInclude,
109
- allowUnset: true,
109
+ allowNull: true,
110
110
  skipRequired: true,
111
111
  stripUnknown: true,
112
112
  stripDeleted: true,
@@ -122,13 +122,6 @@ function applyValidation(schema, definition) {
122
122
  })
123
123
  });
124
124
  });
125
- schema.static('getDeleteValidation', function getDeleteValidation() {
126
- const allowed = definition.access?.delete || 'all';
127
- return validateAccess('delete', _yada.default, allowed, {
128
- model: this,
129
- message: 'You do not have permissions to delete this document.'
130
- });
131
- });
132
125
  schema.static('getSearchValidation', function getSearchValidation(options = {}) {
133
126
  const {
134
127
  defaults,
@@ -137,6 +130,8 @@ function applyValidation(schema, definition) {
137
130
  } = options;
138
131
  return getSchemaFromMongoose(schema, {
139
132
  model: this,
133
+ allowNull: true,
134
+ stripEmpty: true,
140
135
  allowSearch: true,
141
136
  skipRequired: true,
142
137
  allowInclude: true,
@@ -151,6 +146,13 @@ function applyValidation(schema, definition) {
151
146
  })
152
147
  });
153
148
  });
149
+ schema.static('getDeleteValidation', function getDeleteValidation() {
150
+ const allowed = definition.access?.delete || 'all';
151
+ return validateAccess('delete', _yada.default, allowed, {
152
+ model: this,
153
+ message: 'You do not have permissions to delete this document.'
154
+ });
155
+ });
154
156
  schema.static('getIncludeValidation', function getIncludeValidation() {
155
157
  return _include.INCLUDE_FIELD_SCHEMA;
156
158
  });
@@ -227,6 +229,7 @@ function getObjectSchema(arg, options) {
227
229
  } else if (typeof arg === 'object') {
228
230
  const {
229
231
  stripUnknown,
232
+ stripEmpty,
230
233
  expandDotSyntax
231
234
  } = options;
232
235
  const map = {};
@@ -241,6 +244,11 @@ function getObjectSchema(arg, options) {
241
244
  stripUnknown: true
242
245
  });
243
246
  }
247
+ if (stripEmpty) {
248
+ schema = schema.options({
249
+ stripEmpty: true
250
+ });
251
+ }
244
252
  if (expandDotSyntax) {
245
253
  schema = schema.options({
246
254
  expandDotSyntax: true
@@ -284,13 +292,24 @@ function getSchemaForTypedef(typedef, options = {}) {
284
292
  } else {
285
293
  schema = getSchemaForType(type, options);
286
294
 
287
- // Unsetting only allowed for primitive types.
288
- if (allowUnset(typedef, options)) {
289
- schema = _yada.default.allow(null, '', schema);
290
- } else if (allowEmpty(typedef, options)) {
295
+ // Null may be allowed to unset non-required fields
296
+ // in an update operation or to search for non-existent
297
+ // fields in a search operation.
298
+ if (allowNull(typedef, options)) {
299
+ schema = schema.nullable();
300
+ }
301
+
302
+ // Empty strings are allowed to unset non-required fields
303
+ // in an update operation. Technically this should be null,
304
+ // however empty strings are allowed here as well as they
305
+ // generally play nicer with front-end components. For
306
+ // ObjectId fields the empty string must be appended here.
307
+ if (disallowEmpty(typedef, options)) {
291
308
  schema = schema.options({
292
- allowEmpty: true
309
+ allowEmpty: false
293
310
  });
311
+ } else if (appendEmpty(typedef, options)) {
312
+ schema = _yada.default.allow(schema, '');
294
313
  }
295
314
  }
296
315
  if (isRequired(typedef, options)) {
@@ -402,11 +421,38 @@ function getSearchSchema(schema, type) {
402
421
  function isRequired(typedef, options) {
403
422
  return typedef.required && !typedef.default && !options.skipRequired;
404
423
  }
405
- function allowUnset(typedef, options) {
406
- return options.allowUnset && !typedef.required;
424
+ function allowNull(typedef, options) {
425
+ if (!options.allowNull) {
426
+ return false;
427
+ }
428
+ const {
429
+ required,
430
+ type
431
+ } = typedef;
432
+ return !required && type !== 'Boolean';
407
433
  }
408
- function allowEmpty(typedef, options) {
409
- return options.allowEmpty && typedef.type === 'String';
434
+ function disallowEmpty(typedef, options) {
435
+ if (!options.allowNull) {
436
+ return false;
437
+ }
438
+ const {
439
+ type
440
+ } = typedef;
441
+ if (type === 'String' || type === 'ObjectId') {
442
+ return typedef.required;
443
+ } else {
444
+ return false;
445
+ }
446
+ }
447
+ function appendEmpty(typedef, options) {
448
+ if (!options.allowNull) {
449
+ return false;
450
+ }
451
+ const {
452
+ required,
453
+ type
454
+ } = typedef;
455
+ return !required && type === 'ObjectId';
410
456
  }
411
457
  function isExcludedField(field, options) {
412
458
  if ((0, _utils.isSchemaTypedef)(field)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/model",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Bedrock utilities for model creation.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -30,22 +30,22 @@
30
30
  "lodash": "^4.17.21"
31
31
  },
32
32
  "peerDependencies": {
33
- "@bedrockio/yada": "^1.1.2",
34
- "mongoose": "^8.5.1"
33
+ "@bedrockio/yada": "^1.2.1",
34
+ "mongoose": "^8.6.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@babel/cli": "^7.20.7",
38
38
  "@babel/core": "^7.20.12",
39
39
  "@babel/preset-env": "^7.20.2",
40
40
  "@bedrockio/prettier-config": "^1.0.2",
41
- "@bedrockio/yada": "^1.1.2",
41
+ "@bedrockio/yada": "^1.2.1",
42
42
  "@shelf/jest-mongodb": "^4.3.2",
43
43
  "eslint": "^8.33.0",
44
44
  "eslint-plugin-bedrock": "^1.0.26",
45
45
  "jest": "^29.4.1",
46
46
  "jest-environment-node": "^29.4.1",
47
47
  "mongodb": "^6.5.0",
48
- "mongoose": "^8.5.1",
48
+ "mongoose": "^8.6.2",
49
49
  "prettier-eslint": "^15.0.1",
50
50
  "typescript": "^4.9.5"
51
51
  },
package/src/assign.js CHANGED
@@ -7,7 +7,7 @@ export function applyAssign(schema) {
7
7
  schema.method('assign', function assign(fields) {
8
8
  unsetReferenceFields(fields, schema.obj);
9
9
  for (let [path, value] of Object.entries(flattenObject(fields))) {
10
- if (value === null) {
10
+ if (value === null || value === '') {
11
11
  this.set(path, undefined);
12
12
  } else {
13
13
  this.set(path, value);
package/src/search.js CHANGED
@@ -3,7 +3,7 @@ import logger from '@bedrockio/logger';
3
3
  import mongoose from 'mongoose';
4
4
  import { get, pick, isEmpty, escapeRegExp, isPlainObject } from 'lodash';
5
5
 
6
- import { isDateField, isNumberField, getField } from './utils';
6
+ import { isArrayField, isDateField, isNumberField, getField } from './utils';
7
7
  import { SEARCH_DEFAULTS } from './const';
8
8
  import { OBJECT_ID_SCHEMA } from './validation';
9
9
  import { debug } from './env';
@@ -241,6 +241,8 @@ function normalizeQuery(query, schema, root = {}, rootPath = []) {
241
241
  root[path.join('.')] = parseRegexQuery(value);
242
242
  } else if (isArrayQuery(key, value)) {
243
243
  root[path.join('.')] = { $in: value };
244
+ } else if (isEmptyArrayQuery(schema, key, value)) {
245
+ root[path.join('.')] = [];
244
246
  } else {
245
247
  root[path.join('.')] = value;
246
248
  }
@@ -262,12 +264,16 @@ function isArrayQuery(key, value) {
262
264
  return !isMongoOperator(key) && !isInclude(key) && Array.isArray(value);
263
265
  }
264
266
 
267
+ function isEmptyArrayQuery(schema, key, value) {
268
+ return !isMongoOperator(key) && isArrayField(schema, key) && value === null;
269
+ }
270
+
265
271
  function isRangeQuery(schema, key, value) {
266
272
  // Range queries only allowed on Date and Number fields.
267
273
  if (!isDateField(schema, key) && !isNumberField(schema, key)) {
268
274
  return false;
269
275
  }
270
- return typeof value === 'object';
276
+ return typeof value === 'object' && !!value;
271
277
  }
272
278
 
273
279
  function mapOperatorQuery(obj) {
@@ -340,9 +346,7 @@ function applySearchCache(schema, definition) {
340
346
  if (!force) {
341
347
  const $or = Object.keys(cache).map((cachedField) => {
342
348
  return {
343
- [cachedField]: {
344
- $exists: false,
345
- },
349
+ [cachedField]: null,
346
350
  };
347
351
  });
348
352
  query.$or = $or;
package/src/utils.js CHANGED
@@ -36,6 +36,11 @@ export function isNumberField(obj, path) {
36
36
  return isType(obj, path, 'Number');
37
37
  }
38
38
 
39
+ export function isArrayField(obj, path) {
40
+ const field = getField(obj, path);
41
+ return Array.isArray(field?.type);
42
+ }
43
+
39
44
  function isType(obj, path, test) {
40
45
  const { type } = getInnerField(obj, path);
41
46
  return type === test || type === mongoose.Schema.Types[test];
package/src/validation.js CHANGED
@@ -95,7 +95,7 @@ export function applyValidation(schema, definition) {
95
95
  model: this,
96
96
  appendSchema,
97
97
  allowInclude,
98
- allowEmpty: true,
98
+ stripEmpty: true,
99
99
  stripDeleted: true,
100
100
  stripTimestamps: true,
101
101
  allowDefaultTags: true,
@@ -119,7 +119,7 @@ export function applyValidation(schema, definition) {
119
119
  model: this,
120
120
  appendSchema,
121
121
  allowInclude,
122
- allowUnset: true,
122
+ allowNull: true,
123
123
  skipRequired: true,
124
124
  stripUnknown: true,
125
125
  stripDeleted: true,
@@ -137,21 +137,14 @@ export function applyValidation(schema, definition) {
137
137
  }
138
138
  );
139
139
 
140
- schema.static('getDeleteValidation', function getDeleteValidation() {
141
- const allowed = definition.access?.delete || 'all';
142
- return validateAccess('delete', yd, allowed, {
143
- model: this,
144
- message: 'You do not have permissions to delete this document.',
145
- });
146
- });
147
-
148
140
  schema.static(
149
141
  'getSearchValidation',
150
142
  function getSearchValidation(options = {}) {
151
143
  const { defaults, includeDeleted, ...appendSchema } = options;
152
-
153
144
  return getSchemaFromMongoose(schema, {
154
145
  model: this,
146
+ allowNull: true,
147
+ stripEmpty: true,
155
148
  allowSearch: true,
156
149
  skipRequired: true,
157
150
  allowInclude: true,
@@ -168,6 +161,14 @@ export function applyValidation(schema, definition) {
168
161
  }
169
162
  );
170
163
 
164
+ schema.static('getDeleteValidation', function getDeleteValidation() {
165
+ const allowed = definition.access?.delete || 'all';
166
+ return validateAccess('delete', yd, allowed, {
167
+ model: this,
168
+ message: 'You do not have permissions to delete this document.',
169
+ });
170
+ });
171
+
171
172
  schema.static('getIncludeValidation', function getIncludeValidation() {
172
173
  return INCLUDE_FIELD_SCHEMA;
173
174
  });
@@ -238,7 +239,7 @@ function getObjectSchema(arg, options) {
238
239
  } else if (Array.isArray(arg)) {
239
240
  return getArraySchema(arg, options);
240
241
  } else if (typeof arg === 'object') {
241
- const { stripUnknown, expandDotSyntax } = options;
242
+ const { stripUnknown, stripEmpty, expandDotSyntax } = options;
242
243
  const map = {};
243
244
  for (let [key, field] of Object.entries(arg)) {
244
245
  if (!isExcludedField(field, options)) {
@@ -253,6 +254,13 @@ function getObjectSchema(arg, options) {
253
254
  stripUnknown: true,
254
255
  });
255
256
  }
257
+
258
+ if (stripEmpty) {
259
+ schema = schema.options({
260
+ stripEmpty: true,
261
+ });
262
+ }
263
+
256
264
  if (expandDotSyntax) {
257
265
  schema = schema.options({
258
266
  expandDotSyntax: true,
@@ -299,13 +307,24 @@ function getSchemaForTypedef(typedef, options = {}) {
299
307
  } else {
300
308
  schema = getSchemaForType(type, options);
301
309
 
302
- // Unsetting only allowed for primitive types.
303
- if (allowUnset(typedef, options)) {
304
- schema = yd.allow(null, '', schema);
305
- } else if (allowEmpty(typedef, options)) {
310
+ // Null may be allowed to unset non-required fields
311
+ // in an update operation or to search for non-existent
312
+ // fields in a search operation.
313
+ if (allowNull(typedef, options)) {
314
+ schema = schema.nullable();
315
+ }
316
+
317
+ // Empty strings are allowed to unset non-required fields
318
+ // in an update operation. Technically this should be null,
319
+ // however empty strings are allowed here as well as they
320
+ // generally play nicer with front-end components. For
321
+ // ObjectId fields the empty string must be appended here.
322
+ if (disallowEmpty(typedef, options)) {
306
323
  schema = schema.options({
307
- allowEmpty: true,
324
+ allowEmpty: false,
308
325
  });
326
+ } else if (appendEmpty(typedef, options)) {
327
+ schema = yd.allow(schema, '');
309
328
  }
310
329
  }
311
330
 
@@ -352,6 +371,7 @@ function getSchemaForTypedef(typedef, options = {}) {
352
371
  if (typedef.writeAccess && options.requireWriteAccess) {
353
372
  schema = validateAccess('write', schema, typedef.writeAccess, options);
354
373
  }
374
+
355
375
  return schema;
356
376
  }
357
377
 
@@ -460,12 +480,32 @@ function isRequired(typedef, options) {
460
480
  return typedef.required && !typedef.default && !options.skipRequired;
461
481
  }
462
482
 
463
- function allowUnset(typedef, options) {
464
- return options.allowUnset && !typedef.required;
483
+ function allowNull(typedef, options) {
484
+ if (!options.allowNull) {
485
+ return false;
486
+ }
487
+ const { required, type } = typedef;
488
+ return !required && type !== 'Boolean';
489
+ }
490
+
491
+ function disallowEmpty(typedef, options) {
492
+ if (!options.allowNull) {
493
+ return false;
494
+ }
495
+ const { type } = typedef;
496
+ if (type === 'String' || type === 'ObjectId') {
497
+ return typedef.required;
498
+ } else {
499
+ return false;
500
+ }
465
501
  }
466
502
 
467
- function allowEmpty(typedef, options) {
468
- return options.allowEmpty && typedef.type === 'String';
503
+ function appendEmpty(typedef, options) {
504
+ if (!options.allowNull) {
505
+ return false;
506
+ }
507
+ const { required, type } = typedef;
508
+ return !required && type === 'ObjectId';
469
509
  }
470
510
 
471
511
  function isExcludedField(field, options) {
@@ -18,6 +18,7 @@ export const INCLUDE_FIELD_SCHEMA: {
18
18
  strip(strip: any): any;
19
19
  allow(...set: any[]): any;
20
20
  reject(...set: any[]): any;
21
+ nullable(): any;
21
22
  message(message: any): any;
22
23
  tag(tags: any): any;
23
24
  description(description: any): any;
@@ -33,8 +34,11 @@ export const INCLUDE_FIELD_SCHEMA: {
33
34
  } | {
34
35
  default: any;
35
36
  };
36
- inspect(): string;
37
+ getNullable(): {
38
+ nullable: boolean;
39
+ };
37
40
  expandExtra(extra?: {}): {};
41
+ inspect(): string;
38
42
  assertEnum(set: any, allow: any): any;
39
43
  assert(type: any, fn: any): any;
40
44
  pushAssertion(assertion: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../src/include.js"],"names":[],"mappings":"AA2BA,gDAuEC;AAMD,uDA4BC;AAGD,yDAIC;AAGD,yEAUC;AApID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKG"}
1
+ {"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../src/include.js"],"names":[],"mappings":"AA2BA,gDAuEC;AAMD,uDA4BC;AAGD,yDAIC;AAGD,yEAUC;AApID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKG"}
package/types/search.d.ts CHANGED
@@ -15,6 +15,7 @@ export function searchValidation(options?: {}): {
15
15
  strip(strip: any): any;
16
16
  allow(...set: any[]): any;
17
17
  reject(...set: any[]): any;
18
+ nullable(): any;
18
19
  message(message: any): any;
19
20
  tag(tags: any): any;
20
21
  description(description: any): any;
@@ -30,8 +31,11 @@ export function searchValidation(options?: {}): {
30
31
  } | {
31
32
  default: any;
32
33
  };
33
- inspect(): string;
34
+ getNullable(): {
35
+ nullable: boolean;
36
+ };
34
37
  expandExtra(extra?: {}): {};
38
+ inspect(): string;
35
39
  assertEnum(set: any, allow: any): any;
36
40
  assert(type: any, fn: any): any;
37
41
  pushAssertion(assertion: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAgBA,gEA0DC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBC"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAgBA,gEA0DC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBC"}
package/types/utils.d.ts CHANGED
@@ -3,6 +3,7 @@ export function isMongooseSchema(obj: any): boolean;
3
3
  export function isReferenceField(obj: any, path: any): boolean;
4
4
  export function isDateField(obj: any, path: any): boolean;
5
5
  export function isNumberField(obj: any, path: any): boolean;
6
+ export function isArrayField(obj: any, path: any): boolean;
6
7
  export function isSchemaTypedef(arg: any): boolean;
7
8
  export function getField(obj: any, path: any): any;
8
9
  export function getInnerField(obj: any, path: any): any;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAMA,iDAcC;AAED,oDAEC;AAED,+DAEC;AAED,0DAEC;AAED,4DAEC;AAOD,mDAGC;AAuBD,mDAYC;AAKD,wDAEC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAMA,iDAcC;AAED,oDAEC;AAED,+DAEC;AAED,0DAEC;AAED,4DAEC;AAED,2DAGC;AAOD,mDAGC;AAuBD,mDAYC;AAKD,wDAEC"}
@@ -11,7 +11,6 @@ export function getTupleValidator(types: any): {
11
11
  };
12
12
  export const OBJECT_ID_SCHEMA: {
13
13
  required(): any;
14
- allowEmpty(): any;
15
14
  length(length: number): any;
16
15
  min(length: number): any;
17
16
  max(length: number): any;
@@ -77,6 +76,7 @@ export const OBJECT_ID_SCHEMA: {
77
76
  strip(strip: any): any;
78
77
  allow(...set: any[]): any;
79
78
  reject(...set: any[]): any;
79
+ nullable(): any;
80
80
  message(message: any): any;
81
81
  tag(tags: any): any;
82
82
  description(description: any): any;
@@ -93,8 +93,11 @@ export const OBJECT_ID_SCHEMA: {
93
93
  } | {
94
94
  default: any;
95
95
  };
96
- inspect(): string;
96
+ getNullable(): {
97
+ nullable: boolean;
98
+ };
97
99
  expandExtra(extra?: {}): {};
100
+ inspect(): string;
98
101
  assertEnum(set: any, allow: any): any;
99
102
  assert(type: any, fn: any): any;
100
103
  pushAssertion(assertion: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.js"],"names":[],"mappings":"AAkFA,kDAEC;AAED,oEA+FC;AAsBD,wEA2BC;AA2SD;;;EAEC;AAED;;;EAOC;AA1gBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQK"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.js"],"names":[],"mappings":"AAkFA,kDAEC;AAED,oEAgGC;AAsBD,wEA2BC;AAkVD;;;EAEC;AAED;;;EAOC;AAljBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQK"}