@bedrockio/yada 1.0.7 → 1.0.9

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
@@ -22,6 +22,7 @@ Concepts
22
22
  - [Reject](#reject)
23
23
  - [Custom](#custom)
24
24
  - [Default](#default)
25
+ - [Strip](#strip)
25
26
  - [Validation Options](#validation-options)
26
27
  - [Error Messages](#error-messages)
27
28
  - [Localization](#localization)
@@ -300,11 +301,6 @@ const schema = yd
300
301
  .required();
301
302
  ```
302
303
 
303
- #### Methods:
304
-
305
- - `append` - Appends the passed argument to create a new object schema. Accepts
306
- either an object or another `.object()` schema.
307
-
308
304
  ## Date
309
305
 
310
306
  Dates are similar to the basic types with the exception that in addition to date
@@ -376,6 +372,33 @@ await schema.validate(true); // pass
376
372
  await schema.validate('true'); // error!
377
373
  ```
378
374
 
375
+ ### Append
376
+
377
+ Appends another schema:
378
+
379
+ ```js
380
+ const schema1 = yd.string();
381
+ const schema2 = yd.custom((str) => {
382
+ if (str === 'foo') {
383
+ throw new Error('Cannot be foo!');
384
+ }
385
+ });
386
+ const schema = schema1.append(schema2);
387
+ ```
388
+
389
+ Note that when applied to an object schema the fields will be merged. In this
390
+ case a plain object is also accepted:
391
+
392
+ ```js
393
+ const schema1 = yd.object({
394
+ foo: yd.string(),
395
+ });
396
+ const schema2 = yd.object({
397
+ bar: yd.string(),
398
+ });
399
+ const schema = schema1.append(schema2);
400
+ ```
401
+
379
402
  ### Custom
380
403
 
381
404
  The `custom` schema allows for custom validations expressed in code. A custom
@@ -467,6 +490,24 @@ console.log(await schema.validate()); // "hi!"
467
490
  console.log(await schema.validate('hello!')); // "hello!"
468
491
  ```
469
492
 
493
+ ### Strip
494
+
495
+ The `strip` method serves as a way to conditionally exclude fields when the
496
+ schema is used inside an object schema:
497
+
498
+ ```js
499
+ const schema = yd.object({
500
+ name: yd.string(),
501
+ age: yd.number().strip((val, { self }) => {
502
+ // That's private!
503
+ return !self;
504
+ }),
505
+ });
506
+ ```
507
+
508
+ Arguments are identical to those passed to [custom](#custom). The field will be
509
+ stripped out if the function returns a truthy value.
510
+
470
511
  ## Validation Options
471
512
 
472
513
  Validation options in Yada can be passed at runtime on validation or baked into
@@ -46,6 +46,11 @@ class Schema {
46
46
  return await fn(val, options);
47
47
  });
48
48
  }
49
+ strip(strip) {
50
+ return this.clone({
51
+ strip
52
+ });
53
+ }
49
54
  allow(...set) {
50
55
  return this.assertEnum(set, true);
51
56
  }
@@ -120,6 +125,11 @@ class Schema {
120
125
  };
121
126
  return clone;
122
127
  }
128
+ append(schema) {
129
+ const merged = this.clone(schema.meta);
130
+ merged.assertions = [...this.assertions, ...schema.assertions];
131
+ return merged;
132
+ }
123
133
 
124
134
  // Private
125
135
 
@@ -48,9 +48,22 @@ class ObjectSchema extends _TypeSchema.default {
48
48
  for (let [key, schema] of Object.entries(this.meta.fields)) {
49
49
  this.assert('field', async (obj, options) => {
50
50
  if (obj) {
51
- let val;
51
+ const val = obj[key];
52
+ const {
53
+ strip
54
+ } = schema.meta;
55
+ if (strip && strip(val, options)) {
56
+ delete obj[key];
57
+ return;
58
+ }
52
59
  try {
53
- val = await schema.validate(obj[key], options);
60
+ const result = await schema.validate(val, options);
61
+ if (result !== undefined) {
62
+ return {
63
+ ...obj,
64
+ [key]: result
65
+ };
66
+ }
54
67
  } catch (error) {
55
68
  if (error.details?.length === 1) {
56
69
  const {
@@ -62,18 +75,21 @@ class ObjectSchema extends _TypeSchema.default {
62
75
  throw new _errors.FieldError('Field failed validation.', key, error.original, error.details);
63
76
  }
64
77
  }
65
- return {
66
- ...obj,
67
- ...(val !== undefined && {
68
- [key]: val
69
- })
70
- };
71
78
  }
72
79
  });
73
80
  }
74
81
  }
75
82
  append(arg) {
76
- const schema = (0, _Schema.isSchema)(arg) ? arg : new ObjectSchema(arg);
83
+ let schema;
84
+ if (arg instanceof ObjectSchema) {
85
+ schema = arg;
86
+ } else if ((0, _Schema.isSchema)(arg)) {
87
+ // If the schema is of a different type then
88
+ // simply append it and don't merge fields.
89
+ return super.append(arg);
90
+ } else {
91
+ schema = new ObjectSchema(arg);
92
+ }
77
93
  const fields = {
78
94
  ...this.meta.fields,
79
95
  ...schema.meta.fields
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/yada",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Validation library inspired by Joi.",
5
5
  "scripts": {
6
6
  "test": "jest",
package/src/Schema.js CHANGED
@@ -46,6 +46,10 @@ export default class Schema {
46
46
  });
47
47
  }
48
48
 
49
+ strip(strip) {
50
+ return this.clone({ strip });
51
+ }
52
+
49
53
  allow(...set) {
50
54
  return this.assertEnum(set, true);
51
55
  }
@@ -123,6 +127,12 @@ export default class Schema {
123
127
  return clone;
124
128
  }
125
129
 
130
+ append(schema) {
131
+ const merged = this.clone(schema.meta);
132
+ merged.assertions = [...this.assertions, ...schema.assertions];
133
+ return merged;
134
+ }
135
+
126
136
  // Private
127
137
 
128
138
  assertEnum(set, allow) {
package/src/object.js CHANGED
@@ -38,9 +38,22 @@ class ObjectSchema extends TypeSchema {
38
38
  for (let [key, schema] of Object.entries(this.meta.fields)) {
39
39
  this.assert('field', async (obj, options) => {
40
40
  if (obj) {
41
- let val;
41
+ const val = obj[key];
42
+ const { strip } = schema.meta;
43
+
44
+ if (strip && strip(val, options)) {
45
+ delete obj[key];
46
+ return;
47
+ }
48
+
42
49
  try {
43
- val = await schema.validate(obj[key], options);
50
+ const result = await schema.validate(val, options);
51
+ if (result !== undefined) {
52
+ return {
53
+ ...obj,
54
+ [key]: result,
55
+ };
56
+ }
44
57
  } catch (error) {
45
58
  if (error.details?.length === 1) {
46
59
  const { message, original } = error.details[0];
@@ -54,19 +67,22 @@ class ObjectSchema extends TypeSchema {
54
67
  );
55
68
  }
56
69
  }
57
- return {
58
- ...obj,
59
- ...(val !== undefined && {
60
- [key]: val,
61
- }),
62
- };
63
70
  }
64
71
  });
65
72
  }
66
73
  }
67
74
 
68
75
  append(arg) {
69
- const schema = isSchema(arg) ? arg : new ObjectSchema(arg);
76
+ let schema;
77
+ if (arg instanceof ObjectSchema) {
78
+ schema = arg;
79
+ } else if (isSchema(arg)) {
80
+ // If the schema is of a different type then
81
+ // simply append it and don't merge fields.
82
+ return super.append(arg);
83
+ } else {
84
+ schema = new ObjectSchema(arg);
85
+ }
70
86
 
71
87
  const fields = {
72
88
  ...this.meta.fields,
package/test/all.test.js CHANGED
@@ -693,6 +693,29 @@ describe('custom', () => {
693
693
  });
694
694
  });
695
695
 
696
+ describe('strip', () => {
697
+ it('should conditionally strip out fields', async () => {
698
+ const schema = yd.object({
699
+ name: yd.string(),
700
+ age: yd.number().strip((val, { self }) => {
701
+ return !self;
702
+ }),
703
+ });
704
+ const result = await schema.validate(
705
+ {
706
+ name: 'Brett',
707
+ age: 25,
708
+ },
709
+ {
710
+ isPrivate: true,
711
+ }
712
+ );
713
+ expect(result).toEqual({
714
+ name: 'Brett',
715
+ });
716
+ });
717
+ });
718
+
696
719
  describe('array', () => {
697
720
  it('should validate an optional array', async () => {
698
721
  const schema = yd.array();
@@ -955,6 +978,56 @@ describe('default', () => {
955
978
  });
956
979
  });
957
980
 
981
+ describe('append', () => {
982
+ it('should allow appending to a string schema', async () => {
983
+ const custom = yd.custom((str) => {
984
+ if (str === 'fop') {
985
+ throw new Error('You misspelled foo!');
986
+ }
987
+ });
988
+
989
+ const schema = yd.string().append(custom);
990
+
991
+ await assertPass(schema, 'foo');
992
+ await assertFail(schema, 'fop', ['You misspelled foo!']);
993
+ });
994
+
995
+ it('should append a custom schema to an object schema', async () => {
996
+ const custom = yd.custom((obj) => {
997
+ if (obj.foo === 'fop') {
998
+ throw new Error('You misspelled foo!');
999
+ }
1000
+ });
1001
+
1002
+ const schema = yd
1003
+ .object({
1004
+ foo: yd.string(),
1005
+ })
1006
+ .append(custom);
1007
+
1008
+ await assertPass(schema, { foo: 'foo' });
1009
+ await assertFail(schema, { foo: 'fop' }, ['You misspelled foo!']);
1010
+ });
1011
+
1012
+ it('should preserve defaults', async () => {
1013
+ const custom = yd.custom((str) => {
1014
+ if (str === 'fop') {
1015
+ throw new Error('You misspelled foo!');
1016
+ }
1017
+ });
1018
+ const schema = yd.string().required();
1019
+
1020
+ await assertPass(schema.default('foo').append(custom));
1021
+ await assertPass(schema.append(custom).default('foo'));
1022
+ await assertFail(schema.default('fop').append(custom), undefined, [
1023
+ 'You misspelled foo!',
1024
+ ]);
1025
+ await assertFail(schema.append(custom).default('fop'), undefined, [
1026
+ 'You misspelled foo!',
1027
+ ]);
1028
+ });
1029
+ });
1030
+
958
1031
  describe('serialization', () => {
959
1032
  it('should correctly serialize object error', async () => {
960
1033
  const schema = yd.object({