@bedrockio/yada 1.2.9 → 1.4.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/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 1.4.0
2
+
3
+ - Added object schema "require".
4
+ - Added object schema "unwind" to get an inner array schema.
5
+ - Added object schema "export" to allow object spreading.
6
+ - "append" now handles handle deep fields.
7
+
8
+ ## 1.3.0
9
+
10
+ - Added object schema "get".
11
+
1
12
  ## 1.2.9
2
13
 
3
14
  - Fixed field missing on empty strings.
package/README.md CHANGED
@@ -331,7 +331,7 @@ const schema = yd
331
331
  .required();
332
332
  ```
333
333
 
334
- ### Choosing Fields
334
+ ### Selecting Fields
335
335
 
336
336
  Object schemas also have a `pick` and `omit` method that allows custom field
337
337
  selection. These methods accept an array of field names or enumerated arguments:
@@ -350,6 +350,87 @@ schema.pick(['firstName', 'lastName']);
350
350
  schema.omit('firstName', 'lastName');
351
351
  ```
352
352
 
353
+ To get a single field use the `get` method:
354
+
355
+ ```js
356
+ const schema = yd.object({
357
+ profile: yd.object({
358
+ firstName: yd.string().required(),
359
+ lastName: yd.string().required(),
360
+ }),
361
+ });
362
+
363
+ // Gets the inner "profile" schema.
364
+ schema.get('profile');
365
+
366
+ // Gets just the "firstName" schema.
367
+ schema.get('profile.firstName');
368
+
369
+ // May also pass an array of path segments.
370
+ schema.get(['profile', 'firstName']);
371
+ ```
372
+
373
+ To get a nested array field use the `unwind` method:
374
+
375
+ ```js
376
+ const schema = yd.object({
377
+ paymentMethods: yd.array(
378
+ yd.object({
379
+ brand: yd.string().allow('visa', 'mastercard'),
380
+ last4: yd.string().length(4),
381
+ expMonth: yd.number(),
382
+ expYear: yd.number(),
383
+ }),
384
+ ),
385
+ });
386
+
387
+ // Gets the inner "paymentMethods" object schema.
388
+ // Will accept a single paymentMethod.
389
+ schema.unwind('paymentMethods');
390
+ ```
391
+
392
+ Compare `unwind` to `get` which would validate against an array instead of an
393
+ object. Note that `unwind` only makes sense when there is exactly one inner
394
+ schema and will error otherwise.
395
+
396
+ ### Augmenting Fields
397
+
398
+ The `require` method allows augmentation of the schema to enforce specific
399
+ fields:
400
+
401
+ ```js
402
+ const schema = yd.object({
403
+ email: yd.string().email().required(),
404
+ phone: yd.string().phone(),
405
+ dob: yd.date(),
406
+ });
407
+
408
+ // For example a schema for a signup flow that
409
+ // requires more fields for normal users than admins.
410
+ const signupSchema = schema.require('phone', 'dob');
411
+
412
+ // Also accepts an array.
413
+ schema.require(['phone', 'dob']);
414
+ ```
415
+
416
+ Note that there is no way to "unrequire" a field. The idea is to start with the
417
+ minimal schema as a base and lock down more from there.
418
+
419
+ ### Merging Fields
420
+
421
+ The [append](#append) method merges object schemas together, however it
422
+ preserves custom and required assertions on the object itself which is not
423
+ always desired. It also lacks the elegance of the spread operator. For this the
424
+ `export` method is provided which will simply export the schema's fields as an
425
+ object:
426
+
427
+ ```js
428
+ const newSchema = yd.object({
429
+ ...schema1.export(),
430
+ ...schema2.export(),
431
+ });
432
+ ```
433
+
353
434
  ## Date
354
435
 
355
436
  Dates are similar to the basic types with the exception that in addition to date
@@ -448,6 +529,10 @@ const schema2 = yd.object({
448
529
  const schema = schema1.append(schema2);
449
530
  ```
450
531
 
532
+ Note however that this preserves custom and required assertions on the object
533
+ itself. To simply merge fields as an object use the [export](#merging-fields)
534
+ method instead.
535
+
451
536
  ### Custom
452
537
 
453
538
  The `custom` schema allows for custom validations expressed in code. A custom
@@ -727,7 +812,7 @@ The delimiter can be customized with the `delimiter` option:
727
812
  console.info(
728
813
  error.getFullMessage({
729
814
  delimiter: '\n',
730
- })
815
+ }),
731
816
  );
732
817
  // "email" is required
733
818
  // "lastName" is required
@@ -740,7 +825,7 @@ labels:
740
825
  console.info(
741
826
  error.getFullMessage({
742
827
  natural: true,
743
- })
828
+ }),
744
829
  );
745
830
  // "Email" is required
746
831
  // "Last Name" is required
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  exports.isSchema = isSchema;
8
+ var _lodashEs = require("lodash-es");
8
9
  var _errors = require("./errors");
9
- var _utils = require("./utils");
10
10
  const INITIAL_TYPES = ['default', 'required', 'type', 'transform', 'empty'];
11
11
  const REQUIRED_TYPES = ['default', 'required', 'missing'];
12
12
  class Schema {
@@ -151,7 +151,6 @@ class Schema {
151
151
  async validate(value, options = {}) {
152
152
  let details = [];
153
153
  options = {
154
- root: value,
155
154
  ...options,
156
155
  ...this.meta,
157
156
  original: value
@@ -204,7 +203,7 @@ class Schema {
204
203
 
205
204
  /**
206
205
  * Appends another schema. [Link](https://github.com/bedrockio/yada#append)
207
- * @returns {this}
206
+ * @returns {Schema}
208
207
  */
209
208
  append(schema) {
210
209
  const merged = this.clone(schema.meta);
@@ -274,6 +273,12 @@ class Schema {
274
273
  inspect() {
275
274
  return JSON.stringify(this.toOpenApi(), null, 2);
276
275
  }
276
+ get() {
277
+ const {
278
+ name
279
+ } = this.constructor;
280
+ throw new Error(`"get" not implemented by ${name}.`);
281
+ }
277
282
 
278
283
  // Private
279
284
 
@@ -302,7 +307,7 @@ class Schema {
302
307
  // Must not pass cast option down when allowing
303
308
  // other schema types as they may be allowed, for
304
309
  // example allowing a string or array of strings.
305
- options = (0, _utils.omit)(options, 'cast');
310
+ options = (0, _lodashEs.omit)(options, 'cast');
306
311
  return await el.validate(val, options);
307
312
  } catch (err) {
308
313
  const [first] = err.details;
package/dist/cjs/array.js CHANGED
@@ -4,10 +4,10 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = _default;
7
+ var _lodashEs = require("lodash-es");
7
8
  var _Schema = _interopRequireDefault(require("./Schema"));
8
9
  var _TypeSchema = _interopRequireDefault(require("./TypeSchema"));
9
10
  var _errors = require("./errors");
10
- var _utils = require("./utils");
11
11
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
12
  class ArraySchema extends _TypeSchema.default {
13
13
  constructor(schemas) {
@@ -42,7 +42,7 @@ class ArraySchema extends _TypeSchema.default {
42
42
  try {
43
43
  // Allow enum message to take
44
44
  // precedence over generic array message.
45
- options = (0, _utils.omit)(options, 'message');
45
+ options = (0, _lodashEs.omit)(options, 'message');
46
46
  result.push(await schema.validate(el, options));
47
47
  } catch (error) {
48
48
  errors.push(new _errors.ElementError(message, i, error.details));
package/dist/cjs/date.js CHANGED
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = _default;
7
7
  var _validator = _interopRequireDefault(require("validator"));
8
- var _errors = require("./errors");
9
8
  var _Schema = _interopRequireDefault(require("./Schema"));
9
+ var _errors = require("./errors");
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
11
  class DateSchema extends _Schema.default {
12
12
  constructor() {
@@ -32,7 +32,6 @@ class DateSchema extends _Schema.default {
32
32
  return this.clone().assert('min', date => {
33
33
  if (date < min) {
34
34
  throw new _errors.LocalizedError('Must be after {date}.', {
35
- // @ts-ignore
36
35
  date: min.toISOString()
37
36
  });
38
37
  }
@@ -47,7 +46,6 @@ class DateSchema extends _Schema.default {
47
46
  return this.clone().assert('max', date => {
48
47
  if (date > max) {
49
48
  throw new _errors.LocalizedError('Must be before {date}.', {
50
- // @ts-ignore
51
49
  date: max.toISOString()
52
50
  });
53
51
  }
@@ -62,7 +60,6 @@ class DateSchema extends _Schema.default {
62
60
  return this.clone().assert('before', date => {
63
61
  if (date >= max) {
64
62
  throw new _errors.LocalizedError('Must be before {date}.', {
65
- // @ts-ignore
66
63
  date: max.toISOString()
67
64
  });
68
65
  }
@@ -77,7 +74,6 @@ class DateSchema extends _Schema.default {
77
74
  return this.clone().assert('after', date => {
78
75
  if (date <= min) {
79
76
  throw new _errors.LocalizedError('Must be after {date}.', {
80
- // @ts-ignore
81
77
  date: min.toISOString()
82
78
  });
83
79
  }
package/dist/cjs/index.js CHANGED
@@ -40,13 +40,13 @@ Object.defineProperty(exports, "getLocalizedMessages", {
40
40
  Object.defineProperty(exports, "isSchema", {
41
41
  enumerable: true,
42
42
  get: function () {
43
- return _utils.isSchema;
43
+ return _Schema.isSchema;
44
44
  }
45
45
  });
46
46
  Object.defineProperty(exports, "isSchemaError", {
47
47
  enumerable: true,
48
48
  get: function () {
49
- return _utils.isSchemaError;
49
+ return _errors.isSchemaError;
50
50
  }
51
51
  });
52
52
  Object.defineProperty(exports, "number", {
@@ -87,10 +87,11 @@ var _date = _interopRequireDefault(require("./date"));
87
87
  var _object = _interopRequireDefault(require("./object"));
88
88
  var _array = _interopRequireDefault(require("./array"));
89
89
  var _tuple = _interopRequireDefault(require("./tuple"));
90
- var _Schema = _interopRequireDefault(require("./Schema"));
91
- var _utils = require("./utils");
90
+ var _Schema = _interopRequireWildcard(require("./Schema"));
92
91
  var _localization = require("./localization");
93
92
  var _errors = require("./errors");
93
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
94
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
94
95
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
95
96
  /**
96
97
  * Accepts anything.
@@ -132,8 +133,8 @@ var _default = {
132
133
  allow,
133
134
  reject,
134
135
  custom,
135
- isSchema: _utils.isSchema,
136
- isSchemaError: _utils.isSchemaError,
136
+ isSchema: _Schema.isSchema,
137
+ isSchemaError: _errors.isSchemaError,
137
138
  useLocalizer: _localization.useLocalizer,
138
139
  getLocalizedMessages: _localization.getLocalizedMessages,
139
140
  LocalizedError: _errors.LocalizedError
@@ -4,25 +4,22 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = _default;
7
+ var _lodashEs = require("lodash-es");
7
8
  var _TypeSchema = _interopRequireDefault(require("./TypeSchema"));
8
9
  var _errors = require("./errors");
9
- var _utils = require("./utils");
10
10
  var _Schema = _interopRequireWildcard(require("./Schema"));
11
11
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
12
12
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
- const BASE_ASSERTIONS = ['type', 'transform', 'field'];
14
+ const APPEND_ASSERTION_TYPES = ['required', 'type', 'custom'];
15
15
 
16
16
  /**
17
17
  * @typedef {{ [key: string]: Schema } | {}} SchemaMap
18
18
  */
19
19
 
20
20
  class ObjectSchema extends _TypeSchema.default {
21
- constructor(fields, meta) {
22
- super(Object, {
23
- ...meta,
24
- fields
25
- });
21
+ constructor(meta) {
22
+ super(Object, meta);
26
23
  this.setup();
27
24
  }
28
25
  setup() {
@@ -60,14 +57,15 @@ class ObjectSchema extends _TypeSchema.default {
60
57
  return result;
61
58
  }
62
59
  });
63
- for (let [key, schema] of Object.entries(this.getFields())) {
60
+ for (let [key, schema] of Object.entries(this.export())) {
64
61
  if (!(0, _Schema.isSchema)(schema)) {
65
- throw new Error(`Key "${key}" must be a schema`);
62
+ throw new Error(`Key "${key}" must be a schema.`);
66
63
  }
67
64
  this.assert('field', async (obj, options) => {
68
65
  if (obj) {
69
66
  const {
70
- path = []
67
+ path = [],
68
+ original
71
69
  } = options;
72
70
  const {
73
71
  strip,
@@ -86,12 +84,16 @@ class ObjectSchema extends _TypeSchema.default {
86
84
  try {
87
85
  // Do not pass down message into validators
88
86
  // to allow custom messages to take precedence.
89
- options = (0, _utils.omit)(options, 'message');
87
+ options = (0, _lodashEs.omit)(options, 'message');
90
88
  const result = await schema.validate(val, {
91
89
  ...options,
92
- // Re-pass the object as root here as its fields
93
- // may have been transformed by defaults.
94
- root: obj
90
+ // The root object may have been transformed here
91
+ // by defaults or values returned by custom functions
92
+ // so re-pass it here.
93
+ root: obj,
94
+ // The original root represents the root object
95
+ // before it was transformed.
96
+ originalRoot: original
95
97
  });
96
98
  if (result !== undefined) {
97
99
  return {
@@ -109,73 +111,156 @@ class ObjectSchema extends _TypeSchema.default {
109
111
  });
110
112
  }
111
113
  }
112
- getFields() {
113
- return this.meta.fields || {};
114
- }
115
114
 
116
115
  /**
117
- * @param {SchemaMap|Schema} arg Object or schema to append.
116
+ * Gets the schema for the given field. Deep fields accept
117
+ * either a string using dot syntax or an array representing
118
+ * the path.
119
+ *
120
+ * @param {string|Array<string>} [path] The path of the field.
118
121
  */
119
- // @ts-ignore
120
- append(arg) {
121
- let schema;
122
- if (arg instanceof ObjectSchema) {
123
- schema = arg;
124
- } else if (arg instanceof _Schema.default) {
125
- // If the schema is of a different type then
126
- // simply append it and don't merge fields.
127
- return super.append(arg);
122
+ get(path) {
123
+ const {
124
+ fields
125
+ } = this.meta;
126
+ if (!fields) {
127
+ throw new Error('Cannot select field on an open object schema.');
128
+ }
129
+ path = Array.isArray(path) ? path : path.split('.');
130
+ const [name, ...rest] = path;
131
+ const schema = fields[name];
132
+ if (!schema) {
133
+ throw new Error(`Cannot find field "${name}".`);
134
+ }
135
+ if (rest.length) {
136
+ return schema.get(rest);
128
137
  } else {
129
- schema = new ObjectSchema(arg);
138
+ return schema;
130
139
  }
131
- const fields = {
132
- ...this.meta.fields,
133
- ...schema.meta.fields
134
- };
135
- const merged = new ObjectSchema(fields, {
136
- ...this.meta,
137
- ...schema.meta
138
- });
139
- const assertions = [...this.assertions, ...schema.assertions];
140
- for (let assertion of assertions) {
141
- const {
142
- type
143
- } = assertion;
144
- if (!BASE_ASSERTIONS.includes(type)) {
145
- merged.pushAssertion(assertion);
146
- }
140
+ }
141
+
142
+ /**
143
+ * Returns the inner schema of an array field. This only makes
144
+ * sense if the array field holds a single schema, so all other
145
+ * scenarios will throw an error.
146
+ *
147
+ * @param {string|Array<string>} [path] The path of the field.
148
+ */
149
+ unwind(path) {
150
+ path = Array.isArray(path) ? path.join('.') : path;
151
+ const field = this.get(path);
152
+ const {
153
+ schemas
154
+ } = field.meta;
155
+ if (!schemas) {
156
+ throw new Error(`Field "${path}" is not an array schema.`);
157
+ } else if (schemas.length !== 1) {
158
+ throw new Error(`Field "${path}" should contain only one schema.`);
147
159
  }
148
- return merged;
160
+ return schemas[0];
149
161
  }
150
162
 
151
163
  /**
164
+ * Returns a new schema that only validates the selected fields.
165
+ *
152
166
  * @param {...string} [names] Names to include.
153
167
  */
154
168
  pick(...names) {
155
169
  if (Array.isArray(names[0])) {
156
170
  names = names[0];
157
171
  }
158
- const fields = (0, _utils.pick)(this.meta.fields, names);
159
- return new ObjectSchema(fields, {
160
- ...this.meta
172
+ return new ObjectSchema({
173
+ fields: (0, _lodashEs.pick)(this.meta.fields, names)
161
174
  });
162
175
  }
163
176
 
164
177
  /**
178
+ * Returns a new schema that omits fields.
179
+ *
165
180
  * @param {...string} [names] Names to exclude.
166
181
  */
167
182
  omit(...names) {
168
183
  if (Array.isArray(names[0])) {
169
184
  names = names[0];
170
185
  }
171
- const fields = (0, _utils.omit)(this.meta.fields, names);
172
- return new ObjectSchema(fields, {
173
- ...this.meta
186
+ return new ObjectSchema({
187
+ fields: (0, _lodashEs.omit)(this.meta.fields, names)
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Augments the object schema to make fields required.
193
+ * Field names may be passed as an array or arguments.
194
+ * Field names may be deep using dot syntax.
195
+ *
196
+ * @param {...string} fields
197
+ * @param {Array<string>} fields
198
+ */
199
+ require(...fields) {
200
+ if (!fields.length) {
201
+ throw new Error('No fields specified.');
202
+ }
203
+ if (Array.isArray(fields[0])) {
204
+ fields = fields[0];
205
+ }
206
+ const update = {};
207
+ for (let field of fields) {
208
+ (0, _lodashEs.set)(update, field, this.get(field).required());
209
+ }
210
+ return this.append(update);
211
+ }
212
+
213
+ /**
214
+ * Returns the schema's fields as an object allowing them
215
+ * to be "spread" to create new schemas. Note that doing
216
+ * this will mean that custom and required assertions will
217
+ * not be preserved. Compare to {@link append} which
218
+ * preserves all assertions on the base schema.
219
+ */
220
+ export() {
221
+ return this.meta.fields || {};
222
+ }
223
+
224
+ /**
225
+ * Appends another schema and returns a new one. Appended
226
+ * schemas may have nested fields. Note that custom and
227
+ * required assertions on the schemas are preserved. This
228
+ * means that if either object schema is required then the
229
+ * resulting merged schema will also be required. Compare
230
+ * to {@link export} which exports the fields as an object.
231
+ *
232
+ * @param {SchemaMap|Schema} arg Object or schema to append.
233
+ */
234
+ append(arg) {
235
+ let meta;
236
+ let assertions = [...this.assertions];
237
+ if (arg instanceof _Schema.default) {
238
+ meta = arg.meta;
239
+ assertions = [...assertions, ...arg.assertions];
240
+ } else {
241
+ meta = {
242
+ fields: arg
243
+ };
244
+ }
245
+ const fields = mergeFields(this.meta.fields, meta?.fields);
246
+ const schema = new ObjectSchema({
247
+ ...this.meta,
248
+ ...meta,
249
+ fields
174
250
  });
251
+ for (let assertion of assertions) {
252
+ const {
253
+ type
254
+ } = assertion;
255
+ if (APPEND_ASSERTION_TYPES.includes(type)) {
256
+ schema.pushAssertion(assertion);
257
+ }
258
+ }
259
+ return schema;
175
260
  }
176
261
  toOpenApi(extra) {
177
262
  const properties = {};
178
- for (let [key, schema] of Object.entries(this.getFields())) {
263
+ for (let [key, schema] of Object.entries(this.export())) {
179
264
  properties[key] = schema.toOpenApi(extra);
180
265
  }
181
266
  return {
@@ -207,6 +292,24 @@ function expandDotProperties(obj) {
207
292
  }
208
293
  return result;
209
294
  }
295
+ function mergeFields(aFields, bFields) {
296
+ if (!aFields || !bFields) {
297
+ return aFields || bFields;
298
+ }
299
+ const result = {
300
+ ...aFields
301
+ };
302
+ for (let key of Object.keys(bFields)) {
303
+ const aSchema = aFields[key];
304
+ const bSchema = bFields[key];
305
+ if (aSchema instanceof ObjectSchema) {
306
+ result[key] = aSchema.append(bSchema);
307
+ } else {
308
+ result[key] = bSchema;
309
+ }
310
+ }
311
+ return result;
312
+ }
210
313
 
211
314
  /**
212
315
  * Creates an [object schema](https://github.com/bedrockio/yada#object).
@@ -216,5 +319,7 @@ function expandDotProperties(obj) {
216
319
  * empty object is passed then no keys will be allowed.
217
320
  */
218
321
  function _default(map) {
219
- return new ObjectSchema(map);
322
+ return new ObjectSchema({
323
+ fields: map
324
+ });
220
325
  }
@@ -0,0 +1,3 @@
1
+ import { jest, recommended, nodeImports } from '@bedrockio/eslint-plugin';
2
+
3
+ export default [jest, recommended, nodeImports];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/yada",
3
- "version": "1.2.9",
3
+ "version": "1.4.0",
4
4
  "description": "Validation library inspired by Joi.",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -17,19 +17,19 @@
17
17
  "author": "Andrew Plummer <plummer.andrew@gmail.com>",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
+ "lodash-es": "^4.17.21",
20
21
  "validator": "^13.9.0"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@babel/cli": "^7.20.7",
24
25
  "@babel/core": "^7.20.12",
25
26
  "@babel/preset-env": "^7.20.2",
27
+ "@bedrockio/eslint-plugin": "^1.1.7",
26
28
  "@bedrockio/prettier-config": "^1.0.2",
27
29
  "babel-plugin-add-module-exports": "^1.0.4",
28
- "eslint": "^8.26.0",
29
- "eslint-plugin-bedrock": "^1.0.24",
30
+ "eslint": "^9.19.0",
30
31
  "jest": "^29.6.2",
31
- "prettier": "^2.7.1",
32
- "prettier-eslint": "^15.0.1",
32
+ "prettier": "^3.4.2",
33
33
  "typescript": "^5.7.2"
34
34
  },
35
35
  "prettier": "@bedrockio/prettier-config",
package/src/Schema.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { omit } from 'lodash-es';
2
+
1
3
  import {
2
4
  TypeError,
3
5
  FormatError,
@@ -6,7 +8,6 @@ import {
6
8
  LocalizedError,
7
9
  ValidationError,
8
10
  } from './errors';
9
- import { omit } from './utils';
10
11
 
11
12
  const INITIAL_TYPES = ['default', 'required', 'type', 'transform', 'empty'];
12
13
  const REQUIRED_TYPES = ['default', 'required', 'missing'];
@@ -143,7 +144,6 @@ export default class Schema {
143
144
  let details = [];
144
145
 
145
146
  options = {
146
- root: value,
147
147
  ...options,
148
148
  ...this.meta,
149
149
  original: value,
@@ -194,7 +194,7 @@ export default class Schema {
194
194
 
195
195
  /**
196
196
  * Appends another schema. [Link](https://github.com/bedrockio/yada#append)
197
- * @returns {this}
197
+ * @returns {Schema}
198
198
  */
199
199
  append(schema) {
200
200
  const merged = this.clone(schema.meta);
@@ -257,6 +257,11 @@ export default class Schema {
257
257
  return JSON.stringify(this.toOpenApi(), null, 2);
258
258
  }
259
259
 
260
+ get() {
261
+ const { name } = this.constructor;
262
+ throw new Error(`"get" not implemented by ${name}.`);
263
+ }
264
+
260
265
  // Private
261
266
 
262
267
  /**
package/src/array.js CHANGED
@@ -1,7 +1,8 @@
1
+ import { omit } from 'lodash-es';
2
+
1
3
  import Schema from './Schema';
2
4
  import TypeSchema from './TypeSchema';
3
5
  import { ArrayError, ElementError, LocalizedError } from './errors';
4
- import { omit } from './utils';
5
6
 
6
7
  class ArraySchema extends TypeSchema {
7
8
  constructor(schemas) {
@@ -82,7 +83,7 @@ class ArraySchema extends TypeSchema {
82
83
  {
83
84
  length,
84
85
  s,
85
- }
86
+ },
86
87
  );
87
88
  }
88
89
  });
package/src/date.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import validator from 'validator';
2
- import { LocalizedError } from './errors';
3
2
 
4
3
  import Schema from './Schema';
4
+ import { LocalizedError } from './errors';
5
5
 
6
6
  class DateSchema extends Schema {
7
7
  constructor() {
@@ -24,7 +24,6 @@ class DateSchema extends Schema {
24
24
  return this.clone().assert('min', (date) => {
25
25
  if (date < min) {
26
26
  throw new LocalizedError('Must be after {date}.', {
27
- // @ts-ignore
28
27
  date: min.toISOString(),
29
28
  });
30
29
  }
@@ -39,7 +38,6 @@ class DateSchema extends Schema {
39
38
  return this.clone().assert('max', (date) => {
40
39
  if (date > max) {
41
40
  throw new LocalizedError('Must be before {date}.', {
42
- // @ts-ignore
43
41
  date: max.toISOString(),
44
42
  });
45
43
  }
@@ -54,7 +52,6 @@ class DateSchema extends Schema {
54
52
  return this.clone().assert('before', (date) => {
55
53
  if (date >= max) {
56
54
  throw new LocalizedError('Must be before {date}.', {
57
- // @ts-ignore
58
55
  date: max.toISOString(),
59
56
  });
60
57
  }
@@ -69,7 +66,6 @@ class DateSchema extends Schema {
69
66
  return this.clone().assert('after', (date) => {
70
67
  if (date <= min) {
71
68
  throw new LocalizedError('Must be after {date}.', {
72
- // @ts-ignore
73
69
  date: min.toISOString(),
74
70
  });
75
71
  }
@@ -118,7 +114,7 @@ class DateSchema extends Schema {
118
114
  if (typeof original !== 'number' && !options.default) {
119
115
  throw new LocalizedError('Must be a timestamp in milliseconds.');
120
116
  }
121
- }
117
+ },
122
118
  );
123
119
  }
124
120
 
@@ -134,7 +130,7 @@ class DateSchema extends Schema {
134
130
  } else {
135
131
  return new Date(original * 1000);
136
132
  }
137
- }
133
+ },
138
134
  );
139
135
  }
140
136
 
package/src/index.js CHANGED
@@ -7,10 +7,12 @@ import array from './array';
7
7
  import tuple from './tuple';
8
8
 
9
9
  import Schema from './Schema';
10
- import { isSchema, isSchemaError } from './utils';
11
10
  import { useLocalizer, getLocalizedMessages } from './localization';
12
11
  import { LocalizedError } from './errors';
13
12
 
13
+ import { isSchema } from './Schema';
14
+ import { isSchemaError } from './errors';
15
+
14
16
  /**
15
17
  * Accepts anything.
16
18
  */
package/src/object.js CHANGED
@@ -1,17 +1,18 @@
1
+ import { set, pick, omit } from 'lodash-es';
2
+
1
3
  import TypeSchema from './TypeSchema';
2
4
  import { FieldError, LocalizedError } from './errors';
3
- import { pick, omit } from './utils';
4
5
  import Schema, { isSchema } from './Schema';
5
6
 
6
- const BASE_ASSERTIONS = ['type', 'transform', 'field'];
7
+ const APPEND_ASSERTION_TYPES = ['required', 'type', 'custom'];
7
8
 
8
9
  /**
9
10
  * @typedef {{ [key: string]: Schema } | {}} SchemaMap
10
11
  */
11
12
 
12
13
  class ObjectSchema extends TypeSchema {
13
- constructor(fields, meta) {
14
- super(Object, { ...meta, fields });
14
+ constructor(meta) {
15
+ super(Object, meta);
15
16
  this.setup();
16
17
  }
17
18
 
@@ -46,13 +47,13 @@ class ObjectSchema extends TypeSchema {
46
47
  return result;
47
48
  }
48
49
  });
49
- for (let [key, schema] of Object.entries(this.getFields())) {
50
+ for (let [key, schema] of Object.entries(this.export())) {
50
51
  if (!isSchema(schema)) {
51
- throw new Error(`Key "${key}" must be a schema`);
52
+ throw new Error(`Key "${key}" must be a schema.`);
52
53
  }
53
54
  this.assert('field', async (obj, options) => {
54
55
  if (obj) {
55
- const { path = [] } = options;
56
+ const { path = [], original } = options;
56
57
  const { strip, required } = schema.meta;
57
58
  const val = obj[key];
58
59
 
@@ -72,9 +73,13 @@ class ObjectSchema extends TypeSchema {
72
73
  options = omit(options, 'message');
73
74
  const result = await schema.validate(val, {
74
75
  ...options,
75
- // Re-pass the object as root here as its fields
76
- // may have been transformed by defaults.
76
+ // The root object may have been transformed here
77
+ // by defaults or values returned by custom functions
78
+ // so re-pass it here.
77
79
  root: obj,
80
+ // The original root represents the root object
81
+ // before it was transformed.
82
+ originalRoot: original,
78
83
  });
79
84
  if (result !== undefined) {
80
85
  return {
@@ -91,78 +96,162 @@ class ObjectSchema extends TypeSchema {
91
96
  }
92
97
  }
93
98
 
94
- getFields() {
95
- return this.meta.fields || {};
96
- }
97
-
98
99
  /**
99
- * @param {SchemaMap|Schema} arg Object or schema to append.
100
+ * Gets the schema for the given field. Deep fields accept
101
+ * either a string using dot syntax or an array representing
102
+ * the path.
103
+ *
104
+ * @param {string|Array<string>} [path] The path of the field.
100
105
  */
101
- // @ts-ignore
102
- append(arg) {
103
- let schema;
104
- if (arg instanceof ObjectSchema) {
105
- schema = arg;
106
- } else if (arg instanceof Schema) {
107
- // If the schema is of a different type then
108
- // simply append it and don't merge fields.
109
- return super.append(arg);
110
- } else {
111
- schema = new ObjectSchema(arg);
106
+ get(path) {
107
+ const { fields } = this.meta;
108
+ if (!fields) {
109
+ throw new Error('Cannot select field on an open object schema.');
112
110
  }
113
111
 
114
- const fields = {
115
- ...this.meta.fields,
116
- ...schema.meta.fields,
117
- };
112
+ path = Array.isArray(path) ? path : path.split('.');
118
113
 
119
- const merged = new ObjectSchema(fields, {
120
- ...this.meta,
121
- ...schema.meta,
122
- });
114
+ const [name, ...rest] = path;
115
+ const schema = fields[name];
123
116
 
124
- const assertions = [...this.assertions, ...schema.assertions];
125
- for (let assertion of assertions) {
126
- const { type } = assertion;
127
- if (!BASE_ASSERTIONS.includes(type)) {
128
- merged.pushAssertion(assertion);
129
- }
117
+ if (!schema) {
118
+ throw new Error(`Cannot find field "${name}".`);
130
119
  }
131
120
 
132
- return merged;
121
+ if (rest.length) {
122
+ return schema.get(rest);
123
+ } else {
124
+ return schema;
125
+ }
133
126
  }
134
127
 
135
128
  /**
129
+ * Returns the inner schema of an array field. This only makes
130
+ * sense if the array field holds a single schema, so all other
131
+ * scenarios will throw an error.
132
+ *
133
+ * @param {string|Array<string>} [path] The path of the field.
134
+ */
135
+ unwind(path) {
136
+ path = Array.isArray(path) ? path.join('.') : path;
137
+ const field = this.get(path);
138
+ const { schemas } = field.meta;
139
+ if (!schemas) {
140
+ throw new Error(`Field "${path}" is not an array schema.`);
141
+ } else if (schemas.length !== 1) {
142
+ throw new Error(`Field "${path}" should contain only one schema.`);
143
+ }
144
+ return schemas[0];
145
+ }
146
+
147
+ /**
148
+ * Returns a new schema that only validates the selected fields.
149
+ *
136
150
  * @param {...string} [names] Names to include.
137
151
  */
138
152
  pick(...names) {
139
153
  if (Array.isArray(names[0])) {
140
154
  names = names[0];
141
155
  }
142
- const fields = pick(this.meta.fields, names);
143
-
144
- return new ObjectSchema(fields, {
145
- ...this.meta,
156
+ return new ObjectSchema({
157
+ fields: pick(this.meta.fields, names),
146
158
  });
147
159
  }
148
160
 
149
161
  /**
162
+ * Returns a new schema that omits fields.
163
+ *
150
164
  * @param {...string} [names] Names to exclude.
151
165
  */
152
166
  omit(...names) {
153
167
  if (Array.isArray(names[0])) {
154
168
  names = names[0];
155
169
  }
156
- const fields = omit(this.meta.fields, names);
170
+ return new ObjectSchema({
171
+ fields: omit(this.meta.fields, names),
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Augments the object schema to make fields required.
177
+ * Field names may be passed as an array or arguments.
178
+ * Field names may be deep using dot syntax.
179
+ *
180
+ * @param {...string} fields
181
+ * @param {Array<string>} fields
182
+ */
183
+ require(...fields) {
184
+ if (!fields.length) {
185
+ throw new Error('No fields specified.');
186
+ }
187
+
188
+ if (Array.isArray(fields[0])) {
189
+ fields = fields[0];
190
+ }
191
+
192
+ const update = {};
193
+ for (let field of fields) {
194
+ set(update, field, this.get(field).required());
195
+ }
196
+
197
+ return this.append(update);
198
+ }
199
+
200
+ /**
201
+ * Returns the schema's fields as an object allowing them
202
+ * to be "spread" to create new schemas. Note that doing
203
+ * this will mean that custom and required assertions will
204
+ * not be preserved. Compare to {@link append} which
205
+ * preserves all assertions on the base schema.
206
+ */
207
+ export() {
208
+ return this.meta.fields || {};
209
+ }
210
+
211
+ /**
212
+ * Appends another schema and returns a new one. Appended
213
+ * schemas may have nested fields. Note that custom and
214
+ * required assertions on the schemas are preserved. This
215
+ * means that if either object schema is required then the
216
+ * resulting merged schema will also be required. Compare
217
+ * to {@link export} which exports the fields as an object.
218
+ *
219
+ * @param {SchemaMap|Schema} arg Object or schema to append.
220
+ */
221
+ append(arg) {
222
+ let meta;
223
+ let assertions = [...this.assertions];
224
+
225
+ if (arg instanceof Schema) {
226
+ meta = arg.meta;
227
+ assertions = [...assertions, ...arg.assertions];
228
+ } else {
229
+ meta = {
230
+ fields: arg,
231
+ };
232
+ }
157
233
 
158
- return new ObjectSchema(fields, {
234
+ const fields = mergeFields(this.meta.fields, meta?.fields);
235
+
236
+ const schema = new ObjectSchema({
159
237
  ...this.meta,
238
+ ...meta,
239
+ fields,
160
240
  });
241
+
242
+ for (let assertion of assertions) {
243
+ const { type } = assertion;
244
+ if (APPEND_ASSERTION_TYPES.includes(type)) {
245
+ schema.pushAssertion(assertion);
246
+ }
247
+ }
248
+
249
+ return schema;
161
250
  }
162
251
 
163
252
  toOpenApi(extra) {
164
253
  const properties = {};
165
- for (let [key, schema] of Object.entries(this.getFields())) {
254
+ for (let [key, schema] of Object.entries(this.export())) {
166
255
  properties[key] = schema.toOpenApi(extra);
167
256
  }
168
257
  return {
@@ -196,6 +285,24 @@ function expandDotProperties(obj) {
196
285
  return result;
197
286
  }
198
287
 
288
+ function mergeFields(aFields, bFields) {
289
+ if (!aFields || !bFields) {
290
+ return aFields || bFields;
291
+ }
292
+ const result = { ...aFields };
293
+ for (let key of Object.keys(bFields)) {
294
+ const aSchema = aFields[key];
295
+ const bSchema = bFields[key];
296
+
297
+ if (aSchema instanceof ObjectSchema) {
298
+ result[key] = aSchema.append(bSchema);
299
+ } else {
300
+ result[key] = bSchema;
301
+ }
302
+ }
303
+ return result;
304
+ }
305
+
199
306
  /**
200
307
  * Creates an [object schema](https://github.com/bedrockio/yada#object).
201
308
  *
@@ -204,5 +311,7 @@ function expandDotProperties(obj) {
204
311
  * empty object is passed then no keys will be allowed.
205
312
  */
206
313
  export default function (map) {
207
- return new ObjectSchema(map);
314
+ return new ObjectSchema({
315
+ fields: map,
316
+ });
208
317
  }
package/src/password.js CHANGED
@@ -38,22 +38,22 @@ export function validateLength(expected) {
38
38
 
39
39
  export const validateLowercase = validateRegex(
40
40
  LOWER_REG,
41
- 'Must contain at least {length} lowercase character{s}.'
41
+ 'Must contain at least {length} lowercase character{s}.',
42
42
  );
43
43
 
44
44
  export const validateUppercase = validateRegex(
45
45
  UPPER_REG,
46
- 'Must contain at least {length} uppercase character{s}.'
46
+ 'Must contain at least {length} uppercase character{s}.',
47
47
  );
48
48
 
49
49
  export const validateNumbers = validateRegex(
50
50
  NUMBER_REG,
51
- 'Must contain at least {length} number{s}.'
51
+ 'Must contain at least {length} number{s}.',
52
52
  );
53
53
 
54
54
  export const validateSymbols = validateRegex(
55
55
  SYMBOL_REG,
56
- 'Must contain at least {length} symbol{s}.'
56
+ 'Must contain at least {length} symbol{s}.',
57
57
  );
58
58
 
59
59
  function validateRegex(reg, message) {
package/src/string.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import validator from 'validator';
2
+
2
3
  import TypeSchema from './TypeSchema';
3
4
  import { LocalizedError } from './errors';
4
5
  import {
@@ -412,7 +413,7 @@ class StringSchema extends TypeSchema {
412
413
  return;
413
414
  }
414
415
  fn(val, options);
415
- }
416
+ },
416
417
  );
417
418
  }
418
419
 
package/types/Schema.d.ts CHANGED
@@ -68,9 +68,9 @@ export default class Schema {
68
68
  clone(meta: any): this;
69
69
  /**
70
70
  * Appends another schema. [Link](https://github.com/bedrockio/yada#append)
71
- * @returns {this}
71
+ * @returns {Schema}
72
72
  */
73
- append(schema: any): this;
73
+ append(schema: any): Schema;
74
74
  toOpenApi(extra: any): any;
75
75
  getAnyType(): {
76
76
  type: string[];
@@ -85,6 +85,7 @@ export default class Schema {
85
85
  };
86
86
  expandExtra(extra?: {}): {};
87
87
  inspect(): string;
88
+ get(): void;
88
89
  /**
89
90
  * @returns {this}
90
91
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../src/Schema.js"],"names":[],"mappings":"AA+ZA,kDAEC;AApZD;IACE,uBAGC;IAFC,kBAAoB;IACpB,SAAgB;IAKlB;;OAEG;IACH,YAFa,IAAI,CAQhB;IAED;;;OAGG;IACH,mBAFa,IAAI,CAShB;IAED;;;;OAIG;IACH,sBAFa,IAAI,CAShB;IAED;;;;OAIG;IACH,uBAFa,IAAI,CAShB;IAED;;;;OAIG;IACH,mBAFa,IAAI,CAIhB;IAED;;;OAGG;IACH,sBAFa,IAAI,CAIhB;IAED;;;OAGG;IACH,uBAFa,IAAI,CAIhB;IAED;;;OAGG;IACH,YAFa,IAAI,CAIhB;IAED;;OAEG;IACH,uBAFa,IAAI,CAIhB;IAED;;OAEG;IACH,gBAFa,IAAI,CAShB;IAED;;OAEG;IACH,+BAFa,IAAI,CAMhB;IAED;;OAEG;IACH,uBAFa,IAAI,CAIhB;IAED,iDAyCC;IAED;;OAEG;IACH,kBAFa,IAAI,CAOhB;IAED;;;OAGG;IACH,qBAFa,IAAI,CAMhB;IAED,2BAYC;IAED;;MAOC;IAED;;;;MASC;IAED;;MAOC;IAED,4BAMC;IAED,kBAEC;IAID;;OAEG;IACH,kCAFa,IAAI,CAiDhB;IAED;;OAEG;IACH,4BAFa,IAAI,CAUhB;IAED,oCAKC;IAED,gEAQC;IAED;;OAEG;IACH,oBAFa,IAAI,CAShB;IAED,gCAGC;IAED,qEAYC;IAED,qBAsCC;CACF"}
1
+ {"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../src/Schema.js"],"names":[],"mappings":"AAoaA,kDAEC;AAxZD;IACE,uBAGC;IAFC,kBAAoB;IACpB,SAAgB;IAKlB;;OAEG;IACH,YAFa,IAAI,CAQhB;IAED;;;OAGG;IACH,mBAFa,IAAI,CAShB;IAED;;;;OAIG;IACH,sBAFa,IAAI,CAShB;IAED;;;;OAIG;IACH,uBAFa,IAAI,CAShB;IAED;;;;OAIG;IACH,mBAFa,IAAI,CAIhB;IAED;;;OAGG;IACH,sBAFa,IAAI,CAIhB;IAED;;;OAGG;IACH,uBAFa,IAAI,CAIhB;IAED;;;OAGG;IACH,YAFa,IAAI,CAIhB;IAED;;OAEG;IACH,uBAFa,IAAI,CAIhB;IAED;;OAEG;IACH,gBAFa,IAAI,CAShB;IAED;;OAEG;IACH,+BAFa,IAAI,CAMhB;IAED;;OAEG;IACH,uBAFa,IAAI,CAIhB;IAED,iDAwCC;IAED;;OAEG;IACH,kBAFa,IAAI,CAOhB;IAED;;;OAGG;IACH,qBAFa,MAAM,CAMlB;IAED,2BAYC;IAED;;MAOC;IAED;;;;MASC;IAED;;MAOC;IAED,4BAMC;IAED,kBAEC;IAED,YAGC;IAID;;OAEG;IACH,kCAFa,IAAI,CAiDhB;IAED;;OAEG;IACH,4BAFa,IAAI,CAUhB;IAED,oCAKC;IAED,gEAQC;IAED;;OAEG;IACH,oBAFa,IAAI,CAShB;IAED,gCAGC;IAED,qEAYC;IAED,qBAsCC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../src/array.js"],"names":[],"mappings":"AAoIA;;;;;;;GAOG;AACH,8CALc,MAAM,EAAA,eAUnB;mBAjJkB,UAAU;AAK7B;IACE,0BAGC;IAED,cAsCC;IAED,0BAUC;IAED,uBAUC;IAED,uBAaC;IAED,eAaC;IAED,mBAEC;CAuBF;uBAjIsB,cAAc"}
1
+ {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../src/array.js"],"names":[],"mappings":"AAqIA;;;;;;;GAOG;AACH,8CALc,MAAM,EAAA,eAUnB;mBAhJkB,UAAU;AAI7B;IACE,0BAGC;IAED,cAsCC;IAED,0BAUC;IAED,uBAUC;IAED,uBAaC;IAED,eAaC;IAED,mBAEC;CAuBF;uBAhIsB,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../src/date.js"],"names":[],"mappings":"AAoKA;;GAEG;AACH,+CAEC;AApKD;IACE,cAUC;IAED;;OAEG;IACH,SAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAY5B;IAED;;OAEG;IACH,SAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAY5B;IAED;;OAEG;IACH,YAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAY5B;IAED;;OAEG;IACH,WAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAY5B;IAED,aAOC;IAED,eAOC;IAED;;OAEG;IACH,aAFW,MAAM,GAAG,WAAW,QAa9B;IAED,kBAUC;IAED,aAcC;CAwBF;mBA/JkB,UAAU"}
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../src/date.js"],"names":[],"mappings":"AAgKA;;GAEG;AACH,+CAEC;AAhKD;IACE,cAUC;IAED;;OAEG;IACH,SAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAW5B;IAED;;OAEG;IACH,SAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAW5B;IAED;;OAEG;IACH,YAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAW5B;IAED;;OAEG;IACH,WAFW,MAAM,GAAC,MAAM,GAAC,IAAI,QAW5B;IAED,aAOC;IAED,eAOC;IAED;;OAEG;IACH,aAFW,MAAM,GAAG,WAAW,QAa9B;IAED,kBAUC;IAED,aAcC;CAwBF;mBA5JkB,UAAU"}
package/types/index.d.ts CHANGED
@@ -41,8 +41,8 @@ export function reject(...args: any[]): Schema;
41
41
  * @param {Function} fn
42
42
  */
43
43
  export function custom(fn: Function): Schema;
44
- import { isSchema } from './utils';
45
- import { isSchemaError } from './utils';
44
+ import { isSchema } from './Schema';
45
+ import { isSchemaError } from './errors';
46
46
  import { useLocalizer } from './localization';
47
47
  import { getLocalizedMessages } from './localization';
48
48
  import { LocalizedError } from './errors';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;kBAKkB,SAAS;oBAHP,WAAW;iBACd,QAAQ;mBAFN,UAAU;mBAGV,UAAU;mBAJV,UAAU;kBAMX,SAAS;AAO3B;;GAEG;AACH,8BAEC;AAED;;GAEG;AACH,8CAEC;AAED;;GAEG;AACH,+CAEC;AAED;;;GAGG;AACH,6CAEC;yBA/BuC,SAAS;8BAAT,SAAS;6BACE,gBAAgB;qCAAhB,gBAAgB;+BACpC,UAAU;mBAHtB,UAAU"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;kBAKkB,SAAS;oBAHP,WAAW;iBACd,QAAQ;mBAFN,UAAU;mBAGV,UAAU;mBAJV,UAAU;kBAMX,SAAS;AAS3B;;GAEG;AACH,8BAEC;AAED;;GAEG;AACH,8CAEC;AAED;;GAEG;AACH,+CAEC;AAED;;;GAGG;AACH,6CAEC;yBA9BwB,UAAU;8BACL,UAAU;6BAJW,gBAAgB;qCAAhB,gBAAgB;+BACpC,UAAU;mBAFtB,UAAU"}
package/types/object.d.ts CHANGED
@@ -13,20 +13,64 @@ export type SchemaMap = {
13
13
  * @typedef {{ [key: string]: Schema } | {}} SchemaMap
14
14
  */
15
15
  declare class ObjectSchema extends TypeSchema {
16
+ constructor(meta: any);
16
17
  setup(): void;
17
- getFields(): any;
18
18
  /**
19
- * @param {SchemaMap|Schema} arg Object or schema to append.
19
+ * Gets the schema for the given field. Deep fields accept
20
+ * either a string using dot syntax or an array representing
21
+ * the path.
22
+ *
23
+ * @param {string|Array<string>} [path] The path of the field.
20
24
  */
21
- append(arg: SchemaMap | Schema): ObjectSchema;
25
+ get(path?: string | Array<string>): any;
26
+ /**
27
+ * Returns the inner schema of an array field. This only makes
28
+ * sense if the array field holds a single schema, so all other
29
+ * scenarios will throw an error.
30
+ *
31
+ * @param {string|Array<string>} [path] The path of the field.
32
+ */
33
+ unwind(path?: string | Array<string>): any;
22
34
  /**
35
+ * Returns a new schema that only validates the selected fields.
36
+ *
23
37
  * @param {...string} [names] Names to include.
24
38
  */
25
39
  pick(...names?: string[]): ObjectSchema;
26
40
  /**
41
+ * Returns a new schema that omits fields.
42
+ *
27
43
  * @param {...string} [names] Names to exclude.
28
44
  */
29
45
  omit(...names?: string[]): ObjectSchema;
46
+ /**
47
+ * Augments the object schema to make fields required.
48
+ * Field names may be passed as an array or arguments.
49
+ * Field names may be deep using dot syntax.
50
+ *
51
+ * @param {...string} fields
52
+ * @param {Array<string>} fields
53
+ */
54
+ require(...fields: string[]): ObjectSchema;
55
+ /**
56
+ * Returns the schema's fields as an object allowing them
57
+ * to be "spread" to create new schemas. Note that doing
58
+ * this will mean that custom and required assertions will
59
+ * not be preserved. Compare to {@link append} which
60
+ * preserves all assertions on the base schema.
61
+ */
62
+ export(): any;
63
+ /**
64
+ * Appends another schema and returns a new one. Appended
65
+ * schemas may have nested fields. Note that custom and
66
+ * required assertions on the schemas are preserved. This
67
+ * means that if either object schema is required then the
68
+ * resulting merged schema will also be required. Compare
69
+ * to {@link export} which exports the fields as an object.
70
+ *
71
+ * @param {SchemaMap|Schema} arg Object or schema to append.
72
+ */
73
+ append(arg: SchemaMap | Schema): ObjectSchema;
30
74
  }
31
75
  import Schema from './Schema';
32
76
  import TypeSchema from './TypeSchema';
@@ -1 +1 @@
1
- {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../src/object.js"],"names":[],"mappings":"AAsMA;;;;;;GAMG;AACH,uCAJW,SAAS,gBAMnB;wBAvMY;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,EAAE;AAD3C;;GAEG;AAEH;IAME,cA0EC;IAED,iBAEC;IAED;;OAEG;IAEH,YAHW,SAAS,GAAC,MAAM,gBAkC1B;IAED;;OAEG;IACH,gBAFc,MAAM,EAAA,gBAWnB;IAED;;OAEG;IACH,gBAFc,MAAM,EAAA,gBAWnB;CAcF;mBA3KgC,UAAU;uBAHpB,cAAc"}
1
+ {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../src/object.js"],"names":[],"mappings":"AAiTA;;;;;;GAMG;AACH,uCAJW,SAAS,gBAQnB;wBAnTY;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,EAAE;AAD3C;;GAEG;AAEH;IACE,uBAGC;IAED,cA8EC;IAED;;;;;;OAMG;IACH,WAFW,MAAM,GAAC,KAAK,CAAC,MAAM,CAAC,OAsB9B;IAED;;;;;;OAMG;IACH,cAFW,MAAM,GAAC,KAAK,CAAC,MAAM,CAAC,OAY9B;IAED;;;;OAIG;IACH,gBAFc,MAAM,EAAA,gBASnB;IAED;;;;OAIG;IACH,gBAFc,MAAM,EAAA,gBASnB;IAED;;;;;;;OAOG;IACH,mBAHc,MAAM,EAAA,gBAkBnB;IAED;;;;;;OAMG;IACH,cAEC;IAED;;;;;;;;;OASG;IACH,YAFW,SAAS,GAAC,MAAM,gBA+B1B;CAcF;mBAnQgC,UAAU;uBAFpB,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../src/string.js"],"names":[],"mappings":"AAgbA;;GAEG;AACH,iDAEC;AA/ZD;IACE,cAoBC;IAED;;OAEG;IACH,YAFa,IAAI,CAQhB;IAED;;OAEG;IACH,eAFW,MAAM,QAUhB;IAED;;OAEG;IACH,YAFW,MAAM,QAUhB;IAED;;OAEG;IACH,YAFW,MAAM,QAUhB;IAED,aAIC;IAED;;OAEG;IACH,mBAFW,OAAO,QAYjB;IAED;;OAEG;IACH,mBAFW,OAAO,QAYjB;IAED;;OAEG;IACH,WAFW,MAAM,QAahB;IAED,cAMC;IAED,uBASC;IAED,YAMC;IAED,YAMC;IAED,aAMC;IAED,cAMC;IAED;;;OAGG;IACH,iBAFG;QAA0B,OAAO,GAAzB,OAAO;KAAmB,QAQpC;IAED,mBAMC;IAED,WAMC;IAED,gBAMC;IAED,eAMC;IAED,YAMC;IAED,aAOC;IAED,eAMC;IAED;;OAEG;IACH,oBAFW,MAAM,QAQhB;IAED,gBAMC;IAED;;;;;;;OAOG;IACH,mBANG;QAAyB,SAAS,GAA1B,MAAM;QACW,UAAU,GAA3B,MAAM;QACW,UAAU,GAA3B,MAAM;QACW,YAAY,GAA7B,MAAM;QACW,YAAY,GAA7B,MAAM;KAAwB,QA+BxC;IAED;;;;;;;;;;;OAWG;IACH,cAVG;QAA0B,gBAAgB,GAAlC,OAAO;QACW,sBAAsB,GAAxC,OAAO;QACW,YAAY,GAA9B,OAAO;QACW,YAAY,GAA9B,OAAO;QACW,4BAA4B,GAA9C,OAAO;QACW,eAAe,GAAjC,OAAO;QACW,sBAAsB,GAAxC,OAAO;QACW,eAAe,GAAjC,OAAO;QACY,SAAS,GAA5B,MAAM,EAAE;KAAqB,QAQvC;IAED;;;;;;;;OAQG;IACH,iBAPG;QAA0B,WAAW,GAA7B,OAAO;QACW,iBAAiB,GAAnC,OAAO;QACW,kBAAkB,GAApC,OAAO;QACW,iBAAiB,GAAnC,OAAO;QACW,cAAc,GAAhC,OAAO;QACW,iBAAiB,GAAnC,OAAO;KAAmC,QAQpD;IAED;;OAEG;IACH,eAFW,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAQ3B;IAED,YAMC;IAED,YAMC;IAED,cAMC;IAED,cAMC;IAED,iCAWC;CAcF;uBA7asB,cAAc"}
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../src/string.js"],"names":[],"mappings":"AAibA;;GAEG;AACH,iDAEC;AA/ZD;IACE,cAoBC;IAED;;OAEG;IACH,YAFa,IAAI,CAQhB;IAED;;OAEG;IACH,eAFW,MAAM,QAUhB;IAED;;OAEG;IACH,YAFW,MAAM,QAUhB;IAED;;OAEG;IACH,YAFW,MAAM,QAUhB;IAED,aAIC;IAED;;OAEG;IACH,mBAFW,OAAO,QAYjB;IAED;;OAEG;IACH,mBAFW,OAAO,QAYjB;IAED;;OAEG;IACH,WAFW,MAAM,QAahB;IAED,cAMC;IAED,uBASC;IAED,YAMC;IAED,YAMC;IAED,aAMC;IAED,cAMC;IAED;;;OAGG;IACH,iBAFG;QAA0B,OAAO,GAAzB,OAAO;KAAmB,QAQpC;IAED,mBAMC;IAED,WAMC;IAED,gBAMC;IAED,eAMC;IAED,YAMC;IAED,aAOC;IAED,eAMC;IAED;;OAEG;IACH,oBAFW,MAAM,QAQhB;IAED,gBAMC;IAED;;;;;;;OAOG;IACH,mBANG;QAAyB,SAAS,GAA1B,MAAM;QACW,UAAU,GAA3B,MAAM;QACW,UAAU,GAA3B,MAAM;QACW,YAAY,GAA7B,MAAM;QACW,YAAY,GAA7B,MAAM;KAAwB,QA+BxC;IAED;;;;;;;;;;;OAWG;IACH,cAVG;QAA0B,gBAAgB,GAAlC,OAAO;QACW,sBAAsB,GAAxC,OAAO;QACW,YAAY,GAA9B,OAAO;QACW,YAAY,GAA9B,OAAO;QACW,4BAA4B,GAA9C,OAAO;QACW,eAAe,GAAjC,OAAO;QACW,sBAAsB,GAAxC,OAAO;QACW,eAAe,GAAjC,OAAO;QACY,SAAS,GAA5B,MAAM,EAAE;KAAqB,QAQvC;IAED;;;;;;;;OAQG;IACH,iBAPG;QAA0B,WAAW,GAA7B,OAAO;QACW,iBAAiB,GAAnC,OAAO;QACW,kBAAkB,GAApC,OAAO;QACW,iBAAiB,GAAnC,OAAO;QACW,cAAc,GAAhC,OAAO;QACW,iBAAiB,GAAnC,OAAO;KAAmC,QAQpD;IAED;;OAEG;IACH,eAFW,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAQ3B;IAED,YAMC;IAED,YAMC;IAED,cAMC;IAED,cAMC;IAED,iCAWC;CAcF;uBA7asB,cAAc"}
package/.prettierrc.cjs DELETED
@@ -1 +0,0 @@
1
- module.exports = require('@bedrockio/prettier-config');
package/dist/cjs/utils.js DELETED
@@ -1,37 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "isSchema", {
7
- enumerable: true,
8
- get: function () {
9
- return _Schema.isSchema;
10
- }
11
- });
12
- Object.defineProperty(exports, "isSchemaError", {
13
- enumerable: true,
14
- get: function () {
15
- return _errors.isSchemaError;
16
- }
17
- });
18
- exports.omit = omit;
19
- exports.pick = pick;
20
- var _Schema = require("./Schema");
21
- var _errors = require("./errors");
22
- function pick(obj, keys) {
23
- const result = {};
24
- for (let key of keys) {
25
- result[key] = obj[key];
26
- }
27
- return result;
28
- }
29
- function omit(obj, keys) {
30
- const result = {};
31
- for (let key of Object.keys(obj || {})) {
32
- if (!keys.includes(key)) {
33
- result[key] = obj[key];
34
- }
35
- }
36
- return result;
37
- }
package/src/utils.js DELETED
@@ -1,20 +0,0 @@
1
- export { isSchema } from './Schema';
2
- export { isSchemaError } from './errors';
3
-
4
- export function pick(obj, keys) {
5
- const result = {};
6
- for (let key of keys) {
7
- result[key] = obj[key];
8
- }
9
- return result;
10
- }
11
-
12
- export function omit(obj, keys) {
13
- const result = {};
14
- for (let key of Object.keys(obj || {})) {
15
- if (!keys.includes(key)) {
16
- result[key] = obj[key];
17
- }
18
- }
19
- return result;
20
- }