@bedrockio/model 0.5.4 → 0.7.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/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
@@ -1490,6 +1585,12 @@ provided as `undefined` cannot be represented in JSON which requires using
1490
1585
  either a `null` or empty string, both of which would be stored in the database
1491
1586
  if naively assigned with `Object.assign`.
1492
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.
1593
+
1493
1594
  ### Slugs
1494
1595
 
1495
1596
  A common requirement is to allow slugs on documents to serve as ids for human
@@ -12,6 +12,7 @@ var _serialization = require("./serialization");
12
12
  var _slug = require("./slug");
13
13
  var _search = require("./search");
14
14
  var _assign = require("./assign");
15
+ var _upsert = require("./upsert");
15
16
  var _hydrate = require("./hydrate");
16
17
  var _include = require("./include");
17
18
  var _softDelete = require("./soft-delete");
@@ -57,6 +58,7 @@ function createSchema(definition, options = {}) {
57
58
  (0, _include.applyInclude)(schema);
58
59
  (0, _hydrate.applyHydrate)(schema);
59
60
  (0, _assign.applyAssign)(schema);
61
+ (0, _upsert.applyUpsert)(schema);
60
62
  (0, _slug.applySlug)(schema);
61
63
  return schema;
62
64
  }
@@ -26,6 +26,7 @@ function applySearch(schema, definition) {
26
26
  validateDefinition(definition);
27
27
  validateSearchFields(schema, definition);
28
28
  applySearchCache(schema, definition);
29
+ applySearchSync(schema, definition);
29
30
  const {
30
31
  query: searchQuery,
31
32
  fields: searchFields
@@ -236,6 +237,8 @@ function normalizeQuery(query, schema, root = {}, rootPath = []) {
236
237
  root[path.join('.')] = {
237
238
  $in: value
238
239
  };
240
+ } else if (isEmptyArrayQuery(schema, key, value)) {
241
+ root[path.join('.')] = [];
239
242
  } else {
240
243
  root[path.join('.')] = value;
241
244
  }
@@ -255,12 +258,15 @@ function isNestedQuery(key, value) {
255
258
  function isArrayQuery(key, value) {
256
259
  return !isMongoOperator(key) && !isInclude(key) && Array.isArray(value);
257
260
  }
261
+ function isEmptyArrayQuery(schema, key, value) {
262
+ return !isMongoOperator(key) && (0, _utils.isArrayField)(schema, key) && value === null;
263
+ }
258
264
  function isRangeQuery(schema, key, value) {
259
265
  // Range queries only allowed on Date and Number fields.
260
266
  if (!(0, _utils.isDateField)(schema, key) && !(0, _utils.isNumberField)(schema, key)) {
261
267
  return false;
262
268
  }
263
- return typeof value === 'object';
269
+ return typeof value === 'object' && !!value;
264
270
  }
265
271
  function mapOperatorQuery(obj) {
266
272
  const query = {};
@@ -322,9 +328,7 @@ function applySearchCache(schema, definition) {
322
328
  if (!force) {
323
329
  const $or = Object.keys(cache).map(cachedField => {
324
330
  return {
325
- [cachedField]: {
326
- $exists: false
327
- }
331
+ [cachedField]: null
328
332
  };
329
333
  });
330
334
  query.$or = $or;
@@ -391,6 +395,32 @@ function applyCacheHook(schema, definition) {
391
395
  this.assign(getUpdates(this, paths, definition));
392
396
  });
393
397
  }
398
+
399
+ // Search field syncing
400
+
401
+ function applySearchSync(schema, definition) {
402
+ if (!definition.search?.sync) {
403
+ return;
404
+ }
405
+ schema.post('save', async function postSave() {
406
+ for (let entry of definition.search.sync) {
407
+ const {
408
+ ref,
409
+ path
410
+ } = entry;
411
+ const Model = _mongoose.default.models[ref];
412
+ const docs = await Model.find({
413
+ [path]: this.id
414
+ });
415
+ await Promise.all(docs.map(async doc => {
416
+ await doc.save();
417
+ }));
418
+ }
419
+ });
420
+ }
421
+
422
+ // Utils
423
+
394
424
  function isForeignField(schema, path) {
395
425
  if (!path.includes('.')) {
396
426
  return false;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.applyUpsert = applyUpsert;
7
+ function applyUpsert(schema) {
8
+ // Note: Intentionally avoiding the findOneAndUpdate approach here
9
+ // as this will prevent hooks from being run on the document. This
10
+ // means however that we cannot always return a query here as the
11
+ // operations are inherently different.
12
+
13
+ schema.static('findOrCreate', async function findOrCreate(query, fields) {
14
+ fields ||= query;
15
+ let doc = await this.findOne(query);
16
+ if (!doc) {
17
+ doc = await this.create(fields);
18
+ }
19
+ return doc;
20
+ });
21
+ }
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
@@ -137,6 +137,7 @@ function applyValidation(schema, definition) {
137
137
  } = options;
138
138
  return getSchemaFromMongoose(schema, {
139
139
  model: this,
140
+ allowNull: true,
140
141
  allowSearch: true,
141
142
  skipRequired: true,
142
143
  allowInclude: true,
@@ -284,9 +285,11 @@ function getSchemaForTypedef(typedef, options = {}) {
284
285
  } else {
285
286
  schema = getSchemaForType(type, options);
286
287
 
287
- // Unsetting only allowed for primitive types.
288
+ // Only allowed for primitive types.
288
289
  if (allowUnset(typedef, options)) {
289
- schema = _yada.default.allow(null, '', schema);
290
+ schema = _yada.default.allow(schema, '').nullable();
291
+ } else if (allowNull(typedef, options)) {
292
+ schema = schema.nullable();
290
293
  } else if (allowEmpty(typedef, options)) {
291
294
  schema = schema.options({
292
295
  allowEmpty: true
@@ -402,6 +405,9 @@ function getSearchSchema(schema, type) {
402
405
  function isRequired(typedef, options) {
403
406
  return typedef.required && !typedef.default && !options.skipRequired;
404
407
  }
408
+ function allowNull(typedef, options) {
409
+ return options.allowNull && !typedef.required;
410
+ }
405
411
  function allowUnset(typedef, options) {
406
412
  return options.allowUnset && !typedef.required;
407
413
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/model",
3
- "version": "0.5.4",
3
+ "version": "0.7.0",
4
4
  "description": "Bedrock utilities for model creation.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -30,7 +30,7 @@
30
30
  "lodash": "^4.17.21"
31
31
  },
32
32
  "peerDependencies": {
33
- "@bedrockio/yada": "^1.1.2",
33
+ "@bedrockio/yada": "^1.1.3",
34
34
  "mongoose": "^8.5.1"
35
35
  },
36
36
  "devDependencies": {
@@ -38,7 +38,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.1.3",
42
42
  "@shelf/jest-mongodb": "^4.3.2",
43
43
  "eslint": "^8.33.0",
44
44
  "eslint-plugin-bedrock": "^1.0.26",
package/src/schema.js CHANGED
@@ -7,6 +7,7 @@ import { serializeOptions } from './serialization';
7
7
  import { applySlug } from './slug';
8
8
  import { applySearch } from './search';
9
9
  import { applyAssign } from './assign';
10
+ import { applyUpsert } from './upsert';
10
11
  import { applyHydrate } from './hydrate';
11
12
  import { applyInclude } from './include';
12
13
  import { applySoftDelete } from './soft-delete';
@@ -61,6 +62,7 @@ export function createSchema(definition, options = {}) {
61
62
  applyInclude(schema);
62
63
  applyHydrate(schema);
63
64
  applyAssign(schema);
65
+ applyUpsert(schema);
64
66
  applySlug(schema);
65
67
 
66
68
  return schema;
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';
@@ -18,6 +18,7 @@ export function applySearch(schema, definition) {
18
18
  validateDefinition(definition);
19
19
  validateSearchFields(schema, definition);
20
20
  applySearchCache(schema, definition);
21
+ applySearchSync(schema, definition);
21
22
 
22
23
  const { query: searchQuery, fields: searchFields } = definition.search || {};
23
24
 
@@ -240,6 +241,8 @@ function normalizeQuery(query, schema, root = {}, rootPath = []) {
240
241
  root[path.join('.')] = parseRegexQuery(value);
241
242
  } else if (isArrayQuery(key, value)) {
242
243
  root[path.join('.')] = { $in: value };
244
+ } else if (isEmptyArrayQuery(schema, key, value)) {
245
+ root[path.join('.')] = [];
243
246
  } else {
244
247
  root[path.join('.')] = value;
245
248
  }
@@ -261,12 +264,16 @@ function isArrayQuery(key, value) {
261
264
  return !isMongoOperator(key) && !isInclude(key) && Array.isArray(value);
262
265
  }
263
266
 
267
+ function isEmptyArrayQuery(schema, key, value) {
268
+ return !isMongoOperator(key) && isArrayField(schema, key) && value === null;
269
+ }
270
+
264
271
  function isRangeQuery(schema, key, value) {
265
272
  // Range queries only allowed on Date and Number fields.
266
273
  if (!isDateField(schema, key) && !isNumberField(schema, key)) {
267
274
  return false;
268
275
  }
269
- return typeof value === 'object';
276
+ return typeof value === 'object' && !!value;
270
277
  }
271
278
 
272
279
  function mapOperatorQuery(obj) {
@@ -339,9 +346,7 @@ function applySearchCache(schema, definition) {
339
346
  if (!force) {
340
347
  const $or = Object.keys(cache).map((cachedField) => {
341
348
  return {
342
- [cachedField]: {
343
- $exists: false,
344
- },
349
+ [cachedField]: null,
345
350
  };
346
351
  });
347
352
  query.$or = $or;
@@ -415,6 +420,32 @@ function applyCacheHook(schema, definition) {
415
420
  });
416
421
  }
417
422
 
423
+ // Search field syncing
424
+
425
+ function applySearchSync(schema, definition) {
426
+ if (!definition.search?.sync) {
427
+ return;
428
+ }
429
+
430
+ schema.post('save', async function postSave() {
431
+ for (let entry of definition.search.sync) {
432
+ const { ref, path } = entry;
433
+ const Model = mongoose.models[ref];
434
+ const docs = await Model.find({
435
+ [path]: this.id,
436
+ });
437
+
438
+ await Promise.all(
439
+ docs.map(async (doc) => {
440
+ await doc.save();
441
+ })
442
+ );
443
+ }
444
+ });
445
+ }
446
+
447
+ // Utils
448
+
418
449
  function isForeignField(schema, path) {
419
450
  if (!path.includes('.')) {
420
451
  return false;
package/src/upsert.js ADDED
@@ -0,0 +1,18 @@
1
+ export function applyUpsert(schema) {
2
+ // Note: Intentionally avoiding the findOneAndUpdate approach here
3
+ // as this will prevent hooks from being run on the document. This
4
+ // means however that we cannot always return a query here as the
5
+ // operations are inherently different.
6
+
7
+ schema.static('findOrCreate', async function findOrCreate(query, fields) {
8
+ fields ||= query;
9
+
10
+ let doc = await this.findOne(query);
11
+
12
+ if (!doc) {
13
+ doc = await this.create(fields);
14
+ }
15
+
16
+ return doc;
17
+ });
18
+ }
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
@@ -152,6 +152,7 @@ export function applyValidation(schema, definition) {
152
152
 
153
153
  return getSchemaFromMongoose(schema, {
154
154
  model: this,
155
+ allowNull: true,
155
156
  allowSearch: true,
156
157
  skipRequired: true,
157
158
  allowInclude: true,
@@ -299,9 +300,11 @@ function getSchemaForTypedef(typedef, options = {}) {
299
300
  } else {
300
301
  schema = getSchemaForType(type, options);
301
302
 
302
- // Unsetting only allowed for primitive types.
303
+ // Only allowed for primitive types.
303
304
  if (allowUnset(typedef, options)) {
304
- schema = yd.allow(null, '', schema);
305
+ schema = yd.allow(schema, '').nullable();
306
+ } else if (allowNull(typedef, options)) {
307
+ schema = schema.nullable();
305
308
  } else if (allowEmpty(typedef, options)) {
306
309
  schema = schema.options({
307
310
  allowEmpty: true,
@@ -352,6 +355,7 @@ function getSchemaForTypedef(typedef, options = {}) {
352
355
  if (typedef.writeAccess && options.requireWriteAccess) {
353
356
  schema = validateAccess('write', schema, typedef.writeAccess, options);
354
357
  }
358
+
355
359
  return schema;
356
360
  }
357
361
 
@@ -460,6 +464,10 @@ function isRequired(typedef, options) {
460
464
  return typedef.required && !typedef.default && !options.skipRequired;
461
465
  }
462
466
 
467
+ function allowNull(typedef, options) {
468
+ return options.allowNull && !typedef.required;
469
+ }
470
+
463
471
  function allowUnset(typedef, options) {
464
472
  return options.allowUnset && !typedef.required;
465
473
  }
@@ -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"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.js"],"names":[],"mappings":"AAqBA;;;;;;;GAOG;AACH,yCAJW,MAAM,YACN,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAwChC;AAED,iEAsBC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.js"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,yCAJW,MAAM,YACN,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAyChC;AAED,iEAsBC"}
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,gEAyDC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBC"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAgBA,gEA0DC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBC"}
@@ -0,0 +1,2 @@
1
+ export function applyUpsert(schema: any): void;
2
+ //# sourceMappingURL=upsert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../src/upsert.js"],"names":[],"mappings":"AAAA,+CAiBC"}
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"}
@@ -77,6 +77,7 @@ export const OBJECT_ID_SCHEMA: {
77
77
  strip(strip: any): any;
78
78
  allow(...set: any[]): any;
79
79
  reject(...set: any[]): any;
80
+ nullable(): any;
80
81
  message(message: any): any;
81
82
  tag(tags: any): any;
82
83
  description(description: any): any;
@@ -93,8 +94,11 @@ export const OBJECT_ID_SCHEMA: {
93
94
  } | {
94
95
  default: any;
95
96
  };
96
- inspect(): string;
97
+ getNullable(): {
98
+ nullable: boolean;
99
+ };
97
100
  expandExtra(extra?: {}): {};
101
+ inspect(): string;
98
102
  assertEnum(set: any, allow: any): any;
99
103
  assert(type: any, fn: any): any;
100
104
  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;AAkTD;;;EAEC;AAED;;;EAOC;AAlhBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQK"}