From 6d5924d8ec64d950ee883fc273e1ec41626d085b Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Sat, 29 Aug 2020 10:45:13 -0400 Subject: [PATCH 01/31] feat: use class syntax and static factories --- .babelrc.js | 4 +- README.md | 32 ++++++++----- package.json | 1 + src/array.js | 75 +++++++++++++++---------------- src/boolean.js | 36 +++++++-------- src/date.js | 39 ++++++++-------- src/index.js | 36 +++++++++++---- src/mixed.js | 119 +++++++++++++++++++++++++------------------------ src/number.js | 69 ++++++++++++++-------------- src/object.js | 112 +++++++++++++++++++++++----------------------- src/string.js | 60 ++++++++++++------------- test-setup.js | 6 +-- test/array.js | 25 +++++------ test/bool.js | 32 +++---------- test/mixed.js | 5 ++- test/number.js | 12 ++--- test/object.js | 14 +++--- yarn.lock | 5 +++ 18 files changed, 350 insertions(+), 332 deletions(-) diff --git a/.babelrc.js b/.babelrc.js index 6afa4029d..1ff99b592 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -1,7 +1,7 @@ module.exports = (api) => ({ presets: [ [ - 'jason', + 'babel-preset-jason/esm', api.env() !== 'test' ? { ignoreBrowserslistConfig: true, @@ -9,6 +9,8 @@ module.exports = (api) => ({ } : { target: 'node', + + debug: true, targets: { node: 'current' }, }, ], diff --git a/README.md b/README.md index b657d1328..5647c5a1c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane - - [Install](#install) - [Usage](#usage) - [Using a custom locale dictionary](#using-a-custom-locale-dictionary) @@ -124,13 +123,7 @@ npm install -D @types/yup You define and create schema objects. Schema objects are immutable, so each call of a method returns a _new_ schema object. When using es module syntax, yup exports everything as a named export ```js -import * as yup from 'yup'; // for everything -// or -import { string, object } from 'yup'; // for only what you need -``` - -```js -let yup = require('yup'); +import * as yup from 'yup'; let schema = yup.object().shape({ name: yup.string().required(), @@ -161,6 +154,21 @@ schema.cast({ // => { name: 'jimmy', age: 24, createdOn: Date } ``` +The exported functions are factory methods for constructing schema instances, but without the `new` keyword. +If you need access to the actual schema classes, they are also exported: + +```js +import { + BooleanSchema, + DateSchema, + MixedSchema, + NumberSchema, + ArraySchema, + ObjectSchema, + StringSchema, +} from 'yup'; +``` + > If you're looking for an easily serializable DSL for yup schema, check out [yup-ast](https://github.com/WASD-Team/yup-ast) ### Using a custom locale dictionary @@ -1230,10 +1238,14 @@ utility or pattern that works with that pattern. The below demonstrates using th syntax since it's less verbose, but you absolutely aren't required to use it. ```js -let DateSchema = yup.date; +import { DateSchema } from 'yup'; + let invalidDate = new Date(''); // our failed to coerce value class MomentDateSchemaType extends DateSchema { + static create() { + return MomentDateSchemaType(); + } constructor() { super(); this._validFormats = []; @@ -1261,7 +1273,7 @@ class MomentDateSchemaType extends DateSchema { } } -let schema = new MomentDateSchemaType(); +let schema = MomentDateSchemaType.create(); schema.format('YYYY-MM-DD').cast('It is 2012-05-25'); // => Fri May 25 2012 00:00:00 GMT-0400 (Eastern Daylight Time) ``` diff --git a/package.json b/package.json index da7de1107..c552abda4 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "lodash": "^4.17.15", "lodash-es": "^4.17.11", "property-expr": "^2.0.2", + "synchronous-promise": "^2.0.13", "toposort": "^2.0.2" }, "readme": "ERROR: No README data found!", diff --git a/src/array.js b/src/array.js index 168d273d8..50e53e393 100644 --- a/src/array.js +++ b/src/array.js @@ -1,4 +1,3 @@ -import inherits from './util/inherits'; import isAbsent from './util/isAbsent'; import isSchema from './util/isSchema'; import printValue from './util/printValue'; @@ -6,38 +5,38 @@ import MixedSchema from './mixed'; import { array as locale } from './locale'; import runTests from './util/runTests'; -export default ArraySchema; - -function ArraySchema(type) { - if (!(this instanceof ArraySchema)) return new ArraySchema(type); - - MixedSchema.call(this, { type: 'array' }); - - // `undefined` specifically means uninitialized, as opposed to - // "no subtype" - this._subType = undefined; - this.innerType = undefined; - - this.withMutation(() => { - this.transform(function (values) { - if (typeof values === 'string') - try { - values = JSON.parse(values); - } catch (err) { - values = null; - } +export default class ArraySchema extends MixedSchema { + static create(type) { + return new ArraySchema(type); + } + + constructor(type) { + super({ type: 'array' }); + + // `undefined` specifically means uninitialized, as opposed to + // "no subtype" + this._subType = undefined; + this.innerType = undefined; + + this.withMutation(() => { + this.transform(function (values) { + if (typeof values === 'string') + try { + values = JSON.parse(values); + } catch (err) { + values = null; + } + + return this.isType(values) ? values : null; + }); - return this.isType(values) ? values : null; + if (type) this.of(type); }); + } - if (type) this.of(type); - }); -} - -inherits(ArraySchema, MixedSchema, { _typeCheck(v) { return Array.isArray(v); - }, + } _cast(_value, _opts) { const value = MixedSchema.prototype._cast.call(this, _value, _opts); @@ -59,7 +58,7 @@ inherits(ArraySchema, MixedSchema, { }); return isChanged ? castArray : value; - }, + } _validate(_value, options = {}, callback) { let errors = []; @@ -125,13 +124,13 @@ inherits(ArraySchema, MixedSchema, { ); }, ); - }, + } _isPresent(value) { return ( MixedSchema.prototype._isPresent.call(this, value) && value.length > 0 ); - }, + } of(schema) { var next = this.clone(); @@ -147,7 +146,7 @@ inherits(ArraySchema, MixedSchema, { next.innerType = schema; return next; - }, + } min(min, message) { message = message || locale.min; @@ -161,7 +160,7 @@ inherits(ArraySchema, MixedSchema, { return isAbsent(value) || value.length >= this.resolve(min); }, }); - }, + } max(max, message) { message = message || locale.max; @@ -174,7 +173,7 @@ inherits(ArraySchema, MixedSchema, { return isAbsent(value) || value.length <= this.resolve(max); }, }); - }, + } ensure() { return this.default(() => []).transform((val, original) => { @@ -182,7 +181,7 @@ inherits(ArraySchema, MixedSchema, { if (this._typeCheck(val)) return val; return original == null ? [] : [].concat(original); }); - }, + } compact(rejector) { let reject = !rejector ? (v) => !!v : (v, i, a) => !rejector(v, i, a); @@ -190,11 +189,11 @@ inherits(ArraySchema, MixedSchema, { return this.transform((values) => values != null ? values.filter(reject) : values, ); - }, + } describe() { let base = MixedSchema.prototype.describe.call(this); if (this.innerType) base.innerType = this.innerType.describe(); return base; - }, -}); + } +} diff --git a/src/boolean.js b/src/boolean.js index a886fa7f7..716cc2cec 100644 --- a/src/boolean.js +++ b/src/boolean.js @@ -1,28 +1,26 @@ -import inherits from './util/inherits'; import MixedSchema from './mixed'; -export default BooleanSchema; +export default class BooleanSchema extends MixedSchema { + static create(options) { + return new BooleanSchema(options); + } -function BooleanSchema() { - if (!(this instanceof BooleanSchema)) return new BooleanSchema(); + constructor() { + super({ type: 'boolean' }); - MixedSchema.call(this, { type: 'boolean' }); - - this.withMutation(() => { - this.transform(function(value) { - if (!this.isType(value)) { - if (/^(true|1)$/i.test(value)) return true; - if (/^(false|0)$/i.test(value)) return false; - } - return value; + this.withMutation(() => { + this.transform(function (value) { + if (!this.isType(value)) { + if (/^(true|1)$/i.test(value)) return true; + if (/^(false|0)$/i.test(value)) return false; + } + return value; + }); }); - }); -} - -inherits(BooleanSchema, MixedSchema, { + } _typeCheck(v) { if (v instanceof Boolean) v = v.valueOf(); return typeof v === 'boolean'; - }, -}); + } +} diff --git a/src/date.js b/src/date.js index c50e906a4..682346bb3 100644 --- a/src/date.js +++ b/src/date.js @@ -1,5 +1,4 @@ import MixedSchema from './mixed'; -import inherits from './util/inherits'; import isoParse from './util/isodate'; import { date as locale } from './locale'; import isAbsent from './util/isAbsent'; @@ -7,30 +6,30 @@ import Ref from './Reference'; let invalidDate = new Date(''); -let isDate = obj => Object.prototype.toString.call(obj) === '[object Date]'; +let isDate = (obj) => Object.prototype.toString.call(obj) === '[object Date]'; -export default DateSchema; +export default class DateSchema extends MixedSchema { + static create() { + return new DateSchema(); + } + constructor() { + super({ type: 'date' }); -function DateSchema() { - if (!(this instanceof DateSchema)) return new DateSchema(); + this.withMutation(() => { + this.transform(function (value) { + if (this.isType(value)) return value; - MixedSchema.call(this, { type: 'date' }); + value = isoParse(value); - this.withMutation(() => { - this.transform(function(value) { - if (this.isType(value)) return value; - - value = isoParse(value); - // 0 is a valid timestamp equivalent to 1970-01-01T00:00:00Z(unix epoch) or before. - return !isNaN(value) ? new Date(value) : invalidDate; + // 0 is a valid timestamp equivalent to 1970-01-01T00:00:00Z(unix epoch) or before. + return !isNaN(value) ? new Date(value) : invalidDate; + }); }); - }); -} + } -inherits(DateSchema, MixedSchema, { _typeCheck(v) { return isDate(v) && !isNaN(v.getTime()); - }, + } min(min, message = locale.min) { var limit = min; @@ -52,7 +51,7 @@ inherits(DateSchema, MixedSchema, { return isAbsent(value) || value >= this.resolve(limit); }, }); - }, + } max(max, message = locale.max) { var limit = max; @@ -74,5 +73,5 @@ inherits(DateSchema, MixedSchema, { return isAbsent(value) || value <= this.resolve(limit); }, }); - }, -}); + } +} diff --git a/src/index.js b/src/index.js index 8b48d5af3..8913f8bf8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,10 @@ -import mixed from './mixed'; -import bool from './boolean'; -import string from './string'; -import number from './number'; -import date from './date'; -import object from './object'; -import array from './array'; +import MixedSchema from './mixed'; +import BoolSchema from './boolean'; +import StringSchema from './string'; +import NumberSchema from './number'; +import DateSchema from './date'; +import ObjectSchema from './object'; +import ArraySchema from './array'; import Ref from './Reference'; import Lazy from './Lazy'; import ValidationError from './ValidationError'; @@ -12,10 +12,9 @@ import reach from './util/reach'; import isSchema from './util/isSchema'; import setLocale from './setLocale'; -let boolean = bool; let ref = (key, options) => new Ref(key, options); -let lazy = fn => new Lazy(fn); +let lazy = (fn) => new Lazy(fn); function addMethod(schemaType, name, fn) { if (!schemaType || !isSchema(schemaType.prototype)) @@ -29,6 +28,25 @@ function addMethod(schemaType, name, fn) { schemaType.prototype[name] = fn; } +const mixed = MixedSchema.create; +const string = StringSchema.create; +const number = NumberSchema.create; +const bool = BoolSchema.create; +const boolean = bool; +const date = DateSchema.create; +const object = ObjectSchema.create; +const array = ArraySchema.create; + +export { + MixedSchema, + BoolSchema, + StringSchema, + NumberSchema, + DateSchema, + ObjectSchema, + ArraySchema, +}; + export { mixed, string, diff --git a/src/mixed.js b/src/mixed.js index 64ad2e06f..db10a67de 100644 --- a/src/mixed.js +++ b/src/mixed.js @@ -63,35 +63,35 @@ class RefSet { } } -export default function SchemaType(options = {}) { - if (!(this instanceof SchemaType)) return new SchemaType(); +export default class MixedSchema { + static create(options) { + return new MixedSchema(options); + } - this._deps = []; - this._conditions = []; - this._options = { abortEarly: true, recursive: true }; - this._exclusive = Object.create(null); + constructor(options = {}) { + this._deps = []; + this._conditions = []; + this._options = { abortEarly: true, recursive: true }; + this._exclusive = Object.create(null); - this._whitelist = new RefSet(); - this._blacklist = new RefSet(); + this._whitelist = new RefSet(); + this._blacklist = new RefSet(); - this.tests = []; - this.transforms = []; + this.tests = []; + this.transforms = []; - this.withMutation(() => { - this.typeError(locale.notType); - }); + this.withMutation(() => { + this.typeError(locale.notType); + }); - if (has(options, 'default')) this._defaultDefault = options.default; + if (has(options, 'default')) this._defaultDefault = options.default; - this.type = options.type || 'mixed'; - // TODO: remove - this._type = options.type || 'mixed'; -} - -const proto = (SchemaType.prototype = { - __isYupSchema__: true, + this.type = options.type || 'mixed'; + // TODO: remove + this._type = options.type || 'mixed'; + } - constructor: SchemaType, + __isYupSchema__ = true; clone() { if (this._mutate) return this; @@ -101,13 +101,13 @@ const proto = (SchemaType.prototype = { return cloneDeepWith(this, (value) => { if (isSchema(value) && value !== this) return value; }); - }, + } label(label) { var next = this.clone(); next._label = label; return next; - }, + } meta(obj) { if (arguments.length === 0) return this._meta; @@ -115,7 +115,7 @@ const proto = (SchemaType.prototype = { var next = this.clone(); next._meta = Object.assign(next._meta || {}, obj); return next; - }, + } withMutation(fn) { let before = this._mutate; @@ -123,7 +123,7 @@ const proto = (SchemaType.prototype = { let result = fn(this); this._mutate = before; return result; - }, + } concat(schema) { if (!schema || schema === this) return this; @@ -161,12 +161,12 @@ const proto = (SchemaType.prototype = { }); return next; - }, + } isType(v) { if (this._nullable && v === null) return true; return !this._typeCheck || this._typeCheck(v); - }, + } resolve(options) { let schema = this; @@ -185,7 +185,8 @@ const proto = (SchemaType.prototype = { } return schema; - }, + } + /** * * @param {*} value @@ -223,7 +224,7 @@ const proto = (SchemaType.prototype = { } return result; - }, + } _cast(rawValue) { let value = @@ -239,7 +240,7 @@ const proto = (SchemaType.prototype = { } return value; - }, + } _validate(_value, options = {}, cb) { let { @@ -300,7 +301,7 @@ const proto = (SchemaType.prototype = { ); }, ); - }, + } validate(value, options = {}, maybeCb) { let schema = this.resolve({ ...options, value }); @@ -314,7 +315,7 @@ const proto = (SchemaType.prototype = { else resolve(value); }), ); - }, + } validateSync(value, options = {}) { let schema = this.resolve({ ...options, value }); @@ -326,7 +327,7 @@ const proto = (SchemaType.prototype = { }); return result; - }, + } isValid(value, options) { return this.validate(value, options) @@ -335,7 +336,7 @@ const proto = (SchemaType.prototype = { if (err.name === 'ValidationError') return false; throw err; }); - }, + } isValidSync(value, options) { try { @@ -345,12 +346,12 @@ const proto = (SchemaType.prototype = { if (err.name === 'ValidationError') return false; throw err; } - }, + } getDefault(options = {}) { let schema = this.resolve(options); return schema.default(); - }, + } default(def) { if (arguments.length === 0) { @@ -366,17 +367,17 @@ const proto = (SchemaType.prototype = { var next = this.clone(); next._default = def; return next; - }, + } strict(isStrict = true) { var next = this.clone(); next._options.strict = isStrict; return next; - }, + } _isPresent(value) { return value != null; - }, + } required(message = locale.required) { return this.test({ @@ -387,25 +388,25 @@ const proto = (SchemaType.prototype = { return this.schema._isPresent(value); }, }); - }, + } notRequired() { var next = this.clone(); next.tests = next.tests.filter((test) => test.OPTIONS.name !== 'required'); return next; - }, + } nullable(isNullable = true) { var next = this.clone(); next._nullable = isNullable; return next; - }, + } transform(fn) { var next = this.clone(); next.transforms.push(fn); return next; - }, + } /** * Adds a test function to the schema's queue of tests. @@ -465,7 +466,7 @@ const proto = (SchemaType.prototype = { next.tests.push(validate); return next; - }, + } when(keys, options) { if (arguments.length === 1) { @@ -483,7 +484,7 @@ const proto = (SchemaType.prototype = { next._conditions.push(new Condition(deps, options)); return next; - }, + } typeError(message) { var next = this.clone(); @@ -502,7 +503,7 @@ const proto = (SchemaType.prototype = { }, }); return next; - }, + } oneOf(enums, message = locale.oneOf) { var next = this.clone(); @@ -530,7 +531,7 @@ const proto = (SchemaType.prototype = { }); return next; - }, + } notOneOf(enums, message = locale.notOneOf) { var next = this.clone(); @@ -555,17 +556,17 @@ const proto = (SchemaType.prototype = { }); return next; - }, + } strip(strip = true) { let next = this.clone(); next._strip = strip; return next; - }, + } _option(key, overrides) { return has(overrides, key) ? overrides[key] : this._options[key]; - }, + } describe() { const next = this.clone(); @@ -584,7 +585,7 @@ const proto = (SchemaType.prototype = { if (next._blacklist.size) description.notOneOf = next._blacklist.describe(); return description; - }, + } defined(message = locale.defined) { return this.nullable().test({ @@ -595,11 +596,11 @@ const proto = (SchemaType.prototype = { return value !== undefined; }, }); - }, -}); + } +} for (const method of ['validate', 'validateSync']) - proto[`${method}At`] = function (path, value, options = {}) { + MixedSchema.prototype[`${method}At`] = function (path, value, options = {}) { const { parent, parentPath, schema } = getIn( this, path, @@ -613,6 +614,8 @@ for (const method of ['validate', 'validateSync']) }); }; -for (const alias of ['equals', 'is']) proto[alias] = proto.oneOf; -for (const alias of ['not', 'nope']) proto[alias] = proto.notOneOf; -proto.optional = proto.notRequired; +for (const alias of ['equals', 'is']) + MixedSchema.prototype[alias] = MixedSchema.prototype.oneOf; +for (const alias of ['not', 'nope']) + MixedSchema.prototype[alias] = MixedSchema.prototype.notOneOf; +MixedSchema.prototype.optional = MixedSchema.prototype.notRequired; diff --git a/src/number.js b/src/number.js index b179e6aa8..513cc1527 100644 --- a/src/number.js +++ b/src/number.js @@ -1,39 +1,40 @@ -import inherits from './util/inherits'; import MixedSchema from './mixed'; import { number as locale } from './locale'; import isAbsent from './util/isAbsent'; -let isNaN = value => value != +value; +let isNaN = (value) => value != +value; -export default function NumberSchema() { - if (!(this instanceof NumberSchema)) return new NumberSchema(); +export default class NumberSchema extends MixedSchema { + static create() { + return new NumberSchema(); + } - MixedSchema.call(this, { type: 'number' }); + constructor() { + super({ type: 'number' }); - this.withMutation(() => { - this.transform(function(value) { - let parsed = value; + this.withMutation(() => { + this.transform(function (value) { + let parsed = value; - if (typeof parsed === 'string') { - parsed = parsed.replace(/\s/g, ''); - if (parsed === '') return NaN; - // don't use parseFloat to avoid positives on alpha-numeric strings - parsed = +parsed; - } + if (typeof parsed === 'string') { + parsed = parsed.replace(/\s/g, ''); + if (parsed === '') return NaN; + // don't use parseFloat to avoid positives on alpha-numeric strings + parsed = +parsed; + } - if (this.isType(parsed)) return parsed; + if (this.isType(parsed)) return parsed; - return parseFloat(parsed); + return parseFloat(parsed); + }); }); - }); -} + } -inherits(NumberSchema, MixedSchema, { _typeCheck(value) { if (value instanceof Number) value = value.valueOf(); return typeof value === 'number' && !isNaN(value); - }, + } min(min, message = locale.min) { return this.test({ @@ -45,7 +46,7 @@ inherits(NumberSchema, MixedSchema, { return isAbsent(value) || value >= this.resolve(min); }, }); - }, + } max(max, message = locale.max) { return this.test({ @@ -57,7 +58,7 @@ inherits(NumberSchema, MixedSchema, { return isAbsent(value) || value <= this.resolve(max); }, }); - }, + } lessThan(less, message = locale.lessThan) { return this.test({ @@ -69,7 +70,7 @@ inherits(NumberSchema, MixedSchema, { return isAbsent(value) || value < this.resolve(less); }, }); - }, + } moreThan(more, message = locale.moreThan) { return this.test({ @@ -81,27 +82,27 @@ inherits(NumberSchema, MixedSchema, { return isAbsent(value) || value > this.resolve(more); }, }); - }, + } positive(msg = locale.positive) { return this.moreThan(0, msg); - }, + } negative(msg = locale.negative) { return this.lessThan(0, msg); - }, + } integer(message = locale.integer) { return this.test({ name: 'integer', message, - test: val => isAbsent(val) || Number.isInteger(val), + test: (val) => isAbsent(val) || Number.isInteger(val), }); - }, + } truncate() { - return this.transform(value => (!isAbsent(value) ? value | 0 : value)); - }, + return this.transform((value) => (!isAbsent(value) ? value | 0 : value)); + } round(method) { var avail = ['ceil', 'floor', 'round', 'trunc']; @@ -115,8 +116,8 @@ inherits(NumberSchema, MixedSchema, { 'Only valid options for round() are: ' + avail.join(', '), ); - return this.transform( - value => (!isAbsent(value) ? Math[method](value) : value), + return this.transform((value) => + !isAbsent(value) ? Math[method](value) : value, ); - }, -}); + } +} diff --git a/src/object.js b/src/object.js index cbb51629b..a591a5fb6 100644 --- a/src/object.js +++ b/src/object.js @@ -9,7 +9,6 @@ import MixedSchema from './mixed'; import { object as locale } from './locale.js'; import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; -import inherits from './util/inherits'; import runTests from './util/runTests'; let isObject = (obj) => @@ -20,57 +19,58 @@ function unknown(ctx, value) { return Object.keys(value).filter((key) => known.indexOf(key) === -1); } -export default function ObjectSchema(spec) { - if (!(this instanceof ObjectSchema)) return new ObjectSchema(spec); - - MixedSchema.call(this, { - type: 'object', - default() { - if (!this._nodes.length) return undefined; - - let dft = {}; - this._nodes.forEach((key) => { - dft[key] = this.fields[key].default - ? this.fields[key].default() - : undefined; - }); - return dft; - }, - }); +export default class ObjectSchema extends MixedSchema { + static create(spec) { + return new ObjectSchema(spec); + } + constructor(spec) { + super({ + type: 'object', + default() { + if (!this._nodes.length) return undefined; + + let dft = {}; + this._nodes.forEach((key) => { + dft[key] = this.fields[key].default + ? this.fields[key].default() + : undefined; + }); + return dft; + }, + }); - this.fields = Object.create(null); + this.fields = Object.create(null); - this._sortErrors = sortByKeyOrder([]); + this._sortErrors = sortByKeyOrder([]); - this._nodes = []; - this._excludedEdges = []; + this._nodes = []; + this._excludedEdges = []; - this.withMutation(() => { - this.transform(function coerce(value) { - if (typeof value === 'string') { - try { - value = JSON.parse(value); - } catch (err) { - value = null; + this.withMutation(() => { + this.transform(function coerce(value) { + if (typeof value === 'string') { + try { + value = JSON.parse(value); + } catch (err) { + value = null; + } } + if (this.isType(value)) return value; + return null; + }); + + if (spec) { + this.shape(spec); } - if (this.isType(value)) return value; - return null; }); + } - if (spec) { - this.shape(spec); - } - }); -} - -inherits(ObjectSchema, MixedSchema, { _typeCheck(value) { return isObject(value) || typeof value === 'function'; - }, + } _cast(_value, options = {}) { - let value = MixedSchema.prototype._cast.call(this, _value); + let value = super._cast(_value); //should ignore nulls here if (value === undefined) return this.default(); @@ -129,7 +129,7 @@ inherits(ObjectSchema, MixedSchema, { } return isChanged ? intermediateValue : value; - }, + } /** * @typedef {Object} Ancestor @@ -173,7 +173,7 @@ inherits(ObjectSchema, MixedSchema, { opts.originalValue = originalValue; opts.from = from; - MixedSchema.prototype._validate.call(this, _value, opts, (err, value) => { + super._validate(_value, opts, (err, value) => { if (err) { if (abortEarly) return void callback(err); @@ -231,15 +231,15 @@ inherits(ObjectSchema, MixedSchema, { callback, ); }); - }, + } concat(schema) { - var next = MixedSchema.prototype.concat.call(this, schema); + var next = super.concat(schema); next._nodes = sortFields(next.fields, next._excludedEdges); return next; - }, + } shape(schema, excludes = []) { let next = this.clone(); @@ -259,7 +259,7 @@ inherits(ObjectSchema, MixedSchema, { next._nodes = sortFields(fields, next._excludedEdges); return next; - }, + } from(from, to, alias) { let fromGetter = getter(from, true); @@ -276,7 +276,7 @@ inherits(ObjectSchema, MixedSchema, { return newObj; }); - }, + } noUnknown(noAllow = true, message = locale.noUnknown) { if (typeof noAllow === 'string') { @@ -302,31 +302,31 @@ inherits(ObjectSchema, MixedSchema, { next._options.stripUnknown = noAllow; return next; - }, + } unknown(allow = true, message = locale.noUnknown) { return this.noUnknown(!allow, message); - }, + } transformKeys(fn) { return this.transform((obj) => obj && mapKeys(obj, (_, key) => fn(key))); - }, + } camelCase() { return this.transformKeys(camelCase); - }, + } snakeCase() { return this.transformKeys(snakeCase); - }, + } constantCase() { return this.transformKeys((key) => snakeCase(key).toUpperCase()); - }, + } describe() { - let base = MixedSchema.prototype.describe.call(this); + let base = super.describe(); base.fields = mapValues(this.fields, (value) => value.describe()); return base; - }, -}); + } +} diff --git a/src/string.js b/src/string.js index 4e3335b52..096501b86 100644 --- a/src/string.js +++ b/src/string.js @@ -1,4 +1,3 @@ -import inherits from './util/inherits'; import MixedSchema from './mixed'; import { string as locale } from './locale'; import isAbsent from './util/isAbsent'; @@ -8,35 +7,34 @@ let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\u // eslint-disable-next-line let rUrl = /^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; // eslint-disable-next-line -let rUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i +let rUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i; let isTrimmed = (value) => isAbsent(value) || value === value.trim(); -export default function StringSchema() { - if (!(this instanceof StringSchema)) return new StringSchema(); - - MixedSchema.call(this, { type: 'string' }); - - this.withMutation(() => { - this.transform(function (value) { - if (this.isType(value)) return value; - return value != null && value.toString ? value.toString() : value; +export default class StringSchema extends MixedSchema { + static create() { + return new StringSchema(); + } + constructor() { + super({ type: 'string' }); + + this.withMutation(() => { + this.transform(function (value) { + if (this.isType(value)) return value; + return value != null && value.toString ? value.toString() : value; + }); }); - }); -} + } -inherits(StringSchema, MixedSchema, { _typeCheck(value) { if (value instanceof String) value = value.valueOf(); return typeof value === 'string'; - }, + } _isPresent(value) { - return ( - MixedSchema.prototype._isPresent.call(this, value) && value.length > 0 - ); - }, + return super._isPresent(value) && value.length > 0; + } length(length, message = locale.length) { return this.test({ @@ -48,7 +46,7 @@ inherits(StringSchema, MixedSchema, { return isAbsent(value) || value.length === this.resolve(length); }, }); - }, + } min(min, message = locale.min) { return this.test({ @@ -60,7 +58,7 @@ inherits(StringSchema, MixedSchema, { return isAbsent(value) || value.length >= this.resolve(min); }, }); - }, + } max(max, message = locale.max) { return this.test({ @@ -72,7 +70,7 @@ inherits(StringSchema, MixedSchema, { return isAbsent(value) || value.length <= this.resolve(max); }, }); - }, + } matches(regex, options) { let excludeEmptyString = false; @@ -96,7 +94,7 @@ inherits(StringSchema, MixedSchema, { (value === '' && excludeEmptyString) || value.search(regex) !== -1, }); - }, + } email(message = locale.email) { return this.matches(rEmail, { @@ -104,7 +102,7 @@ inherits(StringSchema, MixedSchema, { message, excludeEmptyString: true, }); - }, + } url(message = locale.url) { return this.matches(rUrl, { @@ -112,7 +110,7 @@ inherits(StringSchema, MixedSchema, { message, excludeEmptyString: true, }); - }, + } uuid(message = locale.uuid) { return this.matches(rUUID, { @@ -120,12 +118,12 @@ inherits(StringSchema, MixedSchema, { message, excludeEmptyString: false, }); - }, + } //-- transforms -- ensure() { return this.default('').transform((val) => (val === null ? '' : val)); - }, + } trim(message = locale.trim) { return this.transform((val) => (val != null ? val.trim() : val)).test({ @@ -133,7 +131,7 @@ inherits(StringSchema, MixedSchema, { name: 'trim', test: isTrimmed, }); - }, + } lowercase(message = locale.lowercase) { return this.transform((value) => @@ -144,7 +142,7 @@ inherits(StringSchema, MixedSchema, { exclusive: true, test: (value) => isAbsent(value) || value === value.toLowerCase(), }); - }, + } uppercase(message = locale.uppercase) { return this.transform((value) => @@ -155,5 +153,5 @@ inherits(StringSchema, MixedSchema, { exclusive: true, test: (value) => isAbsent(value) || value === value.toUpperCase(), }); - }, -}); + } +} diff --git a/test-setup.js b/test-setup.js index be6d97ad0..99eb04c94 100644 --- a/test-setup.js +++ b/test-setup.js @@ -20,11 +20,11 @@ Object.defineProperty( global.TestHelpers = require('./test/helpers'); if (global.YUP_USE_SYNC) { - const mixed = require('./src/mixed'); // eslint-disable-line global-require + const MixedSchema = require('./src/mixed'); // eslint-disable-line global-require - const { validate } = mixed.prototype; + const { validate } = MixedSchema.prototype; - mixed.prototype.validate = function (value, options = {}, maybeCb) { + MixedSchema.prototype.validate = function (value, options = {}, maybeCb) { let run = false; options.sync = true; diff --git a/test/array.js b/test/array.js index 16bd64c5f..885cc332f 100644 --- a/test/array.js +++ b/test/array.js @@ -1,7 +1,4 @@ -import string from '../src/string'; -import number from '../src/number'; -import object from '../src/object'; -import array from '../src/array'; +import { string, number, object, array, StringSchema } from '../src'; describe('Array types', () => { describe('casting', () => { @@ -89,14 +86,14 @@ describe('Array types', () => { }); it('should prevent recursive casting', async () => { - let castSpy = sinon.spy(string.prototype, '_cast'); + let castSpy = sinon.spy(StringSchema.prototype, '_cast'); let value = await array(string()).validate([5]); value[0].should.equal('5'); castSpy.should.have.been.calledOnce(); - string.prototype._cast.restore(); + StringSchema.prototype._cast.restore(); }); }); @@ -106,14 +103,14 @@ describe('Array types', () => { .test('name', 'oops', () => false); return Promise.all([ - // inst - // .validate([{ str: '' }]) - // .should.be.rejected() - // .then(err => { - // err.value.should.eql([{ str: '' }]); - // err.errors.length.should.equal(1); - // err.errors.should.eql(['oops']); - // }), + inst + .validate([{ str: '' }]) + .should.be.rejected() + .then((err) => { + err.value.should.eql([{ str: '' }]); + err.errors.length.should.equal(1); + err.errors.should.eql(['oops']); + }), inst .validate([{ str: '' }], { abortEarly: false }) .should.be.rejected() diff --git a/test/bool.js b/test/bool.js index 10155a838..ac31a522e 100644 --- a/test/bool.js +++ b/test/bool.js @@ -1,4 +1,4 @@ -import bool from '../src/boolean'; +import { bool } from '../src'; describe('Boolean types', () => { it('should CAST correctly', () => { @@ -20,11 +20,7 @@ describe('Boolean types', () => { let inst = bool(); expect(inst.default()).to.equal(undefined); - inst - .default(true) - .required() - .default() - .should.equal(true); + inst.default(true).required().default().should.equal(true); }); it('should type check', () => { @@ -41,34 +37,20 @@ describe('Boolean types', () => { expect(inst.isType(null)).to.equal(false); - inst - .nullable() - .isType(null) - .should.equal(true); + inst.nullable().isType(null).should.equal(true); }); it('bool should VALIDATE correctly', () => { let inst = bool().required(); return Promise.all([ - bool() - .isValid('1') - .should.eventually() - .equal(true), - bool() - .strict() - .isValid(null) - .should.eventually() - .equal(false), - bool() - .nullable() - .isValid(null) - .should.eventually() - .equal(true), + bool().isValid('1').should.eventually().equal(true), + bool().strict().isValid(null).should.eventually().equal(false), + bool().nullable().isValid(null).should.eventually().equal(true), inst .validate() .should.be.rejected() - .then(err => { + .then((err) => { err.errors.length.should.equal(1); err.errors[0].should.contain('required'); }), diff --git a/test/mixed.js b/test/mixed.js index ecddab4ac..cda24df0d 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -9,6 +9,7 @@ import { ref, string, ValidationError, + MixedSchema, } from '../src'; import { ensureSync } from './helpers'; @@ -47,8 +48,8 @@ describe('Mixed Types ', () => { next.sub.should.equal(sub); inst.sub.should.equal(next.sub); - inst.should.be.an.instanceOf(mixed); - next.should.be.an.instanceOf(mixed); + inst.should.be.an.instanceOf(MixedSchema); + next.should.be.an.instanceOf(MixedSchema); return Promise.all([ inst.isValid().should.eventually().equal(true), diff --git a/test/number.js b/test/number.js index 52c509e94..f853ae9c0 100644 --- a/test/number.js +++ b/test/number.js @@ -1,15 +1,15 @@ import * as TestHelpers from './helpers'; -import number from '../src/number'; +import { number, NumberSchema } from '../src'; describe('Number types', function () { - it('is newable', () => { - let schema = new number(); - schema.integer().required(); - }); + // it('is newable', () => { + // let schema = new number(); + // schema.integer().required(); + // }); it('is extensible', () => { - class MyNumber extends number { + class MyNumber extends NumberSchema { foo() { return this; } diff --git a/test/object.js b/test/object.js index 6d56ae939..ed732e7fd 100644 --- a/test/object.js +++ b/test/object.js @@ -9,6 +9,8 @@ import { ref, lazy, reach, + StringSchema, + MixedSchema, } from '../src'; import { expect } from 'chai'; @@ -116,7 +118,7 @@ describe('Object types', () => { }); it('should prevent recursive casting', async () => { - let castSpy = sinon.spy(string.prototype, '_cast'); + let castSpy = sinon.spy(StringSchema.prototype, '_cast'); inst = object({ field: string(), @@ -128,7 +130,7 @@ describe('Object types', () => { castSpy.should.have.been.calledOnce(); - string.prototype._cast.restore(); + StringSchema.prototype._cast.restore(); }); it('should respect strict for nested values', async () => { @@ -153,7 +155,7 @@ describe('Object types', () => { err.message.should.match(/must be a `string` type/); }); - it.only('should respect child schema with strict()', async () => { + it('should respect child schema with strict()', async () => { inst = object({ field: number().strict(), }); @@ -185,9 +187,9 @@ describe('Object types', () => { }); it('should not clone during validating', async function () { - let base = mixed.prototype.clone; + let base = MixedSchema.prototype.clone; - mixed.prototype.clone = function (...args) { + MixedSchema.prototype.clone = function (...args) { if (!this._mutate) throw new Error('should not call clone'); return base.apply(this, args); @@ -206,7 +208,7 @@ describe('Object types', () => { /* ignore */ } finally { //eslint-disable-line - mixed.prototype.clone = base; + MixedSchema.prototype.clone = base; } }); }); diff --git a/yarn.lock b/yarn.lock index 668c4acd4..3cacdbb92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8404,6 +8404,11 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +synchronous-promise@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.13.tgz#9d8c165ddee69c5a6542862b405bc50095926702" + integrity sha512-R9N6uDkVsghHePKh1TEqbnLddO2IY25OcsksyFp/qBe7XYd0PVbKEWxhcdMhpLzE1I6skj5l4aEZ3CRxcbArlA== + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" From 85e899baa00dd6748f7a2271f985b9635a16d2c2 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Sat, 29 Aug 2020 13:14:06 -0400 Subject: [PATCH 02/31] WIP --- .babelrc.js | 2 +- package.json | 9 +- src/util/isAbsent.js | 1 - src/util/isAbsent.ts | 1 + src/util/isSchema.js | 1 - src/util/isSchema.ts | 1 + test/tsconfig.json | 8 + tsconfig.json | 7 + yarn.lock | 2250 ++++++++++++++++++++++++++++++++++++++++-- 9 files changed, 2199 insertions(+), 81 deletions(-) delete mode 100644 src/util/isAbsent.js create mode 100644 src/util/isAbsent.ts delete mode 100644 src/util/isSchema.js create mode 100644 src/util/isSchema.ts create mode 100644 test/tsconfig.json create mode 100644 tsconfig.json diff --git a/.babelrc.js b/.babelrc.js index 1ff99b592..e677b7eb0 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -5,7 +5,7 @@ module.exports = (api) => ({ api.env() !== 'test' ? { ignoreBrowserslistConfig: true, - modules: api.env() === 'modules' ? false : 'commonjs', + modules: api.env() === 'esm' ? false : 'commonjs', } : { target: 'node', diff --git a/package.json b/package.json index c552abda4..6c5fd74fe 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,7 @@ "precommit": "lint-staged", "toc": "doctoc README.md --github", "release": "rollout", - "build": "yarn build:commonjs && yarn build:modules && yarn toc", - "build:commonjs": "babel src --out-dir lib --delete-dir-on-start", - "build:modules": "babel src --out-dir es --delete-dir-on-start --env-name modules", + "build": "yarn 4c build && yarn toc", "prepublishOnly": "yarn build" }, "files": [ @@ -66,7 +64,9 @@ ] }, "devDependencies": { + "@4c/cli": "^2.1.11", "@4c/rollout": "^2.1.10", + "@4c/tsconfig": "^0.3.1", "@babel/cli": "7.10.5", "@babel/core": "7.11.4", "babel-core": "^7.0.0-bridge.0", @@ -96,7 +96,8 @@ "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-size-snapshot": "^0.12.0", "sinon": "^9.0.3", - "sinon-chai": "^3.5.0" + "sinon-chai": "^3.5.0", + "typescript": "^4.0.2" }, "dependencies": { "@babel/runtime": "^7.10.5", diff --git a/src/util/isAbsent.js b/src/util/isAbsent.js deleted file mode 100644 index 35fdc9ceb..000000000 --- a/src/util/isAbsent.js +++ /dev/null @@ -1 +0,0 @@ -export default value => value == null; diff --git a/src/util/isAbsent.ts b/src/util/isAbsent.ts new file mode 100644 index 000000000..7d64a6799 --- /dev/null +++ b/src/util/isAbsent.ts @@ -0,0 +1 @@ +export default (value: any): value is undefined | null => value == null; diff --git a/src/util/isSchema.js b/src/util/isSchema.js deleted file mode 100644 index cdd7e5545..000000000 --- a/src/util/isSchema.js +++ /dev/null @@ -1 +0,0 @@ -export default obj => obj && obj.__isYupSchema__; diff --git a/src/util/isSchema.ts b/src/util/isSchema.ts new file mode 100644 index 000000000..939c64d32 --- /dev/null +++ b/src/util/isSchema.ts @@ -0,0 +1 @@ +export default (obj: any): obj is MixedSchema => obj && obj.__isYupSchema__; diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 000000000..fcdfa1619 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../", + "compilerOptions": { + "types": ["node", "jest"], + "rootDir": "../" + }, + "include": ["../src", "."] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..f6f113548 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@4c/tsconfig/web.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/yarn.lock b/yarn.lock index 3cacdbb92..3a494570f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,21 @@ # yarn lockfile v1 +"@4c/build@^2.2.7": + version "2.2.7" + resolved "https://registry.yarnpkg.com/@4c/build/-/build-2.2.7.tgz#123bfd73cb78c1fda46eecbddf3bbeaf3f31c94b" + integrity sha512-rBpfwNUrcnshfNSLzc2MVrfQw9k8zyqy6EEpuN8nbhOpDKgK6lvoZwUfaV0dTVwNM/kjLhSwhX8H5WB9RiTAYQ== + dependencies: + "@4c/cli-core" "^2.1.5" + "@babel/cli" "^7.8.4" + "@babel/core" "^7.9.0" + "@manypkg/get-packages" "^1.0.0" + cpy "^7.3.0" + execa "^4.0.0" + fs-extra "^9.0.0" + listr "^0.14.3" + typescript "^3.8.3" + "@4c/cli-core@^2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@4c/cli-core/-/cli-core-2.1.5.tgz#1688b2df03f53c9f8720dcce5ccd576e6a34207e" @@ -20,6 +35,21 @@ text-table "^0.2.0" yargs "^15.0.1" +"@4c/cli@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@4c/cli/-/cli-2.1.11.tgz#b3837a4ea1cc22dcd801f560ceedf017c06133bf" + integrity sha512-eO0ZvJ+Fk6wrvaQLoxqYtvQihcAajexREGXG2eG0XM6FcNSEP98vJnPrx40lw+U0037YrKJuIxdFbDsPXJ9OgA== + dependencies: + "@4c/build" "^2.2.7" + "@4c/init" "^3.0.7" + "@4c/intl" "^1.2.19" + "@4c/rollout" "^2.1.10" + "@4c/start" "^2.1.2" + pedantic "^5.0.6" + svg2c "^1.4.17" + ts-doctor "^1.2.2" + yargs "^15.0.1" + "@4c/file-butler@^4.1.7": version "4.1.7" resolved "https://registry.yarnpkg.com/@4c/file-butler/-/file-butler-4.1.7.tgz#e878c78e56a6e37c7b6680460a4b15193f839a5d" @@ -32,6 +62,27 @@ read-pkg-up "^7.0.1" yargs "^15.0.1" +"@4c/init@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@4c/init/-/init-3.0.7.tgz#c336f2a49adb557cb01c873ed7691bb29a9a50c6" + integrity sha512-awfKNqCv1RAFOvN0RS60gSLO9kkLtSl2lA5eYCH2PdVTIZDhq9owS4uk+W4rNoOGVTlc+eV6XpStSp7iOQvQQg== + dependencies: + "@4c/cli-core" "^2.1.5" + find-yarn-workspace-root "^2.0.0" + glob "^7.1.3" + node-plop "^0.25.0" + prettier "^2.0.0" + yargs "^15.0.1" + +"@4c/intl@^1.2.19": + version "1.2.19" + resolved "https://registry.yarnpkg.com/@4c/intl/-/intl-1.2.19.tgz#3381e9543d76174cc6ff416a35c52fc298dd2a7f" + integrity sha512-BNV2KFubYHkeSkFJibWwcZgChZ/hdvRywYrgNcnhStacHhpiQ7se4XXa7JMI15W9QYpJgsov4gRUHaiRe1n9bw== + dependencies: + "@4c/cli-core" "^2.1.5" + chalk "^4.0.0" + glob "^7.1.3" + "@4c/rollout@^2.1.10": version "2.1.10" resolved "https://registry.yarnpkg.com/@4c/rollout/-/rollout-2.1.10.tgz#f7eae1d55ba7ebcd7b28493ba32bbfd3d8be5a67" @@ -52,7 +103,32 @@ rxjs "^6.5.2" semver "^7.0.0" -"@babel/cli@7.10.5": +"@4c/start@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@4c/start/-/start-2.1.2.tgz#7e515dbb764b590d2a42ad5668bf7491aa761811" + integrity sha512-n3n8aFNzktzrqTtjDQZRIBI2wuqOx1tIZMFlFNOM9pV5X6JabzahpcHFbLW9yzJEe+ti7emqaSykoXK56nJe/w== + dependencies: + "@4c/cli-core" "^2.1.5" + "@babel/code-frame" "^7.5.5" + dotenv "^8.1.0" + exists-case "^0.1.0" + fork-ts-checker-webpack-plugin "^5.0.5" + friendly-errors-webpack-plugin "^2.0.0-beta.2" + lodash "^4.17.15" + react-dev-utils "^10.0.0" + webpack-dev-server "^3.8.0" + webpack-log "^3.0.1" + webpack-notifier "^1.8.0" + webpackbar "^4.0.0" + +"@4c/tsconfig@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@4c/tsconfig/-/tsconfig-0.3.1.tgz#90ff0cb4fddde7114ee57eeee912737d017ba0e9" + integrity sha512-FBhSdtjfitTiaq590kpxtcnCJsF1LeahEjl5ZYDhAeHT/1pn7i8aho2R1u5eVfSxgYrNy/MVizRer3DwBJh9UA== + dependencies: + typescript-workspace-plugin "^2.0.1" + +"@babel/cli@7.10.5", "@babel/cli@^7.8.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.10.5.tgz#57df2987c8cf89d0fc7d4b157ec59d7619f1b77a" integrity sha512-j9H9qSf3kLdM0Ao3aGPbGZ73mEA9XazuupcS6cDGWuiyAcANoguhP0r2Lx32H5JGw4sSSoHG3x/mxVnHgvOoyA== @@ -68,7 +144,14 @@ optionalDependencies: chokidar "^2.1.8" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": +"@babel/code-frame@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + dependencies: + "@babel/highlight" "^7.8.3" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== @@ -84,7 +167,7 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@7.11.4": +"@babel/core@7.11.4", "@babel/core@^7.7.0", "@babel/core@^7.9.0": version "7.11.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.4.tgz#4301dfdfafa01eeb97f1896c5501a3f0655d4229" integrity sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg== @@ -347,7 +430,7 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/highlight@^7.10.4": +"@babel/highlight@^7.10.4", "@babel/highlight@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== @@ -977,7 +1060,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.10.4": +"@babel/preset-react@^7.10.4", "@babel/preset-react@^7.7.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf" integrity sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw== @@ -997,6 +1080,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.5.5": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.3.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" @@ -1229,6 +1319,27 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@manypkg/find-root@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@manypkg/find-root/-/find-root-1.1.0.tgz#a62d8ed1cd7e7d4c11d9d52a8397460b5d4ad29f" + integrity sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA== + dependencies: + "@babel/runtime" "^7.5.5" + "@types/node" "^12.7.1" + find-up "^4.1.0" + fs-extra "^8.1.0" + +"@manypkg/get-packages@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@manypkg/get-packages/-/get-packages-1.1.1.tgz#7c7e72d0061ab2e61d2ce4da58ce91290a60ac8d" + integrity sha512-J6VClfQSVgR6958eIDTGjfdCrELy1eT+SHeoSMomnvRQVktZMnEA5edIr5ovRFNw5y+Bk/jyoevPzGYod96mhw== + dependencies: + "@babel/runtime" "^7.5.5" + "@manypkg/find-root" "^1.1.0" + fs-extra "^8.1.0" + globby "^11.0.0" + read-yaml-file "^1.1.0" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1453,6 +1564,13 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/globby@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@types/globby/-/globby-9.1.0.tgz#08e2cf99c64f8e45c6cfbe05e9d8ac763aca6482" + integrity sha512-9du/HCA71EBz7syHRnM4Q/u4Fbx3SyN/Uu+4Of9lyPX4A6Xi+A8VMxvx8j5/CMTfrae2Zwdwg0fAaKvKXfRbAw== + dependencies: + globby "*" + "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" @@ -1460,6 +1578,21 @@ dependencies: "@types/node" "*" +"@types/handlebars@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@types/handlebars/-/handlebars-4.1.0.tgz#3fcce9bf88f85fe73dc932240ab3fb682c624850" + integrity sha512-gq9YweFKNNB1uFK71eRqsd4niVkXrxHugqWFQkeLRJvGjnxsLr16bYtcsG4tOFwmYi0Bax+wCkbf1reUfdl4kA== + dependencies: + handlebars "*" + +"@types/inquirer@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-6.0.1.tgz#ea9b483a81f16f4f0d27d5c8d9d081dfa36c4ee9" + integrity sha512-O9rEHE9iBvYaFAGS0fAlDzqY/3CsOrRKzni4zwnAEce2JrHUEbXAce2Pwwe8ZGzmQkucwSXn1tSiKig37INgfA== + dependencies: + "@types/through" "*" + rxjs ">=6.4.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" @@ -1484,6 +1617,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" + integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -1499,6 +1637,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.29.tgz#46275f028b4e463b9ac5fefc1d08bc66cc193f25" integrity sha512-yo8Qz0ygADGFptISDj3pOC9wXfln/5pQaN/ysDIzOaAWXt73cNHmtEC8zSO2Y+kse/txmwIAJzkYZ5fooaS5DQ== +"@types/node@^12.7.1": + version "12.12.54" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.54.tgz#a4b58d8df3a4677b6c08bfbc94b7ad7a7a5f82d1" + integrity sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1514,6 +1657,11 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.0.tgz#dc85454b953178cc6043df5208b9e949b54a3bc4" integrity sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q== +"@types/q@^1.5.1": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" + integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== + "@types/resolve@0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -1526,6 +1674,13 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/through@*": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" @@ -1743,6 +1898,14 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1771,6 +1934,11 @@ acorn@^7.1.1, acorn@^7.2.0, acorn@^7.3.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== +address@1.1.2, address@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + agent-base@6: version "6.0.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a" @@ -1815,6 +1983,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.2: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + anchor-markdown-header@^0.5.5: version "0.5.7" resolved "https://registry.yarnpkg.com/anchor-markdown-header/-/anchor-markdown-header-0.5.7.tgz#045063d76e6a1f9cd327a57a0126aa0fdec371a7" @@ -1828,7 +2006,7 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" -ansi-colors@^3.2.1: +ansi-colors@^3.0.0, ansi-colors@^3.2.1: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== @@ -1845,6 +2023,11 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: dependencies: type-fest "^0.11.0" +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1936,6 +2119,16 @@ array-find-index@^1.0.1: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -1950,7 +2143,7 @@ array-includes@^3.1.1: es-abstract "^1.17.0" is-string "^1.0.5" -array-union@^1.0.2: +array-union@^1.0.1, array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" dependencies: @@ -2042,6 +2235,11 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + async@^2.5.0: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" @@ -2049,6 +2247,13 @@ async@^2.5.0: dependencies: lodash "^4.17.11" +async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2071,6 +2276,15 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -2214,6 +2428,11 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -2258,6 +2477,39 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + boundary@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/boundary/-/boundary-1.0.1.tgz#4d67dc2602c0cc16dd9bce7ebf87e948290f5812" @@ -2374,6 +2626,16 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.10.0.tgz#f179737913eaf0d2b98e4926ac1ca6a15cbcc6a9" + integrity sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA== + dependencies: + caniuse-lite "^1.0.30001035" + electron-to-chromium "^1.3.378" + node-releases "^1.1.52" + pkg-up "^3.1.0" + browserslist@^4.12.0, browserslist@^4.8.3: version "4.13.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.13.0.tgz#42556cba011e1b0a2775b611cba6a8eca18e940d" @@ -2394,6 +2656,11 @@ buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -2420,7 +2687,12 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= -bytes@^3.1.0: +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0, bytes@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== @@ -2492,6 +2764,14 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -2528,6 +2808,11 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== +caniuse-lite@^1.0.30001035: + version "1.0.30001119" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001119.tgz#99185d04bc00e76a86c9ff731dc5ec8e53aefca1" + integrity sha512-Hpwa4obv7EGP+TjkCh/wVvbtNJewxmtg4yVJBLFnxo35vbPapBr138bUWENkb5j5L9JZJ9RXLn4OrXRG/cecPQ== + caniuse-lite@^1.0.30001093: version "1.0.30001107" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001107.tgz#809360df7a5b3458f627aa46b0f6ed6d5239da9a" @@ -2562,6 +2847,15 @@ chai@^4.2.0: pathval "^1.1.0" type-detect "^4.0.5" +chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2572,15 +2866,6 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" @@ -2597,6 +2882,38 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +change-case@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e" + integrity sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw== + dependencies: + camel-case "^3.0.0" + constant-case "^2.0.0" + dot-case "^2.1.0" + header-case "^1.0.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + no-case "^2.3.2" + param-case "^2.1.0" + pascal-case "^2.0.0" + path-case "^2.1.0" + sentence-case "^2.1.0" + snake-case "^2.1.0" + swap-case "^1.1.0" + title-case "^2.1.0" + upper-case "^1.1.1" + upper-case-first "^1.1.0" + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -2641,6 +2958,21 @@ chokidar@^2.0.4, chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" +chokidar@^3.3.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + chokidar@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" @@ -2673,6 +3005,11 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" +ci-info@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -2741,6 +3078,15 @@ cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -2758,6 +3104,15 @@ co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -2832,6 +3187,16 @@ commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" +comment-json@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-3.0.3.tgz#0cadacd6278602b57b8c51b1814dc5d311d228c4" + integrity sha512-P7XwYkC3qjIK45EAa9c5Y3lR7SMXhJqwFdWg3niAIAcbk3zlpKDdajV8Hyz/Y3sGNn3l+YNMl8A2N/OubSArHg== + dependencies: + core-util-is "^1.0.2" + esprima "^4.0.1" + has-own-prop "^2.0.0" + repeat-string "^1.6.1" + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -2853,6 +3218,26 @@ component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2881,6 +3266,16 @@ confusing-browser-globals@^1.0.9: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +consola@^2.10.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.0.tgz#40fc4eefa4d2f8ef2e2806147f056ea207fcc0e9" + integrity sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ== + console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -2892,6 +3287,14 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +constant-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" + integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY= + dependencies: + snake-case "^2.1.0" + upper-case "^1.1.1" + constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -2900,6 +3303,18 @@ contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + conventional-changelog-angular@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz#299fdd43df5a1f095283ac16aeedfb0a682ecab0" @@ -3056,6 +3471,16 @@ convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, dependencies: safe-buffer "~5.1.1" +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -3079,7 +3504,12 @@ core-js-compat@^3.6.2: browserslist "^4.8.3" semver "7.0.0" -core-util-is@1.0.2, core-util-is@~1.0.0: +core-js@^3.3.2: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" + integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== + +core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3143,6 +3573,15 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-spawn@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -3179,6 +3618,49 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-tree@1.0.0-alpha.39: + version "1.0.0-alpha.39" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" + integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + dependencies: + mdn-data "2.0.6" + source-map "^0.6.1" + +css-what@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39" + integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg== + +csso@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903" + integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== + dependencies: + css-tree "1.0.0-alpha.39" + cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -3242,6 +3724,12 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== +debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -3249,11 +3737,19 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" +debug@^3.1.1, debug@^3.2.5: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: - ms "2.0.0" + ms "^2.1.1" + +debug@~2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.1.3.tgz#ce8ab1b5ee8fbee2bfa3b633cab93d366b63418e" + integrity sha1-zoqxte6PvuK/o7Yzyrk9NmtjQY4= + dependencies: + ms "0.7.0" debuglog@^1.0.1: version "1.0.1" @@ -3292,6 +3788,18 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" +deep-equal@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -3302,6 +3810,14 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -3334,6 +3850,33 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +del@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" + integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA== + dependencies: + globby "^10.0.1" + graceful-fs "^4.2.2" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.1" + p-map "^3.0.0" + rimraf "^3.0.0" + slash "^3.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -3343,7 +3886,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@^1.1.2: +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -3355,11 +3898,29 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +detect-port-alt@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -3386,6 +3947,14 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -3404,6 +3973,26 @@ dirty-chai@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/dirty-chai/-/dirty-chai-2.0.1.tgz#6b2162ef17f7943589da840abc96e75bda01aff3" +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + doctoc@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/doctoc/-/doctoc-1.4.0.tgz#3115aa61d0a92f0abb0672036918ea904f5b9e02" @@ -3468,13 +4057,20 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domutils@^1.5.1: +domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" dependencies: dom-serializer "0" domelementtype "1" +dot-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" + integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4= + dependencies: + no-case "^2.2.0" + dot-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" @@ -3482,6 +4078,11 @@ dot-prop@^3.0.0: dependencies: is-obj "^1.0.0" +dotenv@^8.1.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + duplexer@0.1.1, duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -3502,6 +4103,16 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.378: + version "1.3.555" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.555.tgz#a096716ff77cf8da9a608eb628fd6927869503d2" + integrity sha512-/55x3nF2feXFZ5tdGUOr00TxnUjUgdxhrn+eCJ1FAcoAt+cKQTjQkUC5XF4frMWE1R5sjHk+JueuBalimfe5Pg== + electron-to-chromium@^1.3.488: version "1.3.509" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.509.tgz#830fcb89cd66dc2984d18d794973b99e3f00584c" @@ -3542,11 +4153,21 @@ emoji-regex@~6.1.0: version "6.1.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.3.tgz#ec79a3969b02d2ecf2b72254279bf99bc7a83932" -emojis-list@^3.0.0: +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + encoding@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" @@ -3601,7 +4222,14 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: +error-stack-parser@^2.0.2: + version "2.0.6" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" + integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + dependencies: + stackframe "^1.1.1" + +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: version "1.17.6" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== @@ -3632,15 +4260,20 @@ escalade@^3.0.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^2.0.0: +escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + escodegen@^1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" @@ -3761,7 +4394,7 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint@^7.7.0: +eslint@^7.0.0, eslint@^7.7.0: version "7.7.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073" integrity sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg== @@ -3852,11 +4485,28 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + events@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -3897,6 +4547,13 @@ execa@^4.0.0, execa@^4.0.1: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +exists-case@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/exists-case/-/exists-case-0.1.0.tgz#e169b68cae6a8267de31848c2dca982b560e9912" + integrity sha1-4Wm2jK5qgmfeMYSMLcqYK1YOmRI= + dependencies: + debug "~2.1.1" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -3925,6 +4582,42 @@ expect@^26.4.2: jest-message-util "^26.3.0" jest-regex-util "^26.0.0" +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -3977,6 +4670,18 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-glob@^2.0.2: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + fast-glob@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.6.tgz#a5d5b697ec8deda468d85a74035290a025a95295" @@ -3989,6 +4694,18 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" +fast-glob@^3.0.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-glob@^3.1.1: version "3.2.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" @@ -4024,6 +4741,20 @@ fault@^1.0.1: dependencies: format "^0.2.2" +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.1: + version "0.11.3" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== + dependencies: + websocket-driver ">=0.5.1" + fb-watchman@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" @@ -4067,6 +4798,11 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filesize@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" + integrity sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg== + filesize@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" @@ -4088,6 +4824,19 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -4097,6 +4846,14 @@ find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -4117,14 +4874,6 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - find-versions@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" @@ -4132,6 +4881,13 @@ find-versions@^3.2.0: dependencies: semver-regex "^2.0.0" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -4153,6 +4909,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@^1.0.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4161,6 +4922,37 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" +fork-ts-checker-webpack-plugin@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" + integrity sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ== + dependencies: + babel-code-frame "^6.22.0" + chalk "^2.4.1" + chokidar "^3.3.0" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + +fork-ts-checker-webpack-plugin@^5.0.5: + version "5.1.0" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.1.0.tgz#586fbee24aeea950c53bab529e32017f543e71cf" + integrity sha512-vuKyEjSLGbhQbEr5bifXXOkr9iV73L6n72mHoHIv7okvrf7O7z6RKeplM6C6ATPsukoQivij+Ba1vcptL60Z2g== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -4175,12 +4967,32 @@ format@^0.2.2: resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +friendly-errors-webpack-plugin@^2.0.0-beta.2: + version "2.0.0-beta.2" + resolved "https://registry.yarnpkg.com/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-2.0.0-beta.2.tgz#69987c67c2ed3e93847248f8ba1920f75f6ff45c" + integrity sha512-0x14cdjGx5q0yZc3Cy9sgAF/szWUFx1WxH/IX88UuKbM5Z+7FCk/Z/6hFbXMcz3qqK0mp7WrHKX3cxhUAL2aqQ== + dependencies: + chalk "^2.4.2" + error-stack-parser "^2.0.2" + string-width "^2.0.0" + strip-ansi "^5" + from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -4188,6 +5000,15 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" @@ -4212,6 +5033,11 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: dependencies: minipass "^3.0.0" +fs-monkey@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.1.tgz#4a82f36944365e619f4454d9fff106553067b781" + integrity sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA== + fs-readdir-recursive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" @@ -4387,7 +5213,7 @@ glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -4399,6 +5225,22 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +global-modules@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + globals@^11.1.0: version "11.7.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" @@ -4410,6 +5252,45 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globby@*: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +globby@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globby@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + globby@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.0.tgz#56fd0e9f0d4f8fb0c456f1ab0dee96e1380bc154" @@ -4422,6 +5303,17 @@ globby@^11.0.0: merge2 "^1.3.0" slash "^3.0.0" +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -4436,7 +5328,7 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -4445,7 +5337,7 @@ growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" -gzip-size@^5.1.1: +gzip-size@5.1.1, gzip-size@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== @@ -4453,6 +5345,23 @@ gzip-size@^5.1.1: duplexer "^0.1.1" pify "^4.0.1" +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +handlebars@*, handlebars@^4.4.3: + version "4.7.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" + integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + handlebars@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" @@ -4491,6 +5400,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-own-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-own-prop/-/has-own-prop-2.0.0.tgz#f0f95d58f65804f5d218db32563bb85b8e0417af" + integrity sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ== + has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -4553,6 +5467,14 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +header-case@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" + integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.3" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -4572,6 +5494,16 @@ hosted-git-info@^3.0.2: dependencies: lru-cache "^5.1.1" +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -4579,6 +5511,11 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" +html-entities@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" + integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== + html-escaper@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" @@ -4600,6 +5537,48 @@ http-cache-semantics@^4.0.4: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-parser-js@>=0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77" + integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== + http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -4609,6 +5588,25 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -4678,16 +5676,31 @@ ignore-walk@^3.0.3: dependencies: minimatch "^3.0.4" +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + ignore@^4.0.3, ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.1: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + ignore@^5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== +immer@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" + integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== + import-fresh@^3.0.0, import-fresh@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" @@ -4696,6 +5709,14 @@ import-fresh@^3.0.0, import-fresh@^3.1.0: parent-module "^1.0.0" resolve-from "^4.0.0" +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" @@ -4743,7 +5764,12 @@ inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" -ini@^1.3.2: +inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.2, ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" @@ -4756,6 +5782,25 @@ inquirer-autosubmit-prompt@^0.2.0: inquirer "^6.2.1" rxjs "^6.3.3" +inquirer@7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" + integrity sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + inquirer@^6.2.1: version "6.5.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" @@ -4794,6 +5839,14 @@ inquirer@^7.0.0: strip-ansi "^6.0.0" through "^2.3.6" +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + internal-slot@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3" @@ -4814,11 +5867,21 @@ ip-regex@^2.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -ip@1.1.5: +ip@1.1.5, ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -4842,6 +5905,11 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -4983,6 +6051,13 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= +is-lower-case@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" + integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= + dependencies: + lower-case "^1.1.0" + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -5008,6 +6083,30 @@ is-observable@^1.1.0: dependencies: symbol-observable "^1.1.0" +is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -5027,6 +6126,13 @@ is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" +is-regex@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + is-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" @@ -5038,6 +6144,11 @@ is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" +is-root@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -5071,6 +6182,13 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-upper-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" + integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= + dependencies: + upper-case "^1.1.0" + is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -5094,7 +6212,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -is-wsl@^2.2.0: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -5109,6 +6227,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" +isbinaryfile@^4.0.2: + version "4.0.6" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" + integrity sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5542,6 +6665,11 @@ jest@^26.4.2: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" @@ -5550,6 +6678,14 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.6.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -5619,6 +6755,11 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -5633,6 +6774,13 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179" @@ -5669,6 +6817,11 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4" integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA== +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -5848,6 +7001,15 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== +loader-utils@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + loader-utils@^1.2.3: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" @@ -5961,6 +7123,16 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +loglevel@^1.6.8: + version "1.7.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" + integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== + +loglevelnext@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-3.0.1.tgz#e3e4659c4061c09264f6812c33586dc55a009a04" + integrity sha512-JpjaJhIN1reaSb26SIxDGtE0uc67gPl19OMVHrr+Ggt6b/Vy60jmCtKgQBrygAH0bhRA2nkxgDvM+8QvR8r0YA== + loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -5975,9 +7147,21 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" +lower-case-first@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" + integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= + dependencies: + lower-case "^1.1.2" + +lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" @@ -6063,6 +7247,28 @@ md5.js@^1.3.4: hash-base "^3.0.0" inherits "^2.0.1" +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +mdn-data@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" + integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memfs@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.0.tgz#f9438e622b5acd1daa8a4ae160c496fdd1325b26" + integrity sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A== + dependencies: + fs-monkey "1.0.1" + memory-fs@^0.4.0, memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -6124,6 +7330,11 @@ meow@^5.0.0: trim-newlines "^2.0.0" yargs-parser "^10.0.0" +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -6134,6 +7345,16 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -6172,6 +7393,11 @@ mime-db@1.43.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== +mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.26" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" @@ -6179,6 +7405,23 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.43.0" +mime-types@~2.1.17, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.4.4: + version "2.4.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" + integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -6196,7 +7439,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -6320,7 +7563,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -6355,15 +7598,38 @@ move-file@^2.0.0: dependencies: path-exists "^4.0.0" +ms@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" + integrity sha1-hlvpTC5zl62KV9pqYzpuLzB5i4M= + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + ms@^2.0.0, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -6378,6 +7644,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nanoid@^2.0.3: + version "2.1.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" + integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6398,11 +7669,21 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + neo-async@^2.5.0, neo-async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" @@ -6423,6 +7704,18 @@ nise@^4.0.4: just-extend "^4.0.2" path-to-regexp "^1.7.0" +no-case@^2.2.0, no-case@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== + node-gyp@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-6.1.0.tgz#64e31c61a4695ad304c1d5b82cf6b7c79cc79f3f" @@ -6478,6 +7771,17 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= +node-notifier@^5.1.2: + version "5.4.3" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" + integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + node-notifier@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620" @@ -6490,7 +7794,26 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" -node-releases@^1.1.58: +node-plop@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/node-plop/-/node-plop-0.25.0.tgz#1d3bdf286bf74baabb6755b4cef8c6ab37110180" + integrity sha512-OFvnTsDw9nxNdLrYcveJhU2Hnzg+AxOz6xBk8uXsi0vCOSP7Rng98pdgfsuZKyCN+qrc+/fSwlNC5hkXhJ6gww== + dependencies: + "@types/globby" "^9.1.0" + "@types/handlebars" "^4.1.0" + "@types/inquirer" "6.0.1" + change-case "^3.1.0" + core-js "^3.3.2" + del "^5.1.0" + globby "^10.0.1" + handlebars "^4.4.3" + inquirer "^7.0.0" + isbinaryfile "^4.0.2" + lodash.get "^4.4.2" + mkdirp "^0.5.1" + resolve "^1.12.0" + +node-releases@^1.1.52, node-releases@^1.1.58: version "1.1.60" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== @@ -6608,6 +7931,13 @@ npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" +nth-check@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -6639,6 +7969,14 @@ object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== +object-is@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" + integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -6679,13 +8017,21 @@ object.fromentries@^2.0.2: function-bind "^1.1.1" has "^1.0.3" +object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" dependencies: isobject "^3.0.1" -object.values@^1.1.1: +object.values@^1.1.0, object.values@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== @@ -6695,6 +8041,23 @@ object.values@^1.1.1: function-bind "^1.1.1" has "^1.0.3" +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -6714,11 +8077,26 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +open@^7.0.2: + version "7.2.1" + resolved "https://registry.yarnpkg.com/open/-/open-7.2.1.tgz#07b0ade11a43f2a8ce718480bdf3d7563a095195" + integrity sha512-xbYCJib4spUdmcs0g/2mK1nKo/jO2T7INClWd/beL7PFkXRWgr8B23ssDHX/USPn2M2IjDR5UdpYs6I67SnTSA== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + opencollective-postinstall@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -6764,6 +8142,13 @@ ora@^4.0.2: strip-ansi "^6.0.0" wcwidth "^1.0.1" +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -6832,6 +8217,13 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -6839,6 +8231,13 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -6884,6 +8283,13 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" +param-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -6946,6 +8352,19 @@ parse5@5.1.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e" + integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4= + dependencies: + camel-case "^3.0.0" + upper-case-first "^1.1.0" + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -6955,6 +8374,13 @@ path-browserify@0.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== +path-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5" + integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU= + dependencies: + no-case "^2.2.0" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -6978,6 +8404,11 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -6991,6 +8422,11 @@ path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + path-to-regexp@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" @@ -7037,6 +8473,16 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +pedantic@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/pedantic/-/pedantic-5.0.6.tgz#b4dabfd009ccab7478d11a91ac1d92532d3c1adb" + integrity sha512-vAeN8jgK6Og/zMZa9SpjC5OHqyvpi3rBamKuOpVNRifFMF6pVZyUA63ZbQaJUa9SddpN3uXooIjNYZ4tllRT6w== + dependencies: + "@4c/cli-core" "^2.1.5" + eslint "^7.0.0" + prettier "^2.0.0" + yargs "^15.0.1" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -7096,6 +8542,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@3.1.0, pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + platform@^1.3.3: version "1.3.5" resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" @@ -7107,6 +8560,15 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" +portfinder@^1.0.26: + version "1.0.28" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" + integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.5" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -7120,6 +8582,11 @@ prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" +prettier@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6" + integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw== + prettier@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.0.tgz#5a9789f767a243118c60f3e56d95cb6544914fbb" @@ -7135,6 +8602,11 @@ pretty-format@^26.4.2: ansi-styles "^4.0.0" react-is "^16.12.0" +pretty-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" + integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== + private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -7185,6 +8657,14 @@ property-expr@^2.0.2: resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.2.tgz#fff2a43919135553a3bc2fdd94bdb841965b2330" integrity sha512-bc/5ggaYZxNkFKj374aLbEDqVADdYaLcFo8XBkishUWbaAdjlphaBFns9TvRA2pUseVL/wMFmui9X3IdNDU37g== +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -7238,11 +8718,16 @@ punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" -q@^1.5.1: +q@^1.1.2, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -7256,6 +8741,11 @@ querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -7274,6 +8764,56 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-dev-utils@^10.0.0: + version "10.2.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19" + integrity sha512-XxTbgJnYZmxuPtY3y/UV0D8/65NKkmaia4rXzViknVnZeVlklSh8u6TnaEYPfAi/Gh1TP4mEOXHI6jQOPbeakQ== + dependencies: + "@babel/code-frame" "7.8.3" + address "1.1.2" + browserslist "4.10.0" + chalk "2.4.2" + cross-spawn "7.0.1" + detect-port-alt "1.1.6" + escape-string-regexp "2.0.0" + filesize "6.0.1" + find-up "4.1.0" + fork-ts-checker-webpack-plugin "3.1.1" + global-modules "2.0.0" + globby "8.0.2" + gzip-size "5.1.1" + immer "1.10.0" + inquirer "7.0.4" + is-root "2.1.0" + loader-utils "1.2.3" + open "^7.0.2" + pkg-up "3.1.0" + react-error-overlay "^6.0.7" + recursive-readdir "2.2.2" + shell-quote "1.7.2" + strip-ansi "6.0.0" + text-table "0.2.0" + +react-error-overlay@^6.0.7: + version "6.0.7" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" + integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== + react-is@^16.12.0, react-is@^16.8.1: version "16.13.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" @@ -7353,6 +8893,16 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +read-yaml-file@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-yaml-file/-/read-yaml-file-1.1.0.tgz#9362bbcbdc77007cc8ea4519fe1c0b821a7ce0d8" + integrity sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.6.1" + pify "^4.0.1" + strip-bom "^3.0.0" + "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" @@ -7366,7 +8916,7 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.0.2: +"readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.0.6: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -7401,6 +8951,13 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -7448,7 +9005,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.3.0: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== @@ -7588,6 +9145,18 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -7595,6 +9164,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -7640,6 +9214,11 @@ retry@^0.10.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -7757,6 +9336,13 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rxjs@>=6.4.0: + version "6.6.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== + dependencies: + tslib "^1.9.0" + rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.2, rxjs@^6.5.3, rxjs@^6.5.5: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" @@ -7764,15 +9350,20 @@ rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.2, rxjs@^6.5.3, rxjs@^6.5.5: dependencies: tslib "^1.9.0" +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-buffer@>=5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -7798,6 +9389,11 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" +sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + saxes@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -7805,6 +9401,15 @@ saxes@^5.0.0: dependencies: xmlchars "^2.2.0" +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -7814,6 +9419,18 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +selfsigned@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== + dependencies: + node-forge "0.9.0" + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -7843,11 +9460,61 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +sentence-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" + integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ= + dependencies: + no-case "^2.2.0" + upper-case-first "^1.1.2" + serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -7874,6 +9541,16 @@ setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -7903,6 +9580,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -7942,6 +9624,11 @@ sisteransi@^1.0.4: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" integrity sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig== +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + slash@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" @@ -7987,6 +9674,13 @@ smart-buffer@^4.1.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -8014,6 +9708,27 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.20: + version "0.3.20" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" + integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.4.0" + websocket-driver "0.6.5" + socks-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" @@ -8097,6 +9812,29 @@ spdx-license-ids@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -8150,6 +9888,11 @@ ssri@^8.0.0: dependencies: minipass "^3.1.1" +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + stack-utils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" @@ -8157,6 +9900,11 @@ stack-utils@^2.0.2: dependencies: escape-string-regexp "^2.0.0" +stackframe@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" + integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== + state-toggle@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a" @@ -8169,6 +9917,18 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +std-env@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" + integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== + dependencies: + ci-info "^1.6.0" + stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -8223,14 +9983,14 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^3.0.0: +string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== @@ -8298,6 +10058,13 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" +strip-ansi@6.0.0, strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -8310,20 +10077,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.1.0: +strip-ansi@^5, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -8380,6 +10140,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + supports-color@^7.0.0, supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" @@ -8395,6 +10162,45 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +svg2c@^1.4.17: + version "1.4.17" + resolved "https://registry.yarnpkg.com/svg2c/-/svg2c-1.4.17.tgz#c8d649429efb588b810cec89897b94eb5a6e470c" + integrity sha512-6xcXP7KFm2OUjUNK07MJU5uZujgI5Olbp+F2zfBBLE7Icj8869vtz7eve6M6Aea6McePJpvX70/mJk2yBGpz4A== + dependencies: + "@4c/cli-core" "^2.1.5" + "@babel/core" "^7.7.0" + "@babel/preset-react" "^7.7.0" + js-yaml "^3.13.1" + lodash "^4.17.11" + svgo "^1.2.2" + +svgo@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +swap-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" + integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= + dependencies: + lower-case "^1.1.1" + upper-case "^1.1.1" + symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -8500,7 +10306,7 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-table@^0.2.0: +text-table@0.2.0, text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -8528,12 +10334,25 @@ through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + timers-browserify@^2.0.4: version "2.0.10" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" dependencies: setimmediate "^1.0.4" +title-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" + integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o= + dependencies: + no-case "^2.2.0" + upper-case "^1.0.3" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -8581,6 +10400,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + toposort@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" @@ -8640,6 +10464,19 @@ trough@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" +ts-doctor@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ts-doctor/-/ts-doctor-1.2.2.tgz#9620d11717bac66be5b21b9591d0c65772a59ad2" + integrity sha512-vX1ZdK13yzSXeUon2rtMMtHQ8tyHevWeq6RAYq9ng7aIusAvhydSiIQJ8C7PT501knV50eNulzqBaE0Q+k4D2Q== + dependencies: + "@4c/cli-core" "^2.1.5" + "@manypkg/get-packages" "^1.0.0" + comment-json "^3.0.2" + lodash "^4.17.15" + prettier "^2.0.0" + typescript "^3.8.3" + yargs "^15.1.0" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -8708,6 +10545,14 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -8719,6 +10564,21 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +typescript-workspace-plugin@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/typescript-workspace-plugin/-/typescript-workspace-plugin-2.0.1.tgz#3d88be1c35a7fdf2c0160c8cf569ca8993439a12" + integrity sha512-xjIYNFlPIA7IWXvnOFJoAeHPbPJSo0AiQDCRJzaAp3+xZwz6maTgeRLB0oEHVtCqz4Q1CDN6U9kh/2z8sxdDBQ== + +typescript@^3.8.3: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== + +typescript@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== + uglify-js@^3.1.4: version "3.4.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" @@ -8819,11 +10679,26 @@ unist-util-visit@^1.1.0: dependencies: unist-util-visit-parents "^2.0.0" +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + universalify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -8840,6 +10715,18 @@ update-section@^0.3.0: version "0.3.3" resolved "https://registry.yarnpkg.com/update-section/-/update-section-0.3.3.tgz#458f17820d37820dc60e20b86d94391b00123158" +upper-case-first@^1.1.0, upper-case-first@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= + dependencies: + upper-case "^1.1.1" + +upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -8851,6 +10738,14 @@ urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -8866,6 +10761,16 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -8879,7 +10784,12 @@ util@^0.11.0: dependencies: inherits "2.0.3" -uuid@^3.3.2: +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -8917,6 +10827,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -8989,6 +10904,13 @@ watchpack@^1.6.1: chokidar "^3.4.0" watchpack-chokidar2 "^2.0.0" +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -9005,6 +10927,82 @@ webidl-conversions@^6.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webpack-dev-middleware@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@^3.8.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" + integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.3.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.8" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.26" + schema-utils "^1.0.0" + selfsigned "^1.10.7" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.20" + sockjs-client "1.4.0" + spdy "^4.0.2" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "^13.3.2" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-log@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-3.0.1.tgz#647c42231b6f74d7cc3c3a66510370e635d066ea" + integrity sha512-mX/6BJPPpxco6BGCFZJ96NjgnwBrLQx6d7Kxe1PaJ7KvjI3LFmJK9QgRPCAr9tXrPVawPN1cuM8hJ2Vadnwm+Q== + dependencies: + chalk "^2.4.2" + loglevelnext "^3.0.1" + nanoid "^2.0.3" + +webpack-notifier@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/webpack-notifier/-/webpack-notifier-1.8.0.tgz#994bdde0fcefc5f1a92b6d91353c8152ddd9c583" + integrity sha512-I6t76NoPe5DZCCm5geELmDV2wlJ89LbU425uN6T2FG8Ywrrt1ZcUMz6g8yWGNg4pttqTPFQJYUPjWAlzUEQ+cQ== + dependencies: + node-notifier "^5.1.2" + object-assign "^4.1.0" + strip-ansi "^3.0.1" + webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" @@ -9042,6 +11040,41 @@ webpack@^4.43.0: watchpack "^1.6.1" webpack-sources "^1.4.1" +webpackbar@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-4.0.0.tgz#ee7a87f16077505b5720551af413c8ecd5b1f780" + integrity sha512-k1qRoSL/3BVuINzngj09nIwreD8wxV4grcuhHTD8VJgUbGcy8lQSPqv+bM00B7F+PffwIsQ8ISd4mIwRbr23eQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + consola "^2.10.0" + figures "^3.0.0" + pretty-time "^1.1.0" + std-env "^2.2.1" + text-table "^0.2.0" + wrap-ansi "^6.0.0" + +websocket-driver@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= + dependencies: + websocket-extensions ">=0.1.1" + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -9071,7 +11104,7 @@ which-pm-runs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" -which@^1.2.9, which@^1.3.1: +which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: @@ -9103,6 +11136,11 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" @@ -9114,6 +11152,13 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + wrap-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" @@ -9122,7 +11167,16 @@ wrap-ansi@^3.0.1: string-width "^2.1.1" strip-ansi "^4.0.0" -wrap-ansi@^6.2.0: +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^6.0.0, wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== @@ -9152,6 +11206,13 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" +ws@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + ws@^7.2.3: version "7.2.5" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.5.tgz#abb1370d4626a5a9cd79d8de404aa18b3465d10d" @@ -9203,7 +11264,15 @@ yargs-parser@^10.0.0: dependencies: camelcase "^4.1.0" -yargs-parser@^18.1.1: +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^18.1.1, yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -9211,6 +11280,22 @@ yargs-parser@^18.1.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yargs@^15.0.1, yargs@^15.3.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" @@ -9227,3 +11312,20 @@ yargs@^15.0.1, yargs@^15.3.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.1" + +yargs@^15.1.0: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" From 46bfbabf1f24d68261fd52372ad581ec32a9f506 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 31 Aug 2020 11:57:28 -0400 Subject: [PATCH 03/31] WIP --- .babelrc.js | 3 +- package.json | 4 +- src/{Condition.js => Condition.ts} | 37 ++- src/Lazy.js | 36 --- src/Lazy.ts | 55 +++++ src/{Reference.js => Reference.ts} | 28 ++- src/Schema.ts | 58 +++++ src/ValidationError.js | 44 ---- src/ValidationError.ts | 66 ++++++ src/array.js | 199 ---------------- src/array.ts | 214 +++++++++++++++++ src/{boolean.js => boolean.ts} | 11 +- src/{date.js => date.ts} | 25 +- src/globals.d.ts | 10 + src/index.js | 66 ------ src/index.ts | 53 +++++ src/{locale.js => locale.ts} | 60 ++++- src/{mixed.js => mixed.ts} | 219 +++++++++++++----- src/{number.js => number.ts} | 34 +-- src/{object.js => object.ts} | 124 +++++----- src/{string.js => string.ts} | 53 +++-- src/types.ts | 60 +++++ src/util/async.js | 52 ----- src/util/async.ts | 14 ++ ...reateValidation.js => createValidation.ts} | 80 ++++++- src/util/inherits.js | 12 - src/util/isSchema.ts | 4 +- src/util/{prependDeep.js => prependDeep.ts} | 19 +- src/util/{printValue.js => printValue.ts} | 8 +- src/util/{reach.js => reach.ts} | 10 +- src/util/{runTests.js => runTests.ts} | 32 ++- src/util/sortByKeyOrder.js | 17 -- src/util/sortByKeyOrder.ts | 19 ++ src/util/{sortFields.js => sortFields.ts} | 14 +- src/util/toArray.ts | 3 + test/mixed.js | 7 + test/yup.js | 8 +- yarn.lock | 31 ++- 38 files changed, 1133 insertions(+), 656 deletions(-) rename src/{Condition.js => Condition.ts} (56%) delete mode 100644 src/Lazy.js create mode 100644 src/Lazy.ts rename src/{Reference.js => Reference.ts} (67%) create mode 100644 src/Schema.ts delete mode 100644 src/ValidationError.js create mode 100644 src/ValidationError.ts delete mode 100644 src/array.js create mode 100644 src/array.ts rename src/{boolean.js => boolean.ts} (84%) rename src/{date.js => date.ts} (74%) create mode 100644 src/globals.d.ts delete mode 100644 src/index.js create mode 100644 src/index.ts rename src/{locale.js => locale.ts} (59%) rename src/{mixed.js => mixed.ts} (72%) rename src/{number.js => number.ts} (76%) rename src/{object.js => object.ts} (72%) rename src/{string.js => string.ts} (80%) create mode 100644 src/types.ts delete mode 100644 src/util/async.js create mode 100644 src/util/async.ts rename src/util/{createValidation.js => createValidation.ts} (52%) delete mode 100644 src/util/inherits.js rename src/util/{prependDeep.js => prependDeep.ts} (57%) rename src/util/{printValue.js => printValue.ts} (87%) rename src/util/{reach.js => reach.ts} (82%) rename src/util/{runTests.js => runTests.ts} (52%) delete mode 100644 src/util/sortByKeyOrder.js create mode 100644 src/util/sortByKeyOrder.ts rename src/util/{sortFields.js => sortFields.ts} (66%) create mode 100644 src/util/toArray.ts diff --git a/.babelrc.js b/.babelrc.js index e677b7eb0..74c6ca14c 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -10,10 +10,11 @@ module.exports = (api) => ({ : { target: 'node', - debug: true, + // debug: true, targets: { node: 'current' }, }, ], + '@babel/preset-typescript', ], plugins: [ api.env() === 'modules' && [ diff --git a/package.json b/package.json index 6c5fd74fe..f23c00af3 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "roots": [ "test" ], - "testRegex": "\\.js", + "testRegex": "\\.(j|t)s$", "testPathIgnorePatterns": [ "helpers\\.js" ] @@ -69,6 +69,7 @@ "@4c/tsconfig": "^0.3.1", "@babel/cli": "7.10.5", "@babel/core": "7.11.4", + "@babel/preset-typescript": "^7.10.4", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.3.0", @@ -101,6 +102,7 @@ }, "dependencies": { "@babel/runtime": "^7.10.5", + "@types/lodash": "^4.14.161", "lodash": "^4.17.15", "lodash-es": "^4.17.11", "property-expr": "^2.0.2", diff --git a/src/Condition.js b/src/Condition.ts similarity index 56% rename from src/Condition.js rename to src/Condition.ts index b3a9ba599..951bbf678 100644 --- a/src/Condition.js +++ b/src/Condition.ts @@ -1,8 +1,35 @@ import has from 'lodash/has'; import isSchema from './util/isSchema'; +import Reference from './Reference'; +import Schema from './Schema'; -class Condition { - constructor(refs, options) { +export interface ConditionBuilder { + (this: T, value: any, schema: T): T; + (v1: any, v2: any, schema: T): T; + (v1: any, v2: any, v3: any, schema: T): T; + (v1: any, v2: any, v3: any, v4: any, schema: T): T; +} + +export type ConditionConfig = { + is: any | ((...values: any[]) => boolean); + then?: T | ((schema: T) => T); + otherwise?: T | ((schema: T) => T); +}; + +export type ConditionOptions = + | ConditionBuilder + | ConditionConfig; + +export type ResolveOptions = { + value?: any; + parent?: any; + context?: any; +}; + +class Condition { + fn: ConditionBuilder; + + constructor(public refs: Reference[], options: ConditionOptions) { this.refs = refs; if (typeof options === 'function') { @@ -23,9 +50,9 @@ class Condition { let check = typeof is === 'function' ? is - : (...values) => values.every((value) => value === is); + : (...values: any[]) => values.every((value) => value === is); - this.fn = function (...args) { + this.fn = function (...args: any[]) { let options = args.pop(); let schema = args.pop(); let branch = check(...args) ? then : otherwise; @@ -36,7 +63,7 @@ class Condition { }; } - resolve(base, options) { + resolve(base: T, options: ResolveOptions) { let values = this.refs.map((ref) => ref.getValue(options?.value, options?.parent, options?.context), ); diff --git a/src/Lazy.js b/src/Lazy.js deleted file mode 100644 index 78ad7c535..000000000 --- a/src/Lazy.js +++ /dev/null @@ -1,36 +0,0 @@ -import isSchema from './util/isSchema'; - -class Lazy { - constructor(mapFn) { - this._resolve = (value, options) => { - let schema = mapFn(value, options); - - if (!isSchema(schema)) - throw new TypeError('lazy() functions must return a valid schema'); - - return schema.resolve(options); - }; - } - resolve(options) { - return this._resolve(options.value, options); - } - cast(value, options) { - return this._resolve(value, options).cast(value, options); - } - validate(value, options, maybeCb) { - return this._resolve(value, options).validate(value, options, maybeCb); - } - validateSync(value, options) { - return this._resolve(value, options).validateSync(value, options); - } - validateAt(path, value, options) { - return this._resolve(value, options).validateAt(path, value, options); - } - validateSyncAt(path, value, options) { - return this._resolve(value, options).validateSyncAt(path, value, options); - } -} - -Lazy.prototype.__isYupSchema__ = true; - -export default Lazy; diff --git a/src/Lazy.ts b/src/Lazy.ts new file mode 100644 index 000000000..9be3c3190 --- /dev/null +++ b/src/Lazy.ts @@ -0,0 +1,55 @@ +import isSchema from './util/isSchema'; +import Schema, { CastOptions } from './Schema'; +import { Callback, ValidateOptions } from './types'; +import { ResolveOptions } from './Condition'; + +export type LazyBuilder = ( + value: any, + options: ResolveOptions, +) => T; + +export function create(builder: LazyBuilder) { + return new Lazy(builder); +} + +class Lazy implements Schema { + type = 'lazy' as const; + + __isYupSchema__ = true; + + constructor(private builder: LazyBuilder) {} + + private _resolve = (value: any, options: ResolveOptions) => { + let schema = this.builder(value, options); + + if (!isSchema(schema)) + throw new TypeError('lazy() functions must return a valid schema'); + + return schema.resolve(options); + }; + + resolve(options: ResolveOptions) { + return this._resolve(options.value, options); + } + cast(value: any, options: CastOptions) { + return this._resolve(value, options).cast(value, options); + } + validate(value: any, options: ValidateOptions, maybeCb?: Callback) { + return this._resolve(value, options).validate(value, options, maybeCb); + } + + validateSync(value: any, options: ValidateOptions) { + return this._resolve(value, options).validateSync(value, options); + } + validateAt(path: string, value: any, options: ValidateOptions) { + return this._resolve(value, options).validateAt(path, value, options); + } + validateSyncAt(path: string, value: any, options: ValidateOptions) { + return this._resolve(value, options).validateSyncAt(path, value, options); + } + describe() { + return null as any; + } +} + +export default Lazy; diff --git a/src/Reference.js b/src/Reference.ts similarity index 67% rename from src/Reference.js rename to src/Reference.ts index 58e65a134..d6c0d8af4 100644 --- a/src/Reference.js +++ b/src/Reference.ts @@ -5,8 +5,27 @@ const prefixes = { value: '.', }; +export type ReferenceOptions = { + map?: (value: unknown) => unknown; +}; + +export function create(key: string, options: ReferenceOptions) { + return new Reference(key, options); +} + export default class Reference { - constructor(key, options = {}) { + readonly key: string; + readonly isContext: boolean; + readonly isValue: boolean; + readonly isSibling: boolean; + readonly path: any; + + readonly getter: (data: unknown) => unknown; + readonly map?: (value: unknown) => unknown; + + readonly __isYupRef!: boolean; + + constructor(key: string, options: ReferenceOptions = {}) { if (typeof key !== 'string') throw new TypeError('ref must be a string, got: ' + key); @@ -29,7 +48,7 @@ export default class Reference { this.map = options.map; } - getValue(value, parent, context) { + getValue(value: any, parent?: {}, context?: {}) { let result = this.isContext ? context : this.isValue ? value : parent; if (this.getter) result = this.getter(result || {}); @@ -46,7 +65,7 @@ export default class Reference { * @param {Object=} options.context * @param {Object=} options.parent */ - cast(value, options) { + cast(value: any, options?: { parent?: {}; context?: {} }) { return this.getValue(value, options?.parent, options?.context); } @@ -65,9 +84,10 @@ export default class Reference { return `Ref(${this.key})`; } - static isRef(value) { + static isRef(value: any): value is Reference { return value && value.__isYupRef; } } +// @ts-ignore Reference.prototype.__isYupRef = true; diff --git a/src/Schema.ts b/src/Schema.ts new file mode 100644 index 000000000..a3a15d6f6 --- /dev/null +++ b/src/Schema.ts @@ -0,0 +1,58 @@ +import { ResolveOptions } from './Condition'; +import { ValidateOptions, Callback, MessageParams } from './types'; +import { Params } from './ValidationError'; + +export interface CastOptions { + parent?: any; + context?: {}; + assert?: boolean; + // XXX: should be private? + path?: string; +} + +export interface SchemaRefDescription { + type: 'ref'; + key: string; +} + +export interface SchemaInnerTypeDescription extends SchemaDescription { + innerType?: SchemaFieldDescription; +} + +export interface SchemaObjectDescription extends SchemaDescription { + fields: Record; +} + +export type SchemaFieldDescription = + | SchemaDescription + | SchemaRefDescription + | SchemaObjectDescription + | SchemaInnerTypeDescription; + +export interface SchemaDescription { + type: string; + label?: string; + meta: object; + oneOf: unknown[]; + notOneOf: unknown[]; + tests: Array<{ name?: string; params: MessageParams & Params }>; +} + +export default interface Schema { + __isYupSchema__: boolean; + type: string; + // cast(value: any): any; + // validate(value: any): any; + validate(value: any, options: ValidateOptions): Promise; + describe(): any; + validate( + value: any, + options: ValidateOptions | undefined, + callback: Callback, + ): void; + + resolve(options: ResolveOptions): any; + cast(value: any, options?: CastOptions): any; + + describe(): SchemaDescription; +} diff --git a/src/ValidationError.js b/src/ValidationError.js deleted file mode 100644 index aee2c80e1..000000000 --- a/src/ValidationError.js +++ /dev/null @@ -1,44 +0,0 @@ -import printValue from './util/printValue'; - -let strReg = /\$\{\s*(\w+)\s*\}/g; - -export default function ValidationError(errors, value, field, type) { - this.name = 'ValidationError'; - this.value = value; - this.path = field; - this.type = type; - this.errors = []; - this.inner = []; - - if (errors) - [].concat(errors).forEach((err) => { - this.errors = this.errors.concat(err.errors || err); - - if (err.inner) - this.inner = this.inner.concat(err.inner.length ? err.inner : err); - }); - - this.message = - this.errors.length > 1 - ? `${this.errors.length} errors occurred` - : this.errors[0]; - - if (Error.captureStackTrace) Error.captureStackTrace(this, ValidationError); -} - -ValidationError.prototype = Object.create(Error.prototype); -ValidationError.prototype.constructor = ValidationError; - -ValidationError.isError = function (err) { - return err && err.name === 'ValidationError'; -}; - -ValidationError.formatError = function (message, params) { - params.path = params.label || params.path || 'this'; - - if (typeof message === 'string') - return message.replace(strReg, (_, key) => printValue(params[key])); - if (typeof message === 'function') return message(params); - - return message; -}; diff --git a/src/ValidationError.ts b/src/ValidationError.ts new file mode 100644 index 000000000..a715b761e --- /dev/null +++ b/src/ValidationError.ts @@ -0,0 +1,66 @@ +import printValue from './util/printValue'; +import toArray from './util/toArray'; + +let strReg = /\$\{\s*(\w+)\s*\}/g; + +type Params = Record; + +export default class ValidationError extends Error { + value: any; + path?: string; + type?: string; + errors: string[]; + + params?: Params; + + inner: ValidationError[]; + + static formatError( + message: string | ((params: Params) => string) | unknown, + params: Params, + ) { + params.path = params.label || params.path || 'this'; + + if (typeof message === 'string') + return message.replace(strReg, (_, key) => printValue(params[key])); + if (typeof message === 'function') return message(params); + + return message; + } + static isError(err: any): err is ValidationError { + return err && err.name === 'ValidationError'; + } + + constructor( + errorOrErrors: string | ValidationError | ValidationError[], + value?: any, + field?: string, + type?: string, + ) { + super(); + + this.name = 'ValidationError'; + this.value = value; + this.path = field; + this.type = type; + + this.errors = []; + this.inner = []; + + toArray(errorOrErrors).forEach((err) => { + if (ValidationError.isError(err)) { + this.errors.push(...err.errors); + this.inner = this.inner.concat(err.inner.length ? err.inner : err); + } else { + this.errors.push(err); + } + }); + + this.message = + this.errors.length > 1 + ? `${this.errors.length} errors occurred` + : this.errors[0]; + + if (Error.captureStackTrace) Error.captureStackTrace(this, ValidationError); + } +} diff --git a/src/array.js b/src/array.js deleted file mode 100644 index 50e53e393..000000000 --- a/src/array.js +++ /dev/null @@ -1,199 +0,0 @@ -import isAbsent from './util/isAbsent'; -import isSchema from './util/isSchema'; -import printValue from './util/printValue'; -import MixedSchema from './mixed'; -import { array as locale } from './locale'; -import runTests from './util/runTests'; - -export default class ArraySchema extends MixedSchema { - static create(type) { - return new ArraySchema(type); - } - - constructor(type) { - super({ type: 'array' }); - - // `undefined` specifically means uninitialized, as opposed to - // "no subtype" - this._subType = undefined; - this.innerType = undefined; - - this.withMutation(() => { - this.transform(function (values) { - if (typeof values === 'string') - try { - values = JSON.parse(values); - } catch (err) { - values = null; - } - - return this.isType(values) ? values : null; - }); - - if (type) this.of(type); - }); - } - - _typeCheck(v) { - return Array.isArray(v); - } - - _cast(_value, _opts) { - const value = MixedSchema.prototype._cast.call(this, _value, _opts); - - //should ignore nulls here - if (!this._typeCheck(value) || !this.innerType) return value; - - let isChanged = false; - const castArray = value.map((v, idx) => { - const castElement = this.innerType.cast(v, { - ..._opts, - path: `${_opts.path || ''}[${idx}]`, - }); - if (castElement !== v) { - isChanged = true; - } - - return castElement; - }); - - return isChanged ? castArray : value; - } - - _validate(_value, options = {}, callback) { - let errors = []; - let sync = options.sync; - let path = options.path; - let innerType = this.innerType; - let endEarly = this._option('abortEarly', options); - let recursive = this._option('recursive', options); - - let originalValue = - options.originalValue != null ? options.originalValue : _value; - - MixedSchema.prototype._validate.call( - this, - _value, - options, - (err, value) => { - if (err) { - if (endEarly) return void callback(err); - errors.push(err); - value = err.value; - } - - if (!recursive || !innerType || !this._typeCheck(value)) { - callback(errors[0] || null, value); - return; - } - - originalValue = originalValue || value; - - // #950 Ensure that sparse array empty slots are validated - let tests = new Array(value.length); - for (let idx = 0; idx < value.length; idx++) { - let item = value[idx]; - let path = `${options.path || ''}[${idx}]`; - - // object._validate note for isStrict explanation - let innerOptions = { - ...options, - path, - strict: true, - parent: value, - index: idx, - originalValue: originalValue[idx], - }; - - tests[idx] = (_, cb) => - innerType.validate - ? innerType.validate(item, innerOptions, cb) - : cb(null); - } - - runTests( - { - sync, - path, - value, - errors, - endEarly, - tests, - }, - callback, - ); - }, - ); - } - - _isPresent(value) { - return ( - MixedSchema.prototype._isPresent.call(this, value) && value.length > 0 - ); - } - - of(schema) { - var next = this.clone(); - - if (schema !== false && !isSchema(schema)) - throw new TypeError( - '`array.of()` sub-schema must be a valid yup schema, or `false` to negate a current sub-schema. ' + - 'not: ' + - printValue(schema), - ); - - next._subType = schema; - next.innerType = schema; - - return next; - } - - min(min, message) { - message = message || locale.min; - - return this.test({ - message, - name: 'min', - exclusive: true, - params: { min }, - test(value) { - return isAbsent(value) || value.length >= this.resolve(min); - }, - }); - } - - max(max, message) { - message = message || locale.max; - return this.test({ - message, - name: 'max', - exclusive: true, - params: { max }, - test(value) { - return isAbsent(value) || value.length <= this.resolve(max); - }, - }); - } - - ensure() { - return this.default(() => []).transform((val, original) => { - // We don't want to return `null` for nullable schema - if (this._typeCheck(val)) return val; - return original == null ? [] : [].concat(original); - }); - } - - compact(rejector) { - let reject = !rejector ? (v) => !!v : (v, i, a) => !rejector(v, i, a); - - return this.transform((values) => - values != null ? values.filter(reject) : values, - ); - } - - describe() { - let base = MixedSchema.prototype.describe.call(this); - if (this.innerType) base.innerType = this.innerType.describe(); - return base; - } -} diff --git a/src/array.ts b/src/array.ts new file mode 100644 index 000000000..2ac3e243d --- /dev/null +++ b/src/array.ts @@ -0,0 +1,214 @@ +import isAbsent from './util/isAbsent'; +import isSchema from './util/isSchema'; +import printValue from './util/printValue'; +import MixedSchema from './mixed'; +import { array as locale } from './locale'; +import runTests, { RunTest } from './util/runTests'; +import Schema, { SchemaInnerTypeDescription } from './Schema'; +import { InternalOptions, Callback, Message } from './types'; +import ValidationError from './ValidationError'; +import { Test } from './util/createValidation'; +import Reference from './Reference'; + +type RefectorFn = (value: any, index: number, array: any[]) => boolean; + +export function create(type: TInner) { + return new ArraySchema(type); +} + +export default class ArraySchema extends MixedSchema { + private _subType?: T; + + innerType: T | undefined; + + constructor(type: T) { + super({ type: 'array' }); + + // `undefined` specifically means uninitialized, as opposed to + // "no subtype" + this._subType = undefined; + this.innerType = undefined; + + this.withMutation(() => { + this.transform(function (values) { + if (typeof values === 'string') + try { + values = JSON.parse(values); + } catch (err) { + values = null; + } + + return this.isType(values) ? values : null; + }); + + if (type) this.of(type); + }); + } + + protected _typeCheck(v: any): v is any[] { + return Array.isArray(v); + } + + protected _cast(_value: any, _opts: InternalOptions) { + const value = super._cast(_value, _opts); + + //should ignore nulls here + if (!this._typeCheck(value) || !this.innerType) return value; + + let isChanged = false; + const castArray = value.map((v, idx) => { + const castElement = this.innerType!.cast(v, { + ..._opts, + path: `${_opts.path || ''}[${idx}]`, + }); + if (castElement !== v) { + isChanged = true; + } + + return castElement; + }); + + return isChanged ? castArray : value; + } + + protected _validate( + _value: any, + options: InternalOptions = {}, + callback: Callback, + ) { + let errors = [] as ValidationError[]; + let sync = options.sync; + let path = options.path; + let innerType = this.innerType; + let endEarly = this._option('abortEarly', options); + let recursive = this._option('recursive', options); + + let originalValue = + options.originalValue != null ? options.originalValue : _value; + + super._validate(_value, options, (err, value) => { + if (err) { + if (!ValidationError.isError(err) || endEarly) { + return void callback(err, value); + } + errors.push(err); + } + + if (!recursive || !innerType || !this._typeCheck(value)) { + callback(errors[0] || null, value); + return; + } + + originalValue = originalValue || value; + + // #950 Ensure that sparse array empty slots are validated + let tests: RunTest[] = new Array(value.length); + for (let idx = 0; idx < value.length; idx++) { + let item = value[idx]; + let path = `${options.path || ''}[${idx}]`; + + // object._validate note for isStrict explanation + let innerOptions = { + ...options, + path, + strict: true, + parent: value, + index: idx, + originalValue: originalValue[idx], + }; + + tests[idx] = (_, cb) => + // XXX: ??? + // innerType!.validate + // ? + innerType!.validate(item, innerOptions, cb); + // : cb(null); + } + + runTests( + { + sync, + path, + value, + errors, + endEarly, + tests, + }, + callback, + ); + }); + } + + _isPresent(value: any[]) { + return super._isPresent(value) && value.length > 0; + } + + of(schema: TInner | false) { + var next = this.clone(); + + if (schema !== false && !isSchema(schema)) + throw new TypeError( + '`array.of()` sub-schema must be a valid yup schema, or `false` to negate a current sub-schema. ' + + 'not: ' + + printValue(schema), + ); + // FIXME(ts): + next._subType = schema as any; + next.innerType = schema as any; + + return next; + } + + min(min: number | Reference, message?: Message<{ min: number }>) { + message = message || locale.min; + + return this.test({ + message, + name: 'min', + exclusive: true, + params: { min }, + // FIXME(ts): Array + test(value: any[]) { + return isAbsent(value) || value.length >= this.resolve(min); + }, + }); + } + + max(max: number | Reference, message?: Message<{ max: number }>) { + message = message || locale.max; + return this.test({ + message, + name: 'max', + exclusive: true, + params: { max }, + // FIXME(ts): Array + test(value: any[]) { + return isAbsent(value) || value.length <= this.resolve(max); + }, + }); + } + + ensure() { + return this.default(() => []).transform((val, original) => { + // We don't want to return `null` for nullable schema + if (this._typeCheck(val)) return val; + return original == null ? [] : [].concat(original); + }); + } + + compact(rejector?: RefectorFn) { + let reject: RefectorFn = !rejector + ? (v) => !!v + : (v, i, a) => !rejector(v, i, a); + + return this.transform((values: any[]) => + values != null ? values.filter(reject) : values, + ); + } + + describe() { + let base = super.describe() as SchemaInnerTypeDescription; + if (this.innerType) base.innerType = this.innerType.describe(); + return base; + } +} diff --git a/src/boolean.js b/src/boolean.ts similarity index 84% rename from src/boolean.js rename to src/boolean.ts index 716cc2cec..ab8d29dc8 100644 --- a/src/boolean.js +++ b/src/boolean.ts @@ -1,10 +1,10 @@ import MixedSchema from './mixed'; -export default class BooleanSchema extends MixedSchema { - static create(options) { - return new BooleanSchema(options); - } +export function create() { + return new BooleanSchema(); +} +export default class BooleanSchema extends MixedSchema { constructor() { super({ type: 'boolean' }); @@ -18,7 +18,8 @@ export default class BooleanSchema extends MixedSchema { }); }); } - _typeCheck(v) { + + protected _typeCheck(v: any) { if (v instanceof Boolean) v = v.valueOf(); return typeof v === 'boolean'; diff --git a/src/date.js b/src/date.ts similarity index 74% rename from src/date.js rename to src/date.ts index 682346bb3..4ff185b12 100644 --- a/src/date.js +++ b/src/date.ts @@ -1,4 +1,5 @@ import MixedSchema from './mixed'; +// @ts-ignore import isoParse from './util/isodate'; import { date as locale } from './locale'; import isAbsent from './util/isAbsent'; @@ -6,12 +7,14 @@ import Ref from './Reference'; let invalidDate = new Date(''); -let isDate = (obj) => Object.prototype.toString.call(obj) === '[object Date]'; +let isDate = (obj: any): obj is Date => + Object.prototype.toString.call(obj) === '[object Date]'; + +export function create() { + return new DateSchema(); +} export default class DateSchema extends MixedSchema { - static create() { - return new DateSchema(); - } constructor() { super({ type: 'date' }); @@ -27,11 +30,11 @@ export default class DateSchema extends MixedSchema { }); } - _typeCheck(v) { + protected _typeCheck(v: any) { return isDate(v) && !isNaN(v.getTime()); } - min(min, message = locale.min) { + min(min: unknown | Ref, message = locale.min) { var limit = min; if (!Ref.isRef(limit)) { @@ -47,13 +50,13 @@ export default class DateSchema extends MixedSchema { name: 'min', exclusive: true, params: { min }, - test(value) { - return isAbsent(value) || value >= this.resolve(limit); + test(value: Date) { + return isAbsent(value) || value >= this.resolve(limit); }, }); } - max(max, message = locale.max) { + max(max: unknown | Ref, message = locale.max) { var limit = max; if (!Ref.isRef(limit)) { @@ -69,8 +72,8 @@ export default class DateSchema extends MixedSchema { name: 'max', exclusive: true, params: { max }, - test(value) { - return isAbsent(value) || value <= this.resolve(limit); + test(value: Date) { + return isAbsent(value) || value <= this.resolve(limit); }, }); } diff --git a/src/globals.d.ts b/src/globals.d.ts new file mode 100644 index 000000000..099ae5b5e --- /dev/null +++ b/src/globals.d.ts @@ -0,0 +1,10 @@ +declare module 'lodash/has' { + function has( + obj: T, + prop: Key, + ): obj is T & Record { + return has(obj, prop); + } + + export default has; +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 8913f8bf8..000000000 --- a/src/index.js +++ /dev/null @@ -1,66 +0,0 @@ -import MixedSchema from './mixed'; -import BoolSchema from './boolean'; -import StringSchema from './string'; -import NumberSchema from './number'; -import DateSchema from './date'; -import ObjectSchema from './object'; -import ArraySchema from './array'; -import Ref from './Reference'; -import Lazy from './Lazy'; -import ValidationError from './ValidationError'; -import reach from './util/reach'; -import isSchema from './util/isSchema'; -import setLocale from './setLocale'; - -let ref = (key, options) => new Ref(key, options); - -let lazy = (fn) => new Lazy(fn); - -function addMethod(schemaType, name, fn) { - if (!schemaType || !isSchema(schemaType.prototype)) - throw new TypeError('You must provide a yup schema constructor function'); - - if (typeof name !== 'string') - throw new TypeError('A Method name must be provided'); - if (typeof fn !== 'function') - throw new TypeError('Method function must be provided'); - - schemaType.prototype[name] = fn; -} - -const mixed = MixedSchema.create; -const string = StringSchema.create; -const number = NumberSchema.create; -const bool = BoolSchema.create; -const boolean = bool; -const date = DateSchema.create; -const object = ObjectSchema.create; -const array = ArraySchema.create; - -export { - MixedSchema, - BoolSchema, - StringSchema, - NumberSchema, - DateSchema, - ObjectSchema, - ArraySchema, -}; - -export { - mixed, - string, - number, - bool, - boolean, - date, - object, - array, - ref, - lazy, - reach, - isSchema, - addMethod, - setLocale, - ValidationError, -}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..b75cf20b7 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,53 @@ +import MixedSchema, { create as mixedCreate } from './mixed'; +import BoolSchema, { create as boolCreate } from './boolean'; +import StringSchema, { create as stringCreate } from './string'; +import NumberSchema, { create as numberCreate } from './number'; +import DateSchema, { create as dateCreate } from './date'; +import ObjectSchema, { create as objectCreate } from './object'; +import ArraySchema, { create as arrayCreate } from './array'; +import Ref, { create as refCreate } from './Reference'; +import Lazy, { create as lazyCreate } from './Lazy'; +import ValidationError from './ValidationError'; +import reach from './util/reach'; +import isSchema from './util/isSchema'; +import setLocale from './setLocale'; + +function addMethod(schemaType: any, name: string, fn: any) { + if (!schemaType || !isSchema(schemaType.prototype)) + throw new TypeError('You must provide a yup schema constructor function'); + + if (typeof name !== 'string') + throw new TypeError('A Method name must be provided'); + if (typeof fn !== 'function') + throw new TypeError('Method function must be provided'); + + schemaType.prototype[name] = fn; +} + +export { + mixedCreate as mixed, + boolCreate as bool, + boolCreate as boolean, + stringCreate as string, + numberCreate as number, + dateCreate as date, + objectCreate as object, + arrayCreate as array, + refCreate as ref, + lazyCreate as lazy, + reach, + isSchema, + addMethod, + setLocale, + ValidationError, +}; + +export { + MixedSchema, + BoolSchema, + StringSchema, + NumberSchema, + DateSchema, + ObjectSchema, + ArraySchema, +}; diff --git a/src/locale.js b/src/locale.ts similarity index 59% rename from src/locale.js rename to src/locale.ts index 6838516f6..bed14303f 100644 --- a/src/locale.js +++ b/src/locale.ts @@ -1,6 +1,53 @@ import printValue from './util/printValue'; +import { Message } from './types'; -export let mixed = { +export interface MixedLocale { + default?: Message; + required?: Message; + oneOf?: Message<{ values: any }>; + notOneOf?: Message<{ values: any }>; + notType?: Message; + defined?: Message; +} + +export interface StringLocale { + length?: Message<{ length: number }>; + min?: Message<{ min: number }>; + max?: Message<{ max: number }>; + matches?: Message<{ regex: RegExp }>; + email?: Message<{ regex: RegExp }>; + url?: Message<{ regex: RegExp }>; + uuid?: Message<{ regex: RegExp }>; + trim?: Message; + lowercase?: Message; + uppercase?: Message; +} + +export interface NumberLocale { + min?: Message<{ min: number }>; + max?: Message<{ max: number }>; + lessThan?: Message<{ less: number }>; + moreThan?: Message<{ more: number }>; + positive?: Message<{ more: number }>; + negative?: Message<{ less: number }>; + integer?: Message; +} + +export interface DateLocale { + min?: Message<{ min: Date | string }>; + max?: Message<{ max: Date | string }>; +} + +export interface ObjectLocale { + noUnknown?: Message; +} + +export interface ArrayLocale { + min?: Message<{ min: number }>; + max?: Message<{ max: number }>; +} + +export let mixed: Required = { default: '${path} is invalid', required: '${path} is a required field', oneOf: '${path} must be one of the following values: ${values}', @@ -23,7 +70,7 @@ export let mixed = { defined: '${path} must be defined', }; -export let string = { +export let string: Required = { length: '${path} must be exactly ${length} characters', min: '${path} must be at least ${min} characters', max: '${path} must be at most ${max} characters', @@ -36,29 +83,28 @@ export let string = { uppercase: '${path} must be a upper case string', }; -export let number = { +export let number: Required = { min: '${path} must be greater than or equal to ${min}', max: '${path} must be less than or equal to ${max}', lessThan: '${path} must be less than ${less}', moreThan: '${path} must be greater than ${more}', - notEqual: '${path} must be not equal to ${notEqual}', positive: '${path} must be a positive number', negative: '${path} must be a negative number', integer: '${path} must be an integer', }; -export let date = { +export let date: Required = { min: '${path} field must be later than ${min}', max: '${path} field must be at earlier than ${max}', }; export let boolean = {}; -export let object = { +export let object: Required = { noUnknown: '${path} field has unspecified keys: ${unknown}', }; -export let array = { +export let array: Required = { min: '${path} field must have at least ${min} items', max: '${path} field must have less than or equal to ${max} items', }; diff --git a/src/mixed.js b/src/mixed.ts similarity index 72% rename from src/mixed.js rename to src/mixed.ts index db10a67de..0454c13cc 100644 --- a/src/mixed.js +++ b/src/mixed.ts @@ -1,18 +1,38 @@ import has from 'lodash/has'; import cloneDeepWith from 'lodash/cloneDeepWith'; -import toArray from 'lodash/toArray'; import { mixed as locale } from './locale'; -import Condition from './Condition'; +import Condition, { ConditionOptions, ResolveOptions } from './Condition'; import runTests from './util/runTests'; import prependDeep from './util/prependDeep'; import isSchema from './util/isSchema'; -import createValidation from './util/createValidation'; +import createValidation, { + TestFunction, + Test, + TestConfig, +} from './util/createValidation'; import printValue from './util/printValue'; import Ref from './Reference'; import { getIn } from './util/reach'; +import Reference from './Reference'; +import toArray from './util/toArray'; +import { + ValidateOptions, + TransformFunction, + Message, + Callback, + InternalOptions, +} from './types'; +import Schema, { + CastOptions, + SchemaRefDescription, + SchemaDescription, +} from './Schema'; class RefSet { + list: Set; + refs: Map; + constructor() { this.list = new Set(); this.refs = new Map(); @@ -21,7 +41,7 @@ class RefSet { return this.list.size + this.refs.size; } describe() { - const description = []; + const description = [] as Array; for (const item of this.list) description.push(item); for (const [, ref] of this.refs) description.push(ref.describe()); @@ -29,15 +49,15 @@ class RefSet { return description; } toArray() { - return toArray(this.list).concat(toArray(this.refs.values())); + return Array.from(this.list).concat(Array.from(this.refs.values())); } - add(value) { + add(value: unknown) { Ref.isRef(value) ? this.refs.set(value.key, value) : this.list.add(value); } - delete(value) { + delete(value: unknown) { Ref.isRef(value) ? this.refs.delete(value.key) : this.list.delete(value); } - has(value, resolve) { + has(value: unknown, resolve: (v: unknown) => unknown) { if (this.list.has(value)) return true; let item, @@ -53,7 +73,7 @@ class RefSet { next.refs = new Map(this.refs); return next; } - merge(newItems, removeItems) { + merge(newItems: RefSet, removeItems: RefSet) { const next = this.clone(); newItems.list.forEach((value) => next.add(value)); newItems.refs.forEach((value) => next.add(value)); @@ -63,19 +83,58 @@ class RefSet { } } -export default class MixedSchema { - static create(options) { - return new MixedSchema(options); +export interface SchemaOptions { + default?: (this: T) => any; + type: string; +} + +export function create(options?: SchemaOptions) { + return new MixedSchema(options); +} + +export default class MixedSchema implements Schema { + private _deps: string[]; + + __isYupSchema__ = true; + protected _options: Partial; + protected _exclusive: Record = Object.create(null); + + readonly type: string; + protected _whitelist: RefSet = new RefSet(); + protected _blacklist: RefSet = new RefSet(); + + tests: Test[]; + transforms: TransformFunction[]; // TODO + + private _mutate?: boolean; + + protected _label?: string; + protected _meta: any; + private _default?: unknown; + private _nullable: boolean = false; + private _conditions: Condition[]; + private _defaultDefault: ((this: this) => unknown) | undefined; + + private _validating: boolean = false; + private _typeError?: Test; + private _whitelistError?: Test; + private _blacklistError?: Test; + + private _strip: boolean = false; + + optional!: () => MixedSchema; + + static create( + this: new (...args: any[]) => T, + ...args: any[] + ) { + return new this(...args); } - constructor(options = {}) { + constructor(options: SchemaOptions = { type: 'mixed' }) { this._deps = []; this._conditions = []; this._options = { abortEarly: true, recursive: true }; - this._exclusive = Object.create(null); - - this._whitelist = new RefSet(); - this._blacklist = new RefSet(); this.tests = []; this.transforms = []; @@ -86,14 +145,21 @@ export default class MixedSchema { if (has(options, 'default')) this._defaultDefault = options.default; - this.type = options.type || 'mixed'; - // TODO: remove - this._type = options.type || 'mixed'; + this.type = options.type; } - __isYupSchema__ = true; + // TODO: remove + get _type() { + return this.type; + } - clone() { + protected _typeCheck(_: any) { + return true; + } + + // __isYupSchema__ = true; + + clone(): this { if (this._mutate) return this; // if the nested value is a schema we can skip cloning, since @@ -103,13 +169,13 @@ export default class MixedSchema { }); } - label(label) { + label(label: string) { var next = this.clone(); next._label = label; return next; } - meta(obj) { + meta(obj: {}) { if (arguments.length === 0) return this._meta; var next = this.clone(); @@ -117,7 +183,7 @@ export default class MixedSchema { return next; } - withMutation(fn) { + withMutation(fn: (schema: this) => T): T { let before = this._mutate; this._mutate = true; let result = fn(this); @@ -125,15 +191,15 @@ export default class MixedSchema { return result; } - concat(schema) { + concat(schema: this) { if (!schema || schema === this) return this; - if (schema._type !== this._type && this._type !== 'mixed') + if (schema.type !== this.type && this.type !== 'mixed') throw new TypeError( `You cannot \`concat()\` schema's of different types: ${this._type} and ${schema._type}`, ); - var next = prependDeep(schema.clone(), this); + var next = prependDeep(schema.clone() as any, this as any) as this; // new undefined default is overridden by old non-undefined one, revert if (has(schema, '_default')) next._default = schema._default; @@ -163,12 +229,14 @@ export default class MixedSchema { return next; } - isType(v) { + // abstract ?(value: any): boolean; + + isType(v: any) { if (this._nullable && v === null) return true; - return !this._typeCheck || this._typeCheck(v); + return this._typeCheck(v); } - resolve(options) { + resolve(options: ResolveOptions) { let schema = this; if (schema._conditions.length) { @@ -194,7 +262,7 @@ export default class MixedSchema { * @param {*=} options.parent * @param {*=} options.context */ - cast(value, options = {}) { + cast(value: any, options: CastOptions = {}) { let resolvedSchema = this.resolve({ value, ...options, @@ -226,7 +294,7 @@ export default class MixedSchema { return result; } - _cast(rawValue) { + protected _cast(rawValue: any, _options: CastOptions) { let value = rawValue === undefined ? rawValue @@ -242,7 +310,11 @@ export default class MixedSchema { return value; } - _validate(_value, options = {}, cb) { + protected _validate( + _value: any, + options: InternalOptions = {}, + cb: Callback, + ): void { let { sync, path, @@ -276,7 +348,7 @@ export default class MixedSchema { if (this._whitelistError) initialTests.push(this._whitelistError); if (this._blacklistError) initialTests.push(this._blacklistError); - return runTests( + runTests( { args, value, @@ -286,7 +358,7 @@ export default class MixedSchema { endEarly: abortEarly, }, (err) => { - if (err) return void cb(err); + if (err) return void cb(err, value); runTests( { @@ -303,7 +375,8 @@ export default class MixedSchema { ); } - validate(value, options = {}, maybeCb) { + validate(value: any, options: ValidateOptions): Promise; + validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { let schema = this.resolve({ ...options, value }); // callback case is for nested validations @@ -317,7 +390,7 @@ export default class MixedSchema { ); } - validateSync(value, options = {}) { + validateSync(value: any, options: ValidateOptions = {}) { let schema = this.resolve({ ...options, value }); let result; @@ -329,7 +402,7 @@ export default class MixedSchema { return result; } - isValid(value, options) { + isValid(value: any, options: ValidateOptions) { return this.validate(value, options) .then(() => true) .catch((err) => { @@ -338,7 +411,7 @@ export default class MixedSchema { }); } - isValidSync(value, options) { + isValidSync(value: any, options: ValidateOptions) { try { this.validateSync(value, options); return true; @@ -353,7 +426,9 @@ export default class MixedSchema { return schema.default(); } - default(def) { + default(): any; // FIXME(ts): typed default + default(def: TDefault | (() => TDefault)): this; + default(def?: TDefault | (() => TDefault)) { if (arguments.length === 0) { var defaultValue = has(this, '_default') ? this._default @@ -375,7 +450,7 @@ export default class MixedSchema { return next; } - _isPresent(value) { + protected _isPresent(value: unknown) { return value != null; } @@ -402,7 +477,7 @@ export default class MixedSchema { return next; } - transform(fn) { + transform(fn: TransformFunction) { var next = this.clone(); next.transforms.push(fn); return next; @@ -421,8 +496,12 @@ export default class MixedSchema { * If an exclusive test is added to a schema with non-exclusive tests of the same name * the previous tests are removed and further tests of the same name will replace each other. */ - test(...args) { - let opts; + test(options: TestConfig): this; + test(test: TestFunction): this; + test(name: string, test: TestFunction): this; + test(name: string, message: Message, test: TestFunction): this; + test(...args: any[]) { + let opts: TestConfig; if (args.length === 1) { if (typeof args[0] === 'function') { @@ -447,13 +526,14 @@ export default class MixedSchema { let isExclusive = opts.exclusive || (opts.name && next._exclusive[opts.name] === true); - if (opts.exclusive && !opts.name) { - throw new TypeError( - 'Exclusive tests must provide a unique `name` identifying the test', - ); + if (opts.exclusive) { + if (!opts.name) + throw new TypeError( + 'Exclusive tests must provide a unique `name` identifying the test', + ); } - next._exclusive[opts.name] = !!opts.exclusive; + if (opts.name) next._exclusive[opts.name] = !!opts.exclusive; next.tests = next.tests.filter((fn) => { if (fn.OPTIONS.name === opts.name) { @@ -468,25 +548,30 @@ export default class MixedSchema { return next; } - when(keys, options) { - if (arguments.length === 1) { + when(options: ConditionOptions): this; + when(keys: string | string[], options: ConditionOptions): this; + when( + keys: string | string[] | ConditionOptions, + options?: ConditionOptions, + ) { + if (!Array.isArray(keys) && typeof keys !== 'string') { options = keys; keys = '.'; } - var next = this.clone(), - deps = [].concat(keys).map((key) => new Ref(key)); + let next = this.clone(); + let deps = toArray(keys).map((key) => new Ref(key)); deps.forEach((dep) => { if (dep.isSibling) next._deps.push(dep.key); }); - next._conditions.push(new Condition(deps, options)); + next._conditions.push(new Condition(deps, options!)); return next; } - typeError(message) { + typeError(message: Message) { var next = this.clone(); next._typeError = createValidation({ @@ -505,7 +590,7 @@ export default class MixedSchema { return next; } - oneOf(enums, message = locale.oneOf) { + oneOf(enums: unknown[], message = locale.oneOf) { var next = this.clone(); enums.forEach((val) => { @@ -533,7 +618,7 @@ export default class MixedSchema { return next; } - notOneOf(enums, message = locale.notOneOf) { + notOneOf(enums: unknown[], message = locale.notOneOf) { var next = this.clone(); enums.forEach((val) => { next._blacklist.add(val); @@ -564,16 +649,21 @@ export default class MixedSchema { return next; } - _option(key, overrides) { + protected _option( + key: Key, + overrides: ValidateOptions, + ) { return has(overrides, key) ? overrides[key] : this._options[key]; } describe() { const next = this.clone(); - const description = { + const description: SchemaDescription = { type: next._type, meta: next._meta, label: next._label, + oneOf: next._whitelist.describe(), + notOneOf: next._blacklist.describe(), tests: next.tests .map((fn) => ({ name: fn.OPTIONS.name, params: fn.OPTIONS.params })) .filter( @@ -581,9 +671,6 @@ export default class MixedSchema { ), }; - if (next._whitelist.size) description.oneOf = next._whitelist.describe(); - if (next._blacklist.size) description.notOneOf = next._blacklist.describe(); - return description; } @@ -600,7 +687,11 @@ export default class MixedSchema { } for (const method of ['validate', 'validateSync']) - MixedSchema.prototype[`${method}At`] = function (path, value, options = {}) { + MixedSchema.prototype[`${method}At`] = function ( + path: string, + value: any, + options: ValidateOptions = {}, + ) { const { parent, parentPath, schema } = getIn( this, path, diff --git a/src/number.js b/src/number.ts similarity index 76% rename from src/number.js rename to src/number.ts index 513cc1527..8d5d0b5a4 100644 --- a/src/number.js +++ b/src/number.ts @@ -1,14 +1,16 @@ import MixedSchema from './mixed'; import { number as locale } from './locale'; import isAbsent from './util/isAbsent'; +import { Maybe } from './types'; +import Reference from './Reference'; -let isNaN = (value) => value != +value; +let isNaN = (value: Maybe) => value != +value!; -export default class NumberSchema extends MixedSchema { - static create() { - return new NumberSchema(); - } +export function create() { + return new NumberSchema(); +} +export default class NumberSchema extends MixedSchema { constructor() { super({ type: 'number' }); @@ -30,55 +32,55 @@ export default class NumberSchema extends MixedSchema { }); } - _typeCheck(value) { + protected _typeCheck(value: any): value is number { if (value instanceof Number) value = value.valueOf(); return typeof value === 'number' && !isNaN(value); } - min(min, message = locale.min) { + min(min: number | Reference, message = locale.min) { return this.test({ message, name: 'min', exclusive: true, params: { min }, - test(value) { + test(value: Maybe) { return isAbsent(value) || value >= this.resolve(min); }, }); } - max(max, message = locale.max) { + max(max: number | Reference, message = locale.max) { return this.test({ message, name: 'max', exclusive: true, params: { max }, - test(value) { + test(value: Maybe) { return isAbsent(value) || value <= this.resolve(max); }, }); } - lessThan(less, message = locale.lessThan) { + lessThan(less: number | Reference, message = locale.lessThan) { return this.test({ message, name: 'max', exclusive: true, params: { less }, - test(value) { + test(value: Maybe) { return isAbsent(value) || value < this.resolve(less); }, }); } - moreThan(more, message = locale.moreThan) { + moreThan(more: number | Reference, message = locale.moreThan) { return this.test({ message, name: 'min', exclusive: true, params: { more }, - test(value) { + test(value: Maybe) { return isAbsent(value) || value > this.resolve(more); }, }); @@ -104,9 +106,9 @@ export default class NumberSchema extends MixedSchema { return this.transform((value) => (!isAbsent(value) ? value | 0 : value)); } - round(method) { + round(method: 'ceil' | 'floor' | 'round' | 'trunc') { var avail = ['ceil', 'floor', 'round', 'trunc']; - method = (method && method.toLowerCase()) || 'round'; + method = (method?.toLowerCase() as any) || ('round' as const); // this exists for symemtry with the new Math.trunc if (method === 'trunc') return this.truncate(); diff --git a/src/object.js b/src/object.ts similarity index 72% rename from src/object.js rename to src/object.ts index a591a5fb6..b641aad9a 100644 --- a/src/object.js +++ b/src/object.ts @@ -6,12 +6,15 @@ import mapValues from 'lodash/mapValues'; import { getter } from 'property-expr'; import MixedSchema from './mixed'; -import { object as locale } from './locale.js'; +import { object as locale } from './locale'; import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; +import Schema, { CastOptions, SchemaObjectDescription } from './Schema'; +import { InternalOptions, Callback } from './types'; +import { ValidationError } from '.'; -let isObject = (obj) => +let isObject = (obj: any): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; function unknown(ctx, value) { @@ -19,21 +22,36 @@ function unknown(ctx, value) { return Object.keys(value).filter((key) => known.indexOf(key) === -1); } -export default class ObjectSchema extends MixedSchema { - static create(spec) { - return new ObjectSchema(spec); - } - constructor(spec) { +type ObjectShape = Record; + +export function create(spec?: TShape) { + return new ObjectSchema(spec); +} + +export default class ObjectSchema< + TShape extends ObjectShape = ObjectShape +> extends MixedSchema { + fields: TShape; + + private _sortErrors: ( + a: import('/Users/jquense/src/yup/src/ValidationError').default, + b: import('/Users/jquense/src/yup/src/ValidationError').default, + ) => number; + private _nodes: string[]; + private _excludedEdges: string[]; + + constructor(spec?: TShape) { super({ type: 'object', - default() { + default(this: ObjectSchema) { if (!this._nodes.length) return undefined; - let dft = {}; + let dft = {} as Record; this._nodes.forEach((key) => { - dft[key] = this.fields[key].default - ? this.fields[key].default() - : undefined; + dft[key] = + 'default' in this.fields[key] + ? this.fields[key].default() + : undefined; }); return dft; }, @@ -65,12 +83,12 @@ export default class ObjectSchema extends MixedSchema { }); } - _typeCheck(value) { + protected _typeCheck(value: any): value is Record { return isObject(value) || typeof value === 'function'; } - _cast(_value, options = {}) { - let value = super._cast(_value); + protected _cast(_value: any, options: InternalOptions = {}) { + let value = super._cast(_value, options); //should ignore nulls here if (value === undefined) return this.default(); @@ -84,8 +102,8 @@ export default class ObjectSchema extends MixedSchema { Object.keys(value).filter((v) => this._nodes.indexOf(v) === -1), ); - let intermediateValue = {}; // is filled during the transform below - let innerOptions = { + let intermediateValue: Record = {}; // is filled during the transform below + let innerOptions: InternalOptions = { ...options, parent: intermediateValue, __validating: options.__validating || false, @@ -98,13 +116,19 @@ export default class ObjectSchema extends MixedSchema { if (field) { let fieldValue; - let strict = field._options && field._options.strict; + let strict = field._options?.strict; + + let inputValue = value[prop]; // safe to mutate since this is fired in sequence innerOptions.path = (options.path ? `${options.path}.` : '') + prop; - innerOptions.value = value[prop]; + // innerOptions.value = value[prop]; - field = field.resolve(innerOptions); + field = field.resolve({ + value: inputValue, + context: options.context, + parent: intermediateValue, + }); if (field._strip === true) { isChanged = isChanged || prop in value; @@ -113,7 +137,8 @@ export default class ObjectSchema extends MixedSchema { fieldValue = !options.__validating || !strict - ? field.cast(value[prop], innerOptions) + ? // TODO: use _cast, this is double resolving + field.cast(value[prop], innerOptions) : value[prop]; if (fieldValue !== undefined) { @@ -131,32 +156,12 @@ export default class ObjectSchema extends MixedSchema { return isChanged ? intermediateValue : value; } - /** - * @typedef {Object} Ancestor - * @property {Object} schema - a string property of SpecialType - * @property {*} value - a number property of SpecialType - */ - - /** - * - * @param {*} _value - * @param {Object} opts - * @param {string=} opts.path - * @param {*=} opts.parent - * @param {Object=} opts.context - * @param {boolean=} opts.sync - * @param {boolean=} opts.stripUnknown - * @param {boolean=} opts.strict - * @param {boolean=} opts.recursive - * @param {boolean=} opts.abortEarly - * @param {boolean=} opts.__validating - * @param {Object=} opts.originalValue - * @param {Ancestor[]=} opts.from - * @param {Object} [opts.from] - * @param {Function} callback - */ - _validate(_value, opts = {}, callback) { - let errors = []; + protected _validate( + _value: any, + opts: InternalOptions = {}, + callback: Callback, + ) { + let errors = [] as ValidationError[]; let { sync, from = [], @@ -175,10 +180,10 @@ export default class ObjectSchema extends MixedSchema { super._validate(_value, opts, (err, value) => { if (err) { - if (abortEarly) return void callback(err); - + if (!ValidationError.isError(err) || abortEarly) { + return void callback(err, value); + } errors.push(err); - value = err.value; } if (!recursive || !isObject(value)) { @@ -188,7 +193,7 @@ export default class ObjectSchema extends MixedSchema { originalValue = originalValue || value; - let tests = this._nodes.map((key) => (_, cb) => { + let tests = this._nodes.map((key) => (_: any, cb: Callback) => { let path = key.indexOf('.') === -1 ? (opts.path ? `${opts.path}.` : '') + key @@ -197,7 +202,7 @@ export default class ObjectSchema extends MixedSchema { let field = this.fields[key]; if (field && field.validate) { - field.validate( + (field as Schema).validate( value[key], { ...opts, @@ -233,7 +238,7 @@ export default class ObjectSchema extends MixedSchema { }); } - concat(schema) { + concat(schema: ObjectSchema) { var next = super.concat(schema); next._nodes = sortFields(next.fields, next._excludedEdges); @@ -241,7 +246,10 @@ export default class ObjectSchema extends MixedSchema { return next; } - shape(schema, excludes = []) { + shape( + schema: TNextShape, + excludes: [string, string][] = [], + ) { let next = this.clone(); let fields = Object.assign(next.fields, schema); @@ -249,7 +257,7 @@ export default class ObjectSchema extends MixedSchema { next._sortErrors = sortByKeyOrder(Object.keys(fields)); if (excludes.length) { - if (!Array.isArray(excludes[0])) excludes = [excludes]; + if (!Array.isArray(excludes[0])) excludes = [excludes as any]; let keys = excludes.map(([first, second]) => `${first}-${second}`); @@ -261,7 +269,7 @@ export default class ObjectSchema extends MixedSchema { return next; } - from(from, to, alias) { + from(from: string, to: keyof TShape, alias?: boolean) { let fromGetter = getter(from, true); return this.transform((obj) => { @@ -308,7 +316,7 @@ export default class ObjectSchema extends MixedSchema { return this.noUnknown(!allow, message); } - transformKeys(fn) { + transformKeys(fn: (key: string) => string) { return this.transform((obj) => obj && mapKeys(obj, (_, key) => fn(key))); } @@ -325,7 +333,7 @@ export default class ObjectSchema extends MixedSchema { } describe() { - let base = super.describe(); + let base = super.describe() as SchemaObjectDescription; base.fields = mapValues(this.fields, (value) => value.describe()); return base; } diff --git a/src/string.js b/src/string.ts similarity index 80% rename from src/string.js rename to src/string.ts index 096501b86..a1cc43eac 100644 --- a/src/string.js +++ b/src/string.ts @@ -1,6 +1,8 @@ import MixedSchema from './mixed'; import { string as locale } from './locale'; import isAbsent from './util/isAbsent'; +import Reference from './Reference'; +import { Message, Maybe } from './types'; // eslint-disable-next-line let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; @@ -9,12 +11,20 @@ let rUrl = /^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFD // eslint-disable-next-line let rUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i; -let isTrimmed = (value) => isAbsent(value) || value === value.trim(); +let isTrimmed = (value: Maybe) => + isAbsent(value) || value === value.trim(); + +export type MatchOptions = { + excludeEmptyString?: boolean; + message: Message<{ regex: RegExp }>; + name?: string; +}; + +export function create() { + return new StringSchema(); +} export default class StringSchema extends MixedSchema { - static create() { - return new StringSchema(); - } constructor() { super({ type: 'string' }); @@ -26,60 +36,67 @@ export default class StringSchema extends MixedSchema { }); } - _typeCheck(value) { + protected _typeCheck(value: any): value is string { if (value instanceof String) value = value.valueOf(); return typeof value === 'string'; } - _isPresent(value) { + protected _isPresent(value: any) { return super._isPresent(value) && value.length > 0; } - length(length, message = locale.length) { + length( + length: number | Reference, + message: Message<{ length: number }> = locale.length, + ) { return this.test({ message, name: 'length', exclusive: true, params: { length }, - test(value) { + test(value: Maybe) { return isAbsent(value) || value.length === this.resolve(length); }, }); } - min(min, message = locale.min) { + min(min: number | Reference, message: Message<{ min: number }> = locale.min) { return this.test({ message, name: 'min', exclusive: true, params: { min }, - test(value) { + test(value: Maybe) { return isAbsent(value) || value.length >= this.resolve(min); }, }); } - max(max, message = locale.max) { + max(max: number | Reference, message: Message<{ max: number }> = locale.max) { return this.test({ name: 'max', exclusive: true, message, params: { max }, - test(value) { + test(value: Maybe) { return isAbsent(value) || value.length <= this.resolve(max); }, }); } - matches(regex, options) { + matches(regex: RegExp, options: MatchOptions | MatchOptions['message']) { let excludeEmptyString = false; let message; let name; if (options) { if (typeof options === 'object') { - ({ excludeEmptyString, message, name } = options); + ({ + excludeEmptyString = false, + message, + name, + } = options as MatchOptions); } else { message = options; } @@ -89,7 +106,7 @@ export default class StringSchema extends MixedSchema { name: name || 'matches', message: message || locale.matches, params: { regex }, - test: (value) => + test: (value: Maybe) => isAbsent(value) || (value === '' && excludeEmptyString) || value.search(regex) !== -1, @@ -140,7 +157,8 @@ export default class StringSchema extends MixedSchema { message, name: 'string_case', exclusive: true, - test: (value) => isAbsent(value) || value === value.toLowerCase(), + test: (value: Maybe) => + isAbsent(value) || value === value.toLowerCase(), }); } @@ -151,7 +169,8 @@ export default class StringSchema extends MixedSchema { message, name: 'string_case', exclusive: true, - test: (value) => isAbsent(value) || value === value.toUpperCase(), + test: (value: Maybe) => + isAbsent(value) || value === value.toUpperCase(), }); } } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 000000000..d61ee63e4 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,60 @@ +import type Schema from './Schema'; + +export type Callback = (err: Error | null, value?: any) => void; + +export type TransformFunction = ( + this: T, + value: any, + originalValue: any, +) => any; + +export interface ValidateOptions { + /** + * Only validate the input, and skip and coercion or transformation. Default - false + */ + strict?: boolean; + /** + * Return from validation methods on the first error rather than after all validations run. Default - true + */ + abortEarly?: boolean; + /** + * Remove unspecified keys from objects. Default - false + */ + stripUnknown?: boolean; + /** + * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true + */ + recursive?: boolean; + /** + * Any context needed for validating schema conditions (see: when()) + */ + context?: object; +} + +export interface InternalOptions extends ValidateOptions { + __validating?: boolean; + originalValue?: any; + parent?: any; + path?: string; + sync?: boolean; + from?: { schema: Schema; value: any }[]; +} + +export interface MessageParams { + path: string; + value: any; + originalValue: any; + label: string; + type: string; +} + +export type Message = {}> = + | string + | ((params: Extra & MessageParams) => unknown) + | Record; + +export type ExtraParams = Record; + +export type AnyMessageParams = MessageParams & ExtraParams; + +export type Maybe = T | null | undefined; diff --git a/src/util/async.js b/src/util/async.js deleted file mode 100644 index f24eef84e..000000000 --- a/src/util/async.js +++ /dev/null @@ -1,52 +0,0 @@ -export function asCallback(promise, callback) { - promise.then((result) => callback(null, result), callback); -} - -export const once = (cb) => { - let fired = false; - return (...args) => { - if (fired) return; - fired = true; - cb(...args); - }; -}; - -export function parallel(fns, cb) { - let callback = once(cb); - let count = fns.length; - if (count === 0) { - return void callback(null, []); - } - let results = new Array(count); - - for (let i = 0; i < fns.length; i++) { - let idx = i; - const fn = fns[i]; - fn((err, value) => { - if (err) return callback(err); - - results[idx] = value; - if (--count <= 0) callback(null, results); - }); - } -} - -export function settled(fns, cb) { - let callback = once(cb); - let count = fns.length; - if (count === 0) { - return void callback(null, []); - } - const results = new Array(fns.length); - for (let i = 0; i < fns.length; i++) { - let idx = i; - const fn = fns[i]; - fn((err, value) => { - results[idx] = err - ? { fulfilled: false, value: err } - : { fulfilled: true, value }; - - if (--count <= 0) callback(null, results); - }); - } -} diff --git a/src/util/async.ts b/src/util/async.ts new file mode 100644 index 000000000..cbfdf001e --- /dev/null +++ b/src/util/async.ts @@ -0,0 +1,14 @@ +import { Callback } from '../types'; + +export function asCallback(promise: Promise, callback: Callback) { + promise.then((result) => callback(null, result), callback); +} + +export const once = any>(cb: T) => { + let fired = false; + return (...args: Parameters) => { + if (fired) return; + fired = true; + cb(...args); + }; +}; diff --git a/src/util/createValidation.js b/src/util/createValidation.ts similarity index 52% rename from src/util/createValidation.js rename to src/util/createValidation.ts index dccfd5259..22aa76d8c 100644 --- a/src/util/createValidation.js +++ b/src/util/createValidation.ts @@ -1,20 +1,86 @@ import mapValues from 'lodash/mapValues'; import ValidationError from '../ValidationError'; import Ref from '../Reference'; +import { + ValidateOptions, + Message, + InternalOptions, + Callback, + MessageParams, + AnyMessageParams, + ExtraParams, +} from '../types'; +import Schema from '../Schema'; -export default function createValidation(config) { - function validate( - { value, path, label, options, originalValue, sync, ...rest }, - cb, +export type CreateErrorOptions = { + path?: string; + message?: string; + params?: object; + type?: string; +}; + +export type TestContext = { + path: string; + options: ValidateOptions; + parent: any; + schema: any; // TODO: Schema; + resolve: (value: any) => T; + createError: (params?: CreateErrorOptions) => ValidationError; +}; + +export type TestFunction = ( + this: TestContext, + value: T, +) => boolean | ValidationError | Promise; + +export type TestOptions = { + value: any; + path?: string; + label?: string; + options: InternalOptions; + originalValue: any; + schema: TSchema; + sync?: boolean; +}; + +export type TestConfig = { + name?: string; + message?: Message; + test: TestFunction; + params?: ExtraParams; + exclusive?: boolean; +}; + +export type Test = ((opts: TestOptions, cb: Callback) => void) & { + OPTIONS: TestConfig; +}; + +export default function createValidation(config: { + name?: string; + test: TestFunction; + params?: ExtraParams; + message?: Message; +}) { + function validate( + { + value, + path = '', + label, + options, + originalValue, + sync, + ...rest + }: TestOptions, + cb: Callback, ) { const { name, test, params, message } = config; let { parent, context } = options; - function resolve(item) { + function resolve(item: any) { return Ref.isRef(item) ? item.getValue(value, parent, context) : item; } - function createError(overrides = {}) { + function createError(overrides: CreateErrorOptions = {}) { const nextParams = mapValues( { value, @@ -65,7 +131,7 @@ export default function createValidation(config) { try { result = test.call(ctx, value); - if (typeof result?.then === 'function') { + if (typeof (result as any)?.then === 'function') { throw new Error( `Validation test of type: "${ctx.type}" returned a Promise during a synchronous validate. ` + `This test will finish after the validate call has returned`, diff --git a/src/util/inherits.js b/src/util/inherits.js deleted file mode 100644 index a98ad455b..000000000 --- a/src/util/inherits.js +++ /dev/null @@ -1,12 +0,0 @@ -export default function inherits(ctor, superCtor, spec) { - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true, - }, - }); - - Object.assign(ctor.prototype, spec); -} diff --git a/src/util/isSchema.ts b/src/util/isSchema.ts index 939c64d32..9a99d1a73 100644 --- a/src/util/isSchema.ts +++ b/src/util/isSchema.ts @@ -1 +1,3 @@ -export default (obj: any): obj is MixedSchema => obj && obj.__isYupSchema__; +import Schema from '../Schema'; + +export default (obj: any): obj is Schema => obj && obj.__isYupSchema__; diff --git a/src/util/prependDeep.js b/src/util/prependDeep.ts similarity index 57% rename from src/util/prependDeep.js rename to src/util/prependDeep.ts index 6d00e75d4..85e7ea2c4 100644 --- a/src/util/prependDeep.js +++ b/src/util/prependDeep.ts @@ -1,13 +1,22 @@ import has from 'lodash/has'; import isSchema from './isSchema'; -let isObject = obj => Object.prototype.toString.call(obj) === '[object Object]'; +// function has +// (obj: T, prop: Key): obj is T & Record { +// return has(obj, prop) +// } -export default function prependDeep(target, source) { - for (var key in source) +let isObject = (obj: any): obj is {} => + Object.prototype.toString.call(obj) === '[object Object]'; + +export default function prependDeep( + target: Record, + source: Record, +) { + for (let key in source) if (has(source, key)) { - var sourceVal = source[key], - targetVal = target[key]; + let sourceVal = source[key]; + let targetVal = (target as any)[key]; if (targetVal === undefined) { target[key] = sourceVal; diff --git a/src/util/printValue.js b/src/util/printValue.ts similarity index 87% rename from src/util/printValue.js rename to src/util/printValue.ts index ce11e3be5..63140d8a0 100644 --- a/src/util/printValue.js +++ b/src/util/printValue.ts @@ -6,13 +6,13 @@ const symbolToString = const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/; -function printNumber(val) { +function printNumber(val: any) { if (val != +val) return 'NaN'; const isNegativeZero = val === 0 && 1 / val < 0; return isNegativeZero ? '-0' : '' + val; } -function printSimpleValue(val, quoteStrings = false) { +function printSimpleValue(val: any, quoteStrings = false) { if (val == null || val === true || val === false) return '' + val; const typeOf = typeof val; @@ -33,13 +33,13 @@ function printSimpleValue(val, quoteStrings = false) { return null; } -export default function printValue(value, quoteStrings) { +export default function printValue(value: any, quoteStrings?: boolean) { let result = printSimpleValue(value, quoteStrings); if (result !== null) return result; return JSON.stringify( value, - function(key, value) { + function (key, value) { let result = printSimpleValue(this[key], quoteStrings); if (result !== null) return result; return value; diff --git a/src/util/reach.js b/src/util/reach.ts similarity index 82% rename from src/util/reach.js rename to src/util/reach.ts index ab12a914a..504a6b5f2 100644 --- a/src/util/reach.js +++ b/src/util/reach.ts @@ -1,9 +1,9 @@ import { forEach } from 'property-expr'; -let trim = (part) => part.substr(0, part.length - 1).substr(1); +let trim = (part: string) => part.substr(0, part.length - 1).substr(1); -export function getIn(schema, path, value, context = value) { - let parent, lastPart, lastPartDebug; +export function getIn(schema: any, path: string, value: any, context = value) { + let parent: any, lastPart: string, lastPartDebug: string; // root path: '' if (!path) return { parent, parentPath: path, schema }; @@ -47,10 +47,10 @@ export function getIn(schema, path, value, context = value) { lastPartDebug = isBracket ? '[' + _part + ']' : '.' + _part; }); - return { schema, parent, parentPath: lastPart }; + return { schema, parent, parentPath: lastPart! }; } -const reach = (obj, path, value, context) => +const reach = (obj: {}, path: string, value: any, context: any) => getIn(obj, path, value, context).schema; export default reach; diff --git a/src/util/runTests.js b/src/util/runTests.ts similarity index 52% rename from src/util/runTests.js rename to src/util/runTests.ts index fd8b19314..04ee16412 100644 --- a/src/util/runTests.js +++ b/src/util/runTests.ts @@ -1,7 +1,21 @@ import ValidationError from '../ValidationError'; import { once } from './async'; - -export default function runTests(options, cb) { +import { TestOptions } from './createValidation'; +import { Callback } from '../types'; + +export type RunTest = (opts: TestOptions, cb: Callback) => void; + +export type TestRunOptions = { + endEarly?: boolean; + tests: RunTest[]; + args?: TestOptions; + errors?: ValidationError[]; + sort?: (a: ValidationError, b: ValidationError) => number; + path?: string; + value: any; + sync?: boolean; +}; +export default function runTests(options: TestRunOptions, cb: Callback): void { let { endEarly, tests, args, value, errors, sort, path } = options; let callback = once(cb); @@ -9,21 +23,21 @@ export default function runTests(options, cb) { if (!count) return callback(null, value); - const nestedErrors = []; + const nestedErrors = [] as ValidationError[]; errors = errors ? errors : []; for (let i = 0; i < tests.length; i++) { const test = tests[i]; - test(args, function finishTestRun(err) { + test(args!, function finishTestRun(err) { if (err) { // always return early for non validation errors if (!ValidationError.isError(err)) { - return callback(err); + return callback(err, value); } if (endEarly) { err.value = value; - return callback(err); + return callback(err, value); } nestedErrors.push(err); } @@ -33,12 +47,12 @@ export default function runTests(options, cb) { if (sort) nestedErrors.sort(sort); //show parent errors after the nested ones: name.first, name - if (errors.length) nestedErrors.push(...errors); + if (errors!.length) nestedErrors.push(...errors!); errors = nestedErrors; } - if (errors.length) { - callback(new ValidationError(errors, value, path)); + if (errors!.length) { + callback(new ValidationError(errors!, value, path), value); return; } diff --git a/src/util/sortByKeyOrder.js b/src/util/sortByKeyOrder.js deleted file mode 100644 index 9456e6498..000000000 --- a/src/util/sortByKeyOrder.js +++ /dev/null @@ -1,17 +0,0 @@ -function findIndex(arr, err) { - let idx = Infinity; - arr.some((key, ii) => { - if (err.path.indexOf(key) !== -1) { - idx = ii; - return true; - } - }); - - return idx; -} - -export default function sortByKeyOrder(keys) { - return (a, b) => { - return findIndex(keys, a) - findIndex(keys, b); - }; -} diff --git a/src/util/sortByKeyOrder.ts b/src/util/sortByKeyOrder.ts new file mode 100644 index 000000000..3c56c83f0 --- /dev/null +++ b/src/util/sortByKeyOrder.ts @@ -0,0 +1,19 @@ +import ValidationError from '../ValidationError'; + +function findIndex(arr: string[], err: ValidationError) { + let idx = Infinity; + arr.some((key, ii) => { + if (err.path?.indexOf(key) !== -1) { + idx = ii; + return true; + } + }); + + return idx; +} + +export default function sortByKeyOrder(keys: string[]) { + return (a: ValidationError, b: ValidationError) => { + return findIndex(keys, a) - findIndex(keys, b); + }; +} diff --git a/src/util/sortFields.js b/src/util/sortFields.ts similarity index 66% rename from src/util/sortFields.js rename to src/util/sortFields.ts index e3905d7c9..5e93e88b1 100644 --- a/src/util/sortFields.js +++ b/src/util/sortFields.ts @@ -4,12 +4,16 @@ import { split } from 'property-expr'; import Ref from '../Reference'; import isSchema from './isSchema'; +import { MixedSchema } from '..'; -export default function sortFields(fields, excludes = []) { - let edges = []; - let nodes = []; +export default function sortFields( + fields: Record, + excludes: string[] = [], +) { + let edges = [] as Array<[string, string]>; + let nodes = [] as string[]; - function addNode(depPath, key) { + function addNode(depPath: string, key: string) { var node = split(depPath)[0]; if (!~nodes.indexOf(node)) nodes.push(node); @@ -28,5 +32,5 @@ export default function sortFields(fields, excludes = []) { value._deps.forEach((path) => addNode(path, key)); } - return toposort.array(nodes, edges).reverse(); + return toposort.array(nodes, edges).reverse() as string[]; } diff --git a/src/util/toArray.ts b/src/util/toArray.ts new file mode 100644 index 000000000..9259a1b0b --- /dev/null +++ b/src/util/toArray.ts @@ -0,0 +1,3 @@ +export default function toArray(value?: null | T | T[]) { + return value == null ? [] : ([] as T[]).concat(value); +} diff --git a/test/mixed.js b/test/mixed.js index cda24df0d..f597713db 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -903,6 +903,8 @@ describe('Mixed Types ', () => { meta: undefined, label: undefined, tests: [], + oneOf: [], + notOneOf: [], fields: { foo: { type: 'array', @@ -914,10 +916,15 @@ describe('Mixed Types ', () => { params: undefined, }, ], + + oneOf: [], + notOneOf: [], innerType: { type: 'number', meta: undefined, label: undefined, + oneOf: [], + notOneOf: [], tests: [ { name: 'integer', diff --git a/test/yup.js b/test/yup.js index 8ee6543de..9a906cead 100644 --- a/test/yup.js +++ b/test/yup.js @@ -187,8 +187,8 @@ describe('Yup', function () { it('should reach through lazy', async () => { let types = { - '1': object({ foo: string() }), - '2': object({ foo: number() }), + 1: object({ foo: string() }), + 2: object({ foo: number() }), }; let err = await object({ @@ -205,7 +205,7 @@ describe('Yup', function () { err.message.should.match(/must be a `number` type/); }); - describe('parallel', () => { + xdescribe('parallel', () => { it('returns results', (done) => { Async.parallel( [ @@ -245,7 +245,7 @@ describe('Yup', function () { }); }); - describe('settled', () => { + xdescribe('settled', () => { it('handles empty', (done) => { Async.settled([], (err, results) => { expect(results).to.eql([]); diff --git a/yarn.lock b/yarn.lock index 3a494570f..66a17a395 100644 --- a/yarn.lock +++ b/yarn.lock @@ -263,7 +263,7 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-create-class-features-plugin@^7.10.4": +"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== @@ -662,6 +662,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-typescript@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25" + integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-arrow-functions@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" @@ -964,6 +971,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typescript@^7.10.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz#2b4879676af37342ebb278216dd090ac67f13abb" + integrity sha512-edJsNzTtvb3MaXQwj8403B7mZoGu9ElDJQZOKjGUnvilquxBA3IQoEIOvkX/1O8xfAsnHS/oQhe2w/IXrr+w0w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.10.4" + "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" @@ -1073,6 +1089,14 @@ "@babel/plugin-transform-react-jsx-source" "^7.10.4" "@babel/plugin-transform-react-pure-annotations" "^7.10.4" +"@babel/preset-typescript@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz#7d5d052e52a682480d6e2cc5aa31be61c8c25e36" + integrity sha512-SdYnvGPv+bLlwkF2VkJnaX/ni1sMNetcGI1+nThF1gyv6Ph8Qucc4ZZAjM5yZcE/AKRXIOTZz7eSRDWOEjPyRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typescript" "^7.10.4" + "@babel/runtime@^7.10.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c" @@ -1627,6 +1651,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash@^4.14.161": + version "4.14.161" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18" + integrity sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" From 2b367f0a41c5497c289e867529c858f0a9635cc6 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 1 Sep 2020 07:47:49 -0400 Subject: [PATCH 04/31] wip: spec --- package.json | 3 + src/Schema.ts | 1 - src/array.ts | 4 +- src/index.ts | 24 ++++- src/mixed.ts | 233 +++++++++++++++++++++++++++++----------- src/object.ts | 106 +++++++++++++----- src/string.ts | 57 +++++++++- src/types.ts | 2 +- src/util/prependDeep.ts | 5 +- src/util/sortFields.ts | 7 +- test/.eslintrc.js | 12 +++ test/tsconfig.json | 2 +- test/types.ts | 75 +++++++++++++ test/yup.js | 90 ---------------- yarn.lock | 96 ++++++++++++++++- 15 files changed, 517 insertions(+), 200 deletions(-) create mode 100644 test/.eslintrc.js create mode 100644 test/types.ts diff --git a/package.json b/package.json index f23c00af3..3ad5fc714 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "@babel/cli": "7.10.5", "@babel/core": "7.11.4", "@babel/preset-typescript": "^7.10.4", + "@typescript-eslint/parser": "^4.0.1", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.3.0", @@ -87,6 +88,8 @@ "eslint-plugin-jest": "^23.20.0", "eslint-plugin-react": "^7.20.6", "eslint-plugin-react-hooks": "^4.1.0", + "eslint-plugin-ts-expect": "^1.0.1", + "eslint-plugin-typescript": "^0.14.0", "husky": "^4.2.5", "jest": "^26.4.2", "lint-staged": "^10.2.11", diff --git a/src/Schema.ts b/src/Schema.ts index a3a15d6f6..ebae2e66c 100644 --- a/src/Schema.ts +++ b/src/Schema.ts @@ -1,6 +1,5 @@ import { ResolveOptions } from './Condition'; import { ValidateOptions, Callback, MessageParams } from './types'; -import { Params } from './ValidationError'; export interface CastOptions { parent?: any; diff --git a/src/array.ts b/src/array.ts index 2ac3e243d..4f876a69f 100644 --- a/src/array.ts +++ b/src/array.ts @@ -80,8 +80,8 @@ export default class ArraySchema extends MixedSchema { let sync = options.sync; let path = options.path; let innerType = this.innerType; - let endEarly = this._option('abortEarly', options); - let recursive = this._option('recursive', options); + let endEarly = options.abortEarly ?? this.spec.abortEarly; + let recursive = options.recursive ?? this.spec.recursive; let originalValue = options.originalValue != null ? options.originalValue : _value; diff --git a/src/index.ts b/src/index.ts index b75cf20b7..cc6ec1dfa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,16 @@ -import MixedSchema, { create as mixedCreate } from './mixed'; +import MixedSchema, { + create as mixedCreate, + ResolveCast, + SchemaSpec, + ResolveRequired, + Asserts, + TypeOf, +} from './mixed'; import BoolSchema, { create as boolCreate } from './boolean'; import StringSchema, { create as stringCreate } from './string'; import NumberSchema, { create as numberCreate } from './number'; import DateSchema, { create as dateCreate } from './date'; -import ObjectSchema, { create as objectCreate } from './object'; +import ObjectSchema, { create as objectCreate, AssertsShape } from './object'; import ArraySchema, { create as arrayCreate } from './array'; import Ref, { create as refCreate } from './Reference'; import Lazy, { create as lazyCreate } from './Lazy'; @@ -51,3 +58,16 @@ export { ObjectSchema, ArraySchema, }; + +// type TypeOf = T extends { spec: infer TSpec } +// ? TSpec extends SchemaSpec +// ? ResolveCast +// : never +// : never; + +// type Asserts = TypeOf & +// (T extends { spec: infer TSpec } +// ? TSpec extends SchemaSpec +// ? ResolveRequired, TSpec> +// : never +// : never); diff --git a/src/mixed.ts b/src/mixed.ts index 0454c13cc..04100f311 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -4,7 +4,7 @@ import cloneDeepWith from 'lodash/cloneDeepWith'; import { mixed as locale } from './locale'; import Condition, { ConditionOptions, ResolveOptions } from './Condition'; import runTests from './util/runTests'; -import prependDeep from './util/prependDeep'; +import merge from './util/prependDeep'; import isSchema from './util/isSchema'; import createValidation, { TestFunction, @@ -28,6 +28,7 @@ import Schema, { SchemaRefDescription, SchemaDescription, } from './Schema'; +import { ValidationError } from '.'; class RefSet { list: Set; @@ -83,6 +84,22 @@ class RefSet { } } +const UNSET = '@@UNSET_DEFAULT'; + +export interface SchemaSpec { + hasDefault: boolean; + default: TDefault; + abortEarly: boolean; + nullable: boolean; + strip: boolean; + strict: boolean; + recursive: boolean; + noUnknown: boolean; + label: string | undefined; + required: boolean; + meta: any; +} + export interface SchemaOptions { default?: (this: T) => any; type: string; @@ -92,17 +109,99 @@ export function create(options?: SchemaOptions) { return new MixedSchema(options); } +// export type SchemaTypeSpec< +// Default = undefined, +// Nullable extends boolean = false, +// Strip extends boolean = false +// > = { 1: Default; 2: Nullable; 3: Strip }; + +// type ResolveNullable = TSpec extends SchemaTypeSpec ? TType extends null : never + +// export type OutputType = ; +// TType = any, +// TSpec extends SchemaTypeSpec = SchemaTypeSpec +type UNSET = { 1: '@@UNSET_DEFAULT' }; + +type ResolveNullable< + TType, + TSpec extends SchemaSpec +> = TSpec['nullable'] extends true ? TType | null : TType; + +type ResolveDefault = TSpec extends SchemaSpec< + infer Default +> + ? Default extends UNSET + ? TType + : Default extends undefined + ? TType & undefined + : Exclude + : never; + +// type TypeOfShape> = { +// [K in keyof Shape]: ReturnType; +// }; + +export type ResolveCast = ResolveDefault< + ResolveNullable, + TSpec +>; + +export type ResolveRequired< + TType, + TSpec extends SchemaSpec +> = TSpec['required'] extends true ? NonNullable : TType; + +export type TypedSchema = { _tsType: any; _tsValidate: any }; + +// type Keys> = { fields: TShape }; + +// type CastChildren = T extends Keys ? { } + +export type TypeOf = T extends { spec: infer TSpec } + ? TSpec extends SchemaSpec + ? ResolveCast + : never + : never; + +export type Asserts = T extends { spec: infer TSpec } + ? TSpec extends SchemaSpec + ? ResolveRequired, TSpec> + : never + : never; + export default class MixedSchema implements Schema { - private _deps: string[]; + readonly type: string; + + readonly _tsType!: any; + readonly _tsValidate!: any; + + // readonly _tsType2!: TSpec['required'] extends true + // ? NonNullable + // : TType; + + readonly __isYupSchema__ = true; - __isYupSchema__ = true; - protected _options: Partial; + readonly deps: readonly string[] = []; protected _exclusive: Record = Object.create(null); - readonly type: string; protected _whitelist: RefSet = new RefSet(); protected _blacklist: RefSet = new RefSet(); + spec: SchemaSpec = { + nullable: false, + default: undefined as any, + hasDefault: false, + strip: false, + strict: false, + abortEarly: true, + required: false, + recursive: true, + noUnknown: false, + + label: undefined, + meta: undefined, + } as const; + tests: Test[]; transforms: TransformFunction[]; // TODO @@ -110,18 +209,14 @@ export default class MixedSchema implements Schema { protected _label?: string; protected _meta: any; - private _default?: unknown; - private _nullable: boolean = false; - private _conditions: Condition[]; - private _defaultDefault: ((this: this) => unknown) | undefined; + private conditions: Condition[] = []; + // protected configuredDefault: ((this: this) => unknown) | undefined; - private _validating: boolean = false; + // private _validating: boolean = false; private _typeError?: Test; private _whitelistError?: Test; private _blacklistError?: Test; - private _strip: boolean = false; - optional!: () => MixedSchema; static create( @@ -132,10 +227,6 @@ export default class MixedSchema implements Schema { } constructor(options: SchemaOptions = { type: 'mixed' }) { - this._deps = []; - this._conditions = []; - this._options = { abortEarly: true, recursive: true }; - this.tests = []; this.transforms = []; @@ -143,8 +234,6 @@ export default class MixedSchema implements Schema { this.typeError(locale.notType); }); - if (has(options, 'default')) this._defaultDefault = options.default; - this.type = options.type; } @@ -191,18 +280,21 @@ export default class MixedSchema implements Schema { return result; } - concat(schema: this) { + concat(schema: MixedSchema): MixedSchema { + // @ts-ignore if (!schema || schema === this) return this; if (schema.type !== this.type && this.type !== 'mixed') throw new TypeError( - `You cannot \`concat()\` schema's of different types: ${this._type} and ${schema._type}`, + `You cannot \`concat()\` schema's of different types: ${this.type} and ${schema.type}`, ); - var next = prependDeep(schema.clone() as any, this as any) as this; + var next = merge(schema.clone() as any, this as any) as any; // new undefined default is overridden by old non-undefined one, revert - if (has(schema, '_default')) next._default = schema._default; + if (schema.spec.hasDefault) { + next.spec.default = schema.spec.default; + } next.tests = this.tests; next._exclusive = this._exclusive; @@ -232,18 +324,18 @@ export default class MixedSchema implements Schema { // abstract ?(value: any): boolean; isType(v: any) { - if (this._nullable && v === null) return true; + if (this.spec.nullable && v === null) return true; return this._typeCheck(v); } resolve(options: ResolveOptions) { let schema = this; - if (schema._conditions.length) { - let conditions = schema._conditions; + if (schema.conditions.length) { + let conditions = schema.conditions; schema = schema.clone(); - schema._conditions = []; + schema.conditions = []; schema = conditions.reduce( (schema, condition) => condition.resolve(schema, options), schema, @@ -262,7 +354,7 @@ export default class MixedSchema implements Schema { * @param {*=} options.parent * @param {*=} options.context */ - cast(value: any, options: CastOptions = {}) { + cast(value: any, options: CastOptions = {}): TypeOf { let resolvedSchema = this.resolve({ value, ...options, @@ -303,7 +395,7 @@ export default class MixedSchema implements Schema { rawValue, ); - if (value === undefined && has(this, '_default')) { + if (value === undefined && this.spec.default !== UNSET) { value = this.default(); } @@ -320,15 +412,15 @@ export default class MixedSchema implements Schema { path, from = [], originalValue = _value, - strict = this._options.strict, - abortEarly = this._options.abortEarly, + strict = this.spec.strict, + abortEarly = this.spec.abortEarly, } = options; let value = _value; if (!strict) { - this._validating = true; + // this._validating = true; value = this._cast(value, { assert: false, ...options }); - this._validating = false; + // this._validating = false; } // value is cast, we can check if it meets type requirements let args = { @@ -375,8 +467,12 @@ export default class MixedSchema implements Schema { ); } - validate(value: any, options: ValidateOptions): Promise; - validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { + validate(value: any, options?: ValidateOptions): Promise>; + validate( + value: any, + options: ValidateOptions = {}, + maybeCb?: Callback>, + ) { let schema = this.resolve({ ...options, value }); // callback case is for nested validations @@ -390,9 +486,9 @@ export default class MixedSchema implements Schema { ); } - validateSync(value: any, options: ValidateOptions = {}) { + validateSync(value: any, options: ValidateOptions = {}): Asserts { let schema = this.resolve({ ...options, value }); - let result; + let result: any; schema._validate(value, { ...options, sync: true }, (err, value) => { if (err) throw err; @@ -402,21 +498,21 @@ export default class MixedSchema implements Schema { return result; } - isValid(value: any, options: ValidateOptions) { + isValid(value: any, options: ValidateOptions): Promise { return this.validate(value, options) .then(() => true) .catch((err) => { - if (err.name === 'ValidationError') return false; + if (ValidationError.isError(err)) return false; throw err; }); } - isValidSync(value: any, options: ValidateOptions) { + isValidSync(value: any, options: ValidateOptions): boolean { try { this.validateSync(value, options); return true; } catch (err) { - if (err.name === 'ValidationError') return false; + if (ValidationError.isError(err)) return false; throw err; } } @@ -426,13 +522,17 @@ export default class MixedSchema implements Schema { return schema.default(); } - default(): any; // FIXME(ts): typed default - default(def: TDefault | (() => TDefault)): this; + default(): this['spec']['default']; // FIXME(ts): typed default + default( + def: TDefault | (() => TDefault), + ): WithSpec; default(def?: TDefault | (() => TDefault)) { if (arguments.length === 0) { - var defaultValue = has(this, '_default') - ? this._default - : this._defaultDefault; + let defaultValue = this.spec.default; + + if (defaultValue == null) { + return defaultValue; + } return typeof defaultValue === 'function' ? defaultValue.call(this) @@ -440,13 +540,14 @@ export default class MixedSchema implements Schema { } var next = this.clone(); - next._default = def; + next.spec.hasDefault = true; + next.spec.default = def; return next; } strict(isStrict = true) { var next = this.clone(); - next._options.strict = isStrict; + next.spec.strict = isStrict; return next; } @@ -454,7 +555,7 @@ export default class MixedSchema implements Schema { return value != null; } - required(message = locale.required) { + required(message = locale.required): WithSpec { return this.test({ message, name: 'required', @@ -462,7 +563,7 @@ export default class MixedSchema implements Schema { test(value) { return this.schema._isPresent(value); }, - }); + }) as any; } notRequired() { @@ -471,10 +572,12 @@ export default class MixedSchema implements Schema { return next; } - nullable(isNullable = true) { + nullable(isNullable?: true): WithSpec; + nullable(isNullable: false): WithSpec; + nullable(isNullable = true): this { var next = this.clone(); - next._nullable = isNullable; - return next; + next.spec.nullable = isNullable; + return next as any; } transform(fn: TransformFunction) { @@ -563,10 +666,11 @@ export default class MixedSchema implements Schema { let deps = toArray(keys).map((key) => new Ref(key)); deps.forEach((dep) => { - if (dep.isSibling) next._deps.push(dep.key); + // @ts-ignore + if (dep.isSibling) next.deps.push(dep.key); }); - next._conditions.push(new Condition(deps, options!)); + next.conditions.push(new Condition(deps, options!)); return next; } @@ -645,17 +749,10 @@ export default class MixedSchema implements Schema { strip(strip = true) { let next = this.clone(); - next._strip = strip; + next.spec.strip = strip; return next; } - protected _option( - key: Key, - overrides: ValidateOptions, - ) { - return has(overrides, key) ? overrides[key] : this._options[key]; - } - describe() { const next = this.clone(); const description: SchemaDescription = { @@ -675,7 +772,7 @@ export default class MixedSchema implements Schema { } defined(message = locale.defined) { - return this.nullable().test({ + return this.test({ message, name: 'defined', exclusive: true, @@ -705,8 +802,14 @@ for (const method of ['validate', 'validateSync']) }); }; -for (const alias of ['equals', 'is']) +for (const alias of ['equals', 'is'] as const) MixedSchema.prototype[alias] = MixedSchema.prototype.oneOf; -for (const alias of ['not', 'nope']) + +for (const alias of ['not', 'nope'] as const) MixedSchema.prototype[alias] = MixedSchema.prototype.notOneOf; + MixedSchema.prototype.optional = MixedSchema.prototype.notRequired; + +type WithSpec> = T & { + spec: T['spec'] & TSpec; +}; diff --git a/src/object.ts b/src/object.ts index b641aad9a..497bd5b04 100644 --- a/src/object.ts +++ b/src/object.ts @@ -5,8 +5,8 @@ import mapKeys from 'lodash/mapKeys'; import mapValues from 'lodash/mapValues'; import { getter } from 'property-expr'; -import MixedSchema from './mixed'; -import { object as locale } from './locale'; +import MixedSchema, { SchemaSpec, Asserts, TypeOf, TypedSchema } from './mixed'; +import { object as locale, string } from './locale'; import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; @@ -17,22 +17,69 @@ import { ValidationError } from '.'; let isObject = (obj: any): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; -function unknown(ctx, value) { +function unknown(ctx: ObjectSchema, value: any) { let known = Object.keys(ctx.fields); return Object.keys(value).filter((key) => known.indexOf(key) === -1); } type ObjectShape = Record; +// type Obj = Record; + export function create(spec?: TShape) { return new ObjectSchema(spec); } +// export type ObjectSchemaSpec = ; + +type InferFromShape = { + [K in keyof TShape]: TShape[K]['_tsType']; +}; + +type DefaultFromShape = { + [K in keyof TShape]: TShape[K]['spec']['default']; +}; +// type TypeOfShape< +// T extends TypedSchema, +// Shape extends ObjectShape, +// TT = Asserts +// > = TT extends {} +// ? TT & +// { +// [K in keyof Asserts]: Shape extends Record +// ? ReturnType +// : Asserts[K]; +// } +// : TT; + +export type TypeOfShape = { + [K in keyof Shape]: ReturnType; +}; + +export type AssertsShape = { + [K in keyof Shape]: ReturnType; +}; + +// export default interface ObjectSchema +// extends MixedSchema { +// cast(value: any, options?: any): TypeOfShape; + +// // validate(value: any, options?: any): Promise>; +// } + export default class ObjectSchema< TShape extends ObjectShape = ObjectShape > extends MixedSchema { fields: TShape; + _shape!: TShape; + _tsType!: TypeOfShape; + _tsValidate!: AssertsShape; + + spec!: SchemaSpec> & { + noUnknown: boolean; + }; + private _sortErrors: ( a: import('/Users/jquense/src/yup/src/ValidationError').default, b: import('/Users/jquense/src/yup/src/ValidationError').default, @@ -41,9 +88,17 @@ export default class ObjectSchema< private _excludedEdges: string[]; constructor(spec?: TShape) { - super({ - type: 'object', - default(this: ObjectSchema) { + super({ type: 'object' }); + + this.fields = Object.create(null); + + this._sortErrors = sortByKeyOrder([]); + + this._nodes = []; + this._excludedEdges = []; + + this.withMutation(() => { + this.spec.default = () => { if (!this._nodes.length) return undefined; let dft = {} as Record; @@ -53,18 +108,9 @@ export default class ObjectSchema< ? this.fields[key].default() : undefined; }); - return dft; - }, - }); - - this.fields = Object.create(null); - - this._sortErrors = sortByKeyOrder([]); + return dft as any; + }; - this._nodes = []; - this._excludedEdges = []; - - this.withMutation(() => { this.transform(function coerce(value) { if (typeof value === 'string') { try { @@ -97,7 +143,7 @@ export default class ObjectSchema< let fields = this.fields; - let strip = this._option('stripUnknown', options) === true; + let strip = options.stripUnknown ?? this.spec.noUnknown; let props = this._nodes.concat( Object.keys(value).filter((v) => this._nodes.indexOf(v) === -1), ); @@ -108,6 +154,8 @@ export default class ObjectSchema< parent: intermediateValue, __validating: options.__validating || false, }; + // let endEarly = options.abortEarly ?? this.spec.abortEarly; + // let recursive = options.recursive ?? this.spec.recursive; let isChanged = false; for (const prop of props) { @@ -116,7 +164,7 @@ export default class ObjectSchema< if (field) { let fieldValue; - let strict = field._options?.strict; + let strict = field.spec?.strict; let inputValue = value[prop]; @@ -130,7 +178,7 @@ export default class ObjectSchema< parent: intermediateValue, }); - if (field._strip === true) { + if (field.spec?.strip) { isChanged = isChanged || prop in value; continue; } @@ -166,8 +214,8 @@ export default class ObjectSchema< sync, from = [], originalValue = _value, - abortEarly = this._options.abortEarly, - recursive = this._options.recursive, + abortEarly = this.spec.abortEarly, + recursive = this.spec.recursive, } = opts; from = [{ schema: this, value: originalValue }, ...from]; @@ -238,8 +286,8 @@ export default class ObjectSchema< }); } - concat(schema: ObjectSchema) { - var next = super.concat(schema); + concat(schema: ObjectSchema): ObjectSchema { + var next = super.concat(schema) as ObjectSchema; next._nodes = sortFields(next.fields, next._excludedEdges); @@ -247,11 +295,11 @@ export default class ObjectSchema< } shape( - schema: TNextShape, + additions: TNextShape, excludes: [string, string][] = [], - ) { + ): ObjectSchema { let next = this.clone(); - let fields = Object.assign(next.fields, schema); + let fields = Object.assign(next.fields, additions); next.fields = fields; next._sortErrors = sortByKeyOrder(Object.keys(fields)); @@ -266,7 +314,7 @@ export default class ObjectSchema< next._nodes = sortFields(fields, next._excludedEdges); - return next; + return next as any; } from(from: string, to: keyof TShape, alias?: boolean) { @@ -307,7 +355,7 @@ export default class ObjectSchema< }, }); - next._options.stripUnknown = noAllow; + next.spec.noUnknown = noAllow; return next; } diff --git a/src/string.ts b/src/string.ts index a1cc43eac..00b2f35be 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,4 +1,4 @@ -import MixedSchema from './mixed'; +import MixedSchema, { SchemaSpec } from './mixed'; import { string as locale } from './locale'; import isAbsent from './util/isAbsent'; import Reference from './Reference'; @@ -25,6 +25,9 @@ export function create() { } export default class StringSchema extends MixedSchema { + _tsType!: string | undefined; + _tsValidate!: string | undefined; + constructor() { super({ type: 'string' }); @@ -173,4 +176,56 @@ export default class StringSchema extends MixedSchema { isAbsent(value) || value === value.toUpperCase(), }); } + + // required(msg?: any): StringSchema { + // return super.required(msg) as any; + // } + + // nullable(isNullable?: true): StringSchema; + // nullable(isNullable: false): StringSchema>; + // nullable(isNullable?: boolean): StringSchema> { + // return super.nullable(isNullable); + // } } + +// export default interface StringSchema< +// TType extends Maybe = string, +// TSpec extends SchemaSpec = SchemaSpec +// > { +// nullable(isNullable?: true): StringSchema; +// nullable(isNullable: false): StringSchema>; +// required(msg?: any): StringSchema; + +// // required( +// // message?: TestOptionsMessage, +// // ): StringSchema>; +// // defined(): StringSchema>; +// // notRequired(): StringSchema; +// // oneOf( +// // arrayOfValues: ReadonlyArray, +// // message?: MixedLocale['oneOf'], +// // ): StringSchema>; +// // equals( +// // arrayOfValues: ReadonlyArray, +// // message?: MixedLocale['oneOf'], +// // ): StringSchema>; +// // /* +// // All TestFunction generics are intentionally T with (undefined | null) as previous .required / .defined / .nullable +// // will narrow out those types, and tests run for (undefined | null) even if they're not allowed. +// // */ +// // test( +// // name: string, +// // message: TestOptionsMessage, +// // test: TestFunction, +// // ): this; +// // test( +// // name: string, +// // message: TestOptionsMessage, +// // test: AssertingTestFunction, +// // ): StringSchema; +// // test( +// // options: AssertingTestOptions>, +// // ): StringSchema; +// // test(options: TestOptions>): this; +// // optional(): StringSchema; +// } diff --git a/src/types.ts b/src/types.ts index d61ee63e4..a5eae03db 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,6 @@ import type Schema from './Schema'; -export type Callback = (err: Error | null, value?: any) => void; +export type Callback = (err: Error | null, value?: T) => void; export type TransformFunction = ( this: T, diff --git a/src/util/prependDeep.ts b/src/util/prependDeep.ts index 85e7ea2c4..ab875b864 100644 --- a/src/util/prependDeep.ts +++ b/src/util/prependDeep.ts @@ -9,7 +9,7 @@ import isSchema from './isSchema'; let isObject = (obj: any): obj is {} => Object.prototype.toString.call(obj) === '[object Object]'; -export default function prependDeep( +export default function merge( target: Record, source: Record, ) { @@ -25,8 +25,7 @@ export default function prependDeep( } else if (isSchema(targetVal)) { if (isSchema(sourceVal)) target[key] = sourceVal.concat(targetVal); } else if (isObject(targetVal)) { - if (isObject(sourceVal)) - target[key] = prependDeep(targetVal, sourceVal); + if (isObject(sourceVal)) target[key] = merge(targetVal, sourceVal); } else if (Array.isArray(targetVal)) { if (Array.isArray(sourceVal)) target[key] = sourceVal.concat(targetVal); } diff --git a/src/util/sortFields.ts b/src/util/sortFields.ts index 5e93e88b1..e676a8b93 100644 --- a/src/util/sortFields.ts +++ b/src/util/sortFields.ts @@ -1,10 +1,11 @@ import has from 'lodash/has'; +// @ts-expect-error import toposort from 'toposort'; import { split } from 'property-expr'; import Ref from '../Reference'; import isSchema from './isSchema'; -import { MixedSchema } from '..'; +import type MixedSchema from '../mixed'; export default function sortFields( fields: Record, @@ -28,8 +29,8 @@ export default function sortFields( if (!~nodes.indexOf(key)) nodes.push(key); if (Ref.isRef(value) && value.isSibling) addNode(value.path, key); - else if (isSchema(value) && value._deps) - value._deps.forEach((path) => addNode(path, key)); + else if (isSchema(value) && value.deps) + value.deps.forEach((path) => addNode(path, key)); } return toposort.array(nodes, edges).reverse() as string[]; diff --git a/test/.eslintrc.js b/test/.eslintrc.js new file mode 100644 index 000000000..36df48cc3 --- /dev/null +++ b/test/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + + plugins: ['ts-expect'], + rules: { + 'ts-expect/expect': 'error', + }, +}; diff --git a/test/tsconfig.json b/test/tsconfig.json index fcdfa1619..55b810895 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../", + "extends": "../tsconfig.json", "compilerOptions": { "types": ["node", "jest"], "rootDir": "../" diff --git a/test/types.ts b/test/types.ts new file mode 100644 index 000000000..a9924a594 --- /dev/null +++ b/test/types.ts @@ -0,0 +1,75 @@ +import { Asserts } from '../src/mixed'; +import { string, object, mixed, number } from '../src'; + +let schema = object({ + str: string().nullable(), +}).shape({ + num: number(), +}); + +const fff = mixed().nullable(); + +// type TSchema = typeof schema; +// type tt = TSchema['_tsValidate']; + +// const shape = { +// str: string().nullable(), +// }; + +// async function foo() { +// const validate = await schema.validate({}); + +// } + +// type _objNestedNullableRequired = AssertsShape; + +let f = schema.cast(undefined); + +// $ExpectType { str: any, num: any } | undefined +f; + +// $ExpectType string | null | undefined +f!.str; + +const strNullableRequired = string().nullable().required(); +// $ExpectType string +type _strNullableRequired = Asserts; + +const strNullable = string().nullable(); + +// $ExpectType string | null | undefined +type _strNullable = Asserts; + +const strPlain = string(); + +type fff = typeof strPlain['spec']['hasDefault']; + +// $ExpectType string | undefined +type _strPlain = Asserts; + +const strRequired = string().required(); +// $ExpectType string +type _strRequired = Asserts; + +const strDefault = string().nullable().default(undefined); +const strDefault2 = string().nullable().default(''); + +// $ExpectType undefined +strDefault.default(); + +// $ExpectType string +strDefault2.default(); + +// $ExpectType string | null +strDefault2.cast(undefined); + +// async function foo() { +// ff = await str.validate(undefined, {}); +// ff = await str2.validate(null, {}); +// } + +let objWithDefault = object({ + str: string().nullable().default(''), + num: number().default(3), + num2: number(), +}); diff --git a/test/yup.js b/test/yup.js index 9a906cead..69264d5f3 100644 --- a/test/yup.js +++ b/test/yup.js @@ -204,94 +204,4 @@ describe('Yup', function () { .should.be.rejected(); err.message.should.match(/must be a `number` type/); }); - - xdescribe('parallel', () => { - it('returns results', (done) => { - Async.parallel( - [ - (cb) => Async.asCallback(Promise.resolve(1), cb), - (cb) => Async.asCallback(Promise.resolve(2), cb), - (cb) => Async.asCallback(Promise.resolve(3), cb), - (cb) => Async.asCallback(Promise.resolve(4), cb), - ], - (err, results) => { - expect(results).to.eql([1, 2, 3, 4]); - done(); - }, - ); - }); - - it('fails fast', (done) => { - Async.parallel( - [ - (cb) => Async.asCallback(Promise.resolve(1), cb), - (cb) => Async.asCallback(Promise.reject(2), cb), - (cb) => Async.asCallback(Promise.reject(3), cb), - (cb) => Async.asCallback(Promise.resolve(4), cb), - ], - (err, results) => { - expect(results).to.equal(undefined); - expect(err).to.equal(2); - done(); - }, - ); - }); - - it('handles empty', (done) => { - Async.parallel([], (err, results) => { - expect(results).to.eql([]); - done(); - }); - }); - }); - - xdescribe('settled', () => { - it('handles empty', (done) => { - Async.settled([], (err, results) => { - expect(results).to.eql([]); - done(); - }); - }); - - it('returns results', (done) => { - Async.settled( - [ - (cb) => Async.asCallback(Promise.resolve(1), cb), - (cb) => Async.asCallback(Promise.resolve(2), cb), - (cb) => Async.asCallback(Promise.resolve(3), cb), - (cb) => Async.asCallback(Promise.resolve(4), cb), - ], - (err, results) => { - expect(results).to.eql([ - { fulfilled: true, value: 1 }, - { fulfilled: true, value: 2 }, - { fulfilled: true, value: 3 }, - { fulfilled: true, value: 4 }, - ]); - done(); - }, - ); - }); - - it('fails fast', (done) => { - Async.settled( - [ - (cb) => Async.asCallback(Promise.resolve(1), cb), - (cb) => Async.asCallback(Promise.reject(2), cb), - (cb) => Async.asCallback(Promise.reject(3), cb), - (cb) => Async.asCallback(Promise.resolve(4), cb), - ], - (err, results) => { - expect(results).to.eql([ - { fulfilled: true, value: 1 }, - { fulfilled: false, value: 2 }, - { fulfilled: false, value: 3 }, - { fulfilled: true, value: 4 }, - ]); - expect(err).to.equal(null); - done(); - }, - ); - }); - }); }); diff --git a/yarn.lock b/yarn.lock index 66a17a395..2a3211627 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1731,6 +1731,16 @@ "@typescript-eslint/typescript-estree" "2.22.0" eslint-scope "^5.0.0" +"@typescript-eslint/experimental-utils@^2.15.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" + integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.34.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + "@typescript-eslint/parser@^2.0.0": version "2.22.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.22.0.tgz#8eeb6cb6de873f655e64153397d4790898e149d0" @@ -1741,6 +1751,29 @@ "@typescript-eslint/typescript-estree" "2.22.0" eslint-visitor-keys "^1.1.0" +"@typescript-eslint/parser@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.0.1.tgz#73772080db7a7a4534a35d719e006f503e664dc3" + integrity sha512-1+qLmXHNAWSQ7RB6fdSQszAiA7JTwzakj5cNYjBTUmpH2cqilxMZEIV+DRKjVZs8NzP3ALmKexB0w/ExjcK9Iw== + dependencies: + "@typescript-eslint/scope-manager" "4.0.1" + "@typescript-eslint/types" "4.0.1" + "@typescript-eslint/typescript-estree" "4.0.1" + debug "^4.1.1" + +"@typescript-eslint/scope-manager@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.0.1.tgz#24d93c3000bdfcc5a157dc4d32b742405a8631b5" + integrity sha512-u3YEXVJ8jsj7QCJk3om0Y457fy2euEOkkzxIB/LKU3MdyI+FJ2gI0M4aKEaXzwCSfNDiZ13a3lDo5DVozc+XLQ== + dependencies: + "@typescript-eslint/types" "4.0.1" + "@typescript-eslint/visitor-keys" "4.0.1" + +"@typescript-eslint/types@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.0.1.tgz#1cf72582f764931f085cb8230ff215980fe467b2" + integrity sha512-S+gD3fgbkZYW2rnbjugNMqibm9HpEjqZBZkTiI3PwbbNGWmAcxolWIUwZ0SKeG4Dy2ktpKKaI/6+HGYVH8Qrlg== + "@typescript-eslint/typescript-estree@2.22.0": version "2.22.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.22.0.tgz#a16ed45876abf743e1f5857e2f4a1c3199fd219e" @@ -1754,6 +1787,41 @@ semver "^6.3.0" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" + integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/typescript-estree@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.1.tgz#29a43c7060641ec51c902d9f50ac7c5866ec479f" + integrity sha512-zGzleORFXrRWRJAMLTB2iJD1IZbCPkg4hsI8mGdpYlKaqzvKYSEWVAYh14eauaR+qIoZVWrXgYSXqLtTlxotiw== + dependencies: + "@typescript-eslint/types" "4.0.1" + "@typescript-eslint/visitor-keys" "4.0.1" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/visitor-keys@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.1.tgz#d4e8de62775f2a6db71c7e8539633680039fdd6c" + integrity sha512-yBSqd6FjnTzbg5RUy9J+9kJEyQjTI34JdGMJz+9ttlJzLCnGkBikxw+N5n2VDcc3CesbIEJ0MnZc5uRYnrEnCw== + dependencies: + "@typescript-eslint/types" "4.0.1" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -4395,6 +4463,20 @@ eslint-plugin-react@^7.20.6: resolve "^1.17.0" string.prototype.matchall "^4.0.2" +eslint-plugin-ts-expect@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-ts-expect/-/eslint-plugin-ts-expect-1.0.1.tgz#5a2c4f81a2413b2de17dfed72de260c201386e6d" + integrity sha512-ZGaCifWWRvOzc6MwEC1RelMPMgvB2q/e0VGhhtdviQ3TErvX6471Bz7Rrisd0sVbV5wBCN//vqdi8tb69RpwGA== + dependencies: + "@typescript-eslint/experimental-utils" "^2.15.0" + +eslint-plugin-typescript@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-typescript/-/eslint-plugin-typescript-0.14.0.tgz#068549c3f4c7f3f85d88d398c29fa96bf500884c" + integrity sha512-2u1WnnDF2mkWWgU1lFQ2RjypUlmRoBEvQN02y9u+IL12mjWlkKFGEBnVsjs9Y8190bfPQCvWly1c2rYYUSOxWw== + dependencies: + requireindex "~1.1.0" + eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -4411,7 +4493,7 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: +eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -4423,6 +4505,11 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + eslint@^7.0.0, eslint@^7.7.0: version "7.7.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073" @@ -5281,7 +5368,7 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" -globby@*: +globby@*, globby@^11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== @@ -9174,6 +9261,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requireindex@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162" + integrity sha1-5UBLgVV+91225JxacgBIk/4D4WI= + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" From 689e5d43b57ed3f1364d0e71e7a167ca8e53bb59 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Fri, 4 Sep 2020 11:27:12 -0400 Subject: [PATCH 05/31] WIP --- jest-sync.config.json | 4 +- package.json | 4 +- src/mixed.ts | 211 +++++------ src/object.ts | 11 +- src/string.ts | 34 +- src/util/types.ts | 102 ++++++ test/mixed.js | 2 +- test/types.ts | 152 +++++--- types/index.d.ts | 823 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1153 insertions(+), 190 deletions(-) create mode 100644 src/util/types.ts create mode 100644 types/index.d.ts diff --git a/jest-sync.config.json b/jest-sync.config.json index 207c4c7e0..aadc50d75 100644 --- a/jest-sync.config.json +++ b/jest-sync.config.json @@ -6,6 +6,6 @@ "testEnvironment": "node", "setupFilesAfterEnv": ["./test-setup.js"], "roots": ["test"], - "testRegex": "\\.js", - "testPathIgnorePatterns": ["helpers\\.js"] + "testRegex": "\\.(t|j)s$", + "testPathIgnorePatterns": ["helpers\\.js", "\\.eslintrc\\.js", "types\\.ts"] } diff --git a/package.json b/package.json index 3ad5fc714..11ae8ff2b 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,9 @@ ], "testRegex": "\\.(j|t)s$", "testPathIgnorePatterns": [ - "helpers\\.js" + "helpers\\.js", + "\\.eslintrc\\.js", + "types\\.ts" ] }, "devDependencies": { diff --git a/src/mixed.ts b/src/mixed.ts index 04100f311..d1db776f4 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -21,7 +21,7 @@ import { TransformFunction, Message, Callback, - InternalOptions, + InternalOptions,, Maybe } from './types'; import Schema, { CastOptions, @@ -29,6 +29,7 @@ import Schema, { SchemaDescription, } from './Schema'; import { ValidationError } from '.'; +import { ResolveInput, ResolveOutput, SetNullability, TypeDef } from './util/types'; class RefSet { list: Set; @@ -86,98 +87,42 @@ class RefSet { const UNSET = '@@UNSET_DEFAULT'; -export interface SchemaSpec { - hasDefault: boolean; - default: TDefault; - abortEarly: boolean; - nullable: boolean; - strip: boolean; - strict: boolean; - recursive: boolean; - noUnknown: boolean; - label: string | undefined; - required: boolean; - meta: any; -} - -export interface SchemaOptions { - default?: (this: T) => any; - type: string; -} - -export function create(options?: SchemaOptions) { - return new MixedSchema(options); -} - -// export type SchemaTypeSpec< -// Default = undefined, -// Nullable extends boolean = false, -// Strip extends boolean = false -// > = { 1: Default; 2: Nullable; 3: Strip }; - -// type ResolveNullable = TSpec extends SchemaTypeSpec ? TType extends null : never - -// export type OutputType = ; -// TType = any, -// TSpec extends SchemaTypeSpec = SchemaTypeSpec -type UNSET = { 1: '@@UNSET_DEFAULT' }; - -type ResolveNullable< - TType, - TSpec extends SchemaSpec -> = TSpec['nullable'] extends true ? TType | null : TType; - -type ResolveDefault = TSpec extends SchemaSpec< - infer Default -> - ? Default extends UNSET - ? TType - : Default extends undefined - ? TType & undefined - : Exclude - : never; - -// type TypeOfShape> = { -// [K in keyof Shape]: ReturnType; +// export type TypeDef = { +// nullable: boolean; +// required: boolean; +// default: T; // }; -export type ResolveCast = ResolveDefault< - ResolveNullable, - TSpec ->; - -export type ResolveRequired< - TType, - TSpec extends SchemaSpec -> = TSpec['required'] extends true ? NonNullable : TType; - -export type TypedSchema = { _tsType: any; _tsValidate: any }; - -// type Keys> = { fields: TShape }; - -// type CastChildren = T extends Keys ? { } +export type SchemaSpec = { + nullable?: boolean; + default: TDefault; + hasDefault?: boolean; + abortEarly?: boolean; + strip?: boolean; + strict?: boolean; + recursive?: boolean; + label?: string | undefined; + meta?: any; +}; -export type TypeOf = T extends { spec: infer TSpec } - ? TSpec extends SchemaSpec - ? ResolveCast - : never - : never; +export type SchemaOptions = { + type?: string; + spec: SchemaSpec; +}; -export type Asserts = T extends { spec: infer TSpec } - ? TSpec extends SchemaSpec - ? ResolveRequired, TSpec> - : never - : never; +export function create() { + return new MixedSchema(); +} -export default class MixedSchema implements Schema { +export default class MixedSchema< + TType = any, + TDef extends TypeDef = 'optional' | 'nonnullable', + TDefault extends Maybe = undefined, +> implements Schema { readonly type: string; - readonly _tsType!: any; - readonly _tsValidate!: any; - - // readonly _tsType2!: TSpec['required'] extends true - // ? NonNullable - // : TType; + readonly __inputType!: ResolveInput; + readonly __outputType!: ResolveOutput; readonly __isYupSchema__ = true; @@ -187,21 +132,6 @@ export default class MixedSchema implements Schema { protected _whitelist: RefSet = new RefSet(); protected _blacklist: RefSet = new RefSet(); - spec: SchemaSpec = { - nullable: false, - default: undefined as any, - hasDefault: false, - strip: false, - strict: false, - abortEarly: true, - required: false, - recursive: true, - noUnknown: false, - - label: undefined, - meta: undefined, - } as const; - tests: Test[]; transforms: TransformFunction[]; // TODO @@ -219,14 +149,16 @@ export default class MixedSchema implements Schema { optional!: () => MixedSchema; - static create( - this: new (...args: any[]) => T, - ...args: any[] - ) { - return new this(...args); - } + spec: SchemaSpec; + + // static create( + // this: new (...args: any[]) => T, + // ...args: any[] + // ) { + // return new this(...args); + // } - constructor(options: SchemaOptions = { type: 'mixed' }) { + constructor(options?: SchemaOptions) { this.tests = []; this.transforms = []; @@ -234,7 +166,24 @@ export default class MixedSchema implements Schema { this.typeError(locale.notType); }); - this.type = options.type; + this.type = options?.type || ('mixed' as const); + + this.spec = { + // __def: null as any, + hasDefault: false, + strip: false, + strict: false, + abortEarly: true, + recursive: true, + // noUnknown: false, + + label: undefined, + meta: undefined, + nullable: false, + // required: false, + default: undefined as any, + ...options?.spec, + }; } // TODO: remove @@ -289,7 +238,7 @@ export default class MixedSchema implements Schema { `You cannot \`concat()\` schema's of different types: ${this.type} and ${schema.type}`, ); - var next = merge(schema.clone() as any, this as any) as any; + var next = merge(schema.clone() as any, this as any) as this; // new undefined default is overridden by old non-undefined one, revert if (schema.spec.hasDefault) { @@ -354,7 +303,7 @@ export default class MixedSchema implements Schema { * @param {*=} options.parent * @param {*=} options.context */ - cast(value: any, options: CastOptions = {}): TypeOf { + cast(value: any, options: CastOptions = {}): ResolveInput { let resolvedSchema = this.resolve({ value, ...options, @@ -467,12 +416,11 @@ export default class MixedSchema implements Schema { ); } - validate(value: any, options?: ValidateOptions): Promise>; validate( value: any, - options: ValidateOptions = {}, - maybeCb?: Callback>, - ) { + options?: ValidateOptions, + ): Promise>; + validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { let schema = this.resolve({ ...options, value }); // callback case is for nested validations @@ -486,7 +434,10 @@ export default class MixedSchema implements Schema { ); } - validateSync(value: any, options: ValidateOptions = {}): Asserts { + validateSync( + value: any, + options: ValidateOptions = {}, + ): ResolveOutput { let schema = this.resolve({ ...options, value }); let result: any; @@ -522,10 +473,10 @@ export default class MixedSchema implements Schema { return schema.default(); } - default(): this['spec']['default']; // FIXME(ts): typed default - default( - def: TDefault | (() => TDefault), - ): WithSpec; + default(): TDefault; // FIXME(ts): typed default + default>( + def: TNextDefault | (() => TNextDefault), + ): MixedSchema default(def?: TDefault | (() => TDefault)) { if (arguments.length === 0) { let defaultValue = this.spec.default; @@ -541,7 +492,7 @@ export default class MixedSchema implements Schema { var next = this.clone(); next.spec.hasDefault = true; - next.spec.default = def; + next.spec.default = def as any; return next; } @@ -555,7 +506,9 @@ export default class MixedSchema implements Schema { return value != null; } - required(message = locale.required): WithSpec { + required( + message = locale.required, + ): MixedSchema | 'required'> { return this.test({ message, name: 'required', @@ -566,15 +519,19 @@ export default class MixedSchema implements Schema { }) as any; } - notRequired() { + notRequired(): MixedSchema | 'optional'> { var next = this.clone(); next.tests = next.tests.filter((test) => test.OPTIONS.name !== 'required'); - return next; + return next as any; } - nullable(isNullable?: true): WithSpec; - nullable(isNullable: false): WithSpec; - nullable(isNullable = true): this { + nullable( + isNullable?: true, + ): MixedSchema, TDefault>; + nullable( + isNullable: false, + ): MixedSchema, TDefault>; + nullable(isNullable = true): MixedSchema { var next = this.clone(); next.spec.nullable = isNullable; return next as any; diff --git a/src/object.ts b/src/object.ts index 497bd5b04..23a6744cf 100644 --- a/src/object.ts +++ b/src/object.ts @@ -5,7 +5,7 @@ import mapKeys from 'lodash/mapKeys'; import mapValues from 'lodash/mapValues'; import { getter } from 'property-expr'; -import MixedSchema, { SchemaSpec, Asserts, TypeOf, TypedSchema } from './mixed'; +import MixedSchema, { SchemaSpec } from './mixed'; import { object as locale, string } from './locale'; import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; @@ -32,13 +32,6 @@ export function create(spec?: TShape) { // export type ObjectSchemaSpec = ; -type InferFromShape = { - [K in keyof TShape]: TShape[K]['_tsType']; -}; - -type DefaultFromShape = { - [K in keyof TShape]: TShape[K]['spec']['default']; -}; // type TypeOfShape< // T extends TypedSchema, // Shape extends ObjectShape, @@ -76,7 +69,7 @@ export default class ObjectSchema< _tsType!: TypeOfShape; _tsValidate!: AssertsShape; - spec!: SchemaSpec> & { + spec!: SchemaSpec & { noUnknown: boolean; }; diff --git a/src/string.ts b/src/string.ts index 00b2f35be..4ba930691 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,8 +1,9 @@ -import MixedSchema, { SchemaSpec } from './mixed'; +import MixedSchema from './mixed'; import { string as locale } from './locale'; import isAbsent from './util/isAbsent'; import Reference from './Reference'; import { Message, Maybe } from './types'; +import { SetNullability, SetPresence, TypeDef } from './util/types'; // eslint-disable-next-line let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; @@ -24,12 +25,17 @@ export function create() { return new StringSchema(); } -export default class StringSchema extends MixedSchema { +// @ts-ignore +export default class StringSchema< + TType extends string = string, + TDef extends TypeDef = 'optional' | 'nonnullable', + TDefault extends Maybe = undefined +> extends MixedSchema { _tsType!: string | undefined; _tsValidate!: string | undefined; constructor() { - super({ type: 'string' }); + super({ type: 'string', spec: {} }); this.withMutation(() => { this.transform(function (value) { @@ -188,6 +194,28 @@ export default class StringSchema extends MixedSchema { // } } +// @ts-ignore +export default interface StringSchema< + TType extends string, + TDef extends TypeDef, + TDefault extends Maybe +> extends MixedSchema { + default(): TDefault; + default>( + def: TNextDefault | (() => TNextDefault), + ): StringSchema; + + required(): StringSchema>; + notRequired(): StringSchema>; + + nullable( + isNullable?: true, + ): StringSchema>; + nullable( + isNullable: false, + ): StringSchema>; +} + // export default interface StringSchema< // TType extends Maybe = string, // TSpec extends SchemaSpec = SchemaSpec diff --git a/src/util/types.ts b/src/util/types.ts new file mode 100644 index 000000000..aeab2ca8a --- /dev/null +++ b/src/util/types.ts @@ -0,0 +1,102 @@ +// type UNSET = { 1: '@@UNSET_DEFAULT' }; + +import Schema from '../Schema'; +import { Maybe } from '../types'; + +export type Presence = 'required' | 'optional'; +export type Nullability = 'nullable' | 'nonnullable'; + +type Pluck = T extends O ? T : never; + +type MaintainOptionality = T extends undefined ? U | undefined : U; + +type StrictNonNullable = T extends null ? never : T; + +export type TypeDef = Nullability | Presence; + +export type SetNullability< + Def extends TypeDef, + TValue extends Nullability +> = TValue extends 'nullable' + ? Exclude | 'nullable' + : Exclude | 'nonnullable'; + +export type SetPresence< + Def extends TypeDef, + TValue extends Presence +> = TValue extends 'required' + ? Exclude | 'required' + : Exclude | 'optional'; + +export type ResolveDefault = undefined> = + | TType + | TDefault; + +export type ResolveInput< + TType, + Def extends TypeDef, + TDefault extends Maybe = undefined +> = Def extends 'nullable' + ? TType | null | TDefault + : StrictNonNullable; + +export type ResolveOutput< + TType, + Def extends TypeDef, + TDefault extends Maybe = undefined +> = Pluck extends never + ? ResolveInput // + : NonNullable>; + +export type TypedSchema = { __inputType: any; __outputType: any }; + +export type TypeOf = TSchema['__inputType']; + +export type Asserts = TSchema['__outputType']; + +// type ResolveNullable< +// TType, +// TSpec extends SchemaSpec +// > = TSpec['nullable'] extends true ? TType | null : TType; + +// type ResolveDefault = TSpec extends SchemaSpec< +// infer Default +// > +// ? Default extends UNSET +// ? TType +// : Default extends undefined +// ? TType & undefined +// : Exclude +// : never; + +// // type TypeOfShape> = { +// // [K in keyof Shape]: ReturnType; +// // }; + +// export type ResolveCast = ResolveDefault< +// ResolveNullable, +// TSpec +// >; + +// export type ResolveRequired< +// TType, +// TSpec extends SchemaSpec +// > = TSpec['required'] extends true ? NonNullable : TType; + +// export type TypedSchema = { _tsType: any; _tsValidate: any }; + +// // type Keys> = { fields: TShape }; + +// // type CastChildren = T extends Keys ? { } + +// export type TypeOf = T extends { spec: infer TSpec } +// ? TSpec extends SchemaSpec +// ? ResolveCast +// : never +// : never; + +// export type Asserts = T extends { spec: infer TSpec } +// ? TSpec extends SchemaSpec +// ? ResolveRequired, TSpec> +// : never +// : never; diff --git a/test/mixed.js b/test/mixed.js index f597713db..5af54bbbe 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -968,7 +968,7 @@ describe('Mixed Types ', () => { it('should pass when value is null', async () => { let inst = object({ - prop: string().defined(), + prop: string().nullable().defined(), }); await inst.isValid({ prop: null }).should.eventually().equal(true); diff --git a/test/types.ts b/test/types.ts index a9924a594..84b906ca9 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,75 +1,133 @@ -import { Asserts } from '../src/mixed'; +// import { Asserts } from '../src/mixed'; import { string, object, mixed, number } from '../src'; +import { + Asserts, + ResolveInput, + ResolveOutput, + TypeOf, +} from '../src/util/types'; + +// let schema = object({ +// str: string().nullable(), +// }).shape({ +// num: number(), +// }); -let schema = object({ - str: string().nullable(), -}).shape({ - num: number(), -}); +// const fff = mixed().nullable(); -const fff = mixed().nullable(); +mixed().required().nullable(); -// type TSchema = typeof schema; -// type tt = TSchema['_tsValidate']; +string().required().nullable(); -// const shape = { -// str: string().nullable(), -// }; +/** Type utils */ +{ + // $ExpectType string | null + type _i1 = ResolveInput; -// async function foo() { -// const validate = await schema.validate({}); + // $ExpectType string | undefined + type _i2 = ResolveInput; -// } + // $ExpectType string + type _i3 = ResolveInput; -// type _objNestedNullableRequired = AssertsShape; + // $ExpectType string + type _i4 = ResolveInput; -let f = schema.cast(undefined); + // $ExpectType string + type _i5 = ResolveInput; -// $ExpectType { str: any, num: any } | undefined -f; + // $ExpectType string | null | undefined + type _i6 = ResolveInput; -// $ExpectType string | null | undefined -f!.str; + // $ExpectType string + type _o1 = ResolveOutput; + + // $ExpectType string | undefined + type _o2 = ResolveOutput; + + // $ExpectType string + type _o3 = ResolveOutput; + + // $ExpectType string + type _o4 = ResolveOutput; + + // $ExpectType string + type _o5 = ResolveOutput; + + // $ExpectType string | null | undefined + type _o6 = ResolveOutput; + + // $ExpectType string | null + type _o7 = ResolveOutput; + + // $ExpectError number is not a MaybeString + type _e1 = ResolveOutput; +} const strNullableRequired = string().nullable().required(); + +// $ExpectType string | null | undefined +type _strNullableRequired1 = TypeOf; + // $ExpectType string -type _strNullableRequired = Asserts; +type _strNullableRequired2 = Asserts; const strNullable = string().nullable(); // $ExpectType string | null | undefined type _strNullable = Asserts; -const strPlain = string(); +const strDefined = string().default(''); -type fff = typeof strPlain['spec']['hasDefault']; +// $ExpectType "" +const _strDefined = strDefined.default(); -// $ExpectType string | undefined -type _strPlain = Asserts; +const strDefault = string().nullable().default(''); -const strRequired = string().required(); -// $ExpectType string -type _strRequired = Asserts; +// $ExpectType string | null +type _strDefault1 = TypeOf; + +// $ExpectType string | null +type _strDefault2 = Asserts; -const strDefault = string().nullable().default(undefined); -const strDefault2 = string().nullable().default(''); +const strDefaultRequired = string().nullable().required().default(''); -// $ExpectType undefined -strDefault.default(); +// $ExpectType string | null +type _strDefaultRequired1 = TypeOf; // $ExpectType string -strDefault2.default(); +type _strDefaultRequired2 = Asserts; -// $ExpectType string | null -strDefault2.cast(undefined); - -// async function foo() { -// ff = await str.validate(undefined, {}); -// ff = await str2.validate(null, {}); -// } - -let objWithDefault = object({ - str: string().nullable().default(''), - num: number().default(3), - num2: number(), -}); +// const strPlain = string(); + +// type fff = typeof strPlain['spec']['hasDefault']; + +// // $ExpectType string | undefined +// type _strPlain = Asserts; + +// const strRequired = string().required(); +// // $ExpectType string +// type _strRequired = Asserts; + +// const strDefault = string().nullable().default(undefined); +// const strDefault2 = string().nullable().default(''); + +// // $ExpectType undefined +// strDefault.default(); + +// // $ExpectType string +// strDefault2.default(); + +// // $ExpectType string | null +// strDefault2.cast(undefined); + +// // async function foo() { +// // ff = await str.validate(undefined, {}); +// // ff = await str2.validate(null, {}); +// // } + +// let objWithDefault = object({ +// str: string().nullable().default(''), +// num: number().default(3), +// num2: number(), +// }); diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 000000000..512bff878 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,823 @@ +// Type definitions for yup 0.29 +// Project: https://github.com/jquense/yup +// Definitions by: Dominik Hardtke , +// Vladyslav Tserman , +// Moreton Bay Regional Council , +// Sindre Seppola +// Yash Kulshrestha +// Vincent Pizzo +// Robert Bullen +// Yusuke Sato +// Desmond Koh +// Maurice de Beijer +// Kalley Powell +// Elías García +// Ian Sanders +// Jay Fong +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 3.6 + +export function reach( + schema: Schema, + path: string, + value?: any, + context?: any, +): Schema; +export function addMethod>( + schemaCtor: AnySchemaConstructor, + name: string, + method: (this: T, ...args: any[]) => T, +): void; +export function ref(path: string, options?: { contextPrefix: string }): Ref; +export function lazy(fn: (value: T) => Schema): Lazy; +export function setLocale(customLocale: LocaleObject): void; +export function isSchema(obj: any): obj is Schema; + +export const mixed: MixedSchemaConstructor; +export const string: StringSchemaConstructor; +export const number: NumberSchemaConstructor; +export const boolean: BooleanSchemaConstructor; +export const bool: BooleanSchemaConstructor; +export const date: DateSchemaConstructor; +export const array: ArraySchemaConstructor; +export const object: ObjectSchemaConstructor; + +export type AnySchemaConstructor = + | MixedSchemaConstructor + | StringSchemaConstructor + | NumberSchemaConstructor + | BooleanSchemaConstructor + | DateSchemaConstructor + | ArraySchemaConstructor + | ObjectSchemaConstructor; + +export type TestOptionsMessage< + Extra extends Record = {}, + R = any +> = string | ((params: Extra & Partial) => R); + +export interface Schema { + type: string; + clone(): this; + label(label: string): this; + meta(metadata: any): this; + meta(): any; + describe(): SchemaDescription; + concat(schema: this): this; + validate(value: any, options?: ValidateOptions): Promise; + validateSync(value: any, options?: ValidateOptions): T; + validateAt(path: string, value: T, options?: ValidateOptions): Promise; + validateSyncAt(path: string, value: T, options?: ValidateOptions): T; + isValid(value: any, options?: any): Promise; + isValidSync(value: any, options?: any): value is T; + cast(value?: any, options?: any): T; + isType(value: any): value is T; + strict(isStrict: boolean): this; + strip(strip: boolean): this; + withMutation(fn: (current: this) => void): void; + default(value: any): this; + default(): T; + typeError(message?: TestOptionsMessage): this; + notOneOf(arrayOfValues: any[], message?: MixedLocale['notOneOf']): this; + when(keys: string | any[], builder: WhenOptions): this; + transform(fn: TransformFunction): this; +} + +// Note: Using `{} | null | undefined` allows excluding `null` & `undefined` +// whereas using `any` as the default type would mean that `nullable(false)`, +// `defined`, and `required` would all have no effect. + +export interface MixedSchemaConstructor { + // tslint:disable-next-line:no-unnecessary-generics + (): MixedSchema; + // tslint:disable-next-line:no-unnecessary-generics + new (options?: { + type?: string; + [key: string]: any; + }): MixedSchema; +} + +export interface MixedSchema + extends Schema { + nullable(isNullable?: true): MixedSchema; + nullable(isNullable: false): MixedSchema>; + nullable(isNullable?: boolean): MixedSchema; + required( + message?: TestOptionsMessage, + ): MixedSchema>; + defined(): MixedSchema>; + notRequired(): MixedSchema; + optional(): MixedSchema; + concat(schema: this): this; + concat(schema: Schema): MixedSchema; + oneOf( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): MixedSchema>; + equals( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): MixedSchema>; + test( + name: string, + message: TestOptionsMessage, + test: AssertingTestFunction, + ): MixedSchema; + test(name: string, message: TestOptionsMessage, test: TestFunction): this; + test( + options: AssertingTestOptions>, + ): MixedSchema; + test(options: TestOptions>): this; +} + +export interface StringSchemaConstructor { + (): StringSchema; + new (): StringSchema; +} + +export interface StringSchema< + T extends string | null | undefined = string | undefined +> extends Schema { + length( + limit: number | Ref, + message?: StringLocale['length'], + ): StringSchema; + min(limit: number | Ref, message?: StringLocale['min']): StringSchema; + max(limit: number | Ref, message?: StringLocale['max']): StringSchema; + matches( + regex: RegExp, + messageOrOptions?: + | StringLocale['matches'] + | { message?: StringLocale['matches']; excludeEmptyString?: boolean }, + ): StringSchema; + email(message?: StringLocale['email']): StringSchema; + url(message?: StringLocale['url']): StringSchema; + uuid(message?: StringLocale['uuid']): StringSchema; + ensure(): StringSchema; + trim(message?: StringLocale['trim']): StringSchema; + lowercase(message?: StringLocale['lowercase']): StringSchema; + uppercase(message?: StringLocale['uppercase']): StringSchema; + nullable(isNullable?: true): StringSchema; + nullable(isNullable: false): StringSchema>; + nullable(isNullable?: boolean): StringSchema; + required( + message?: TestOptionsMessage, + ): StringSchema>; + defined(): StringSchema>; + notRequired(): StringSchema; + oneOf( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): StringSchema>; + equals( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): StringSchema>; + /* + All TestFunction generics are intentionally T with (undefined | null) as previous .required / .defined / .nullable + will narrow out those types, and tests run for (undefined | null) even if they're not allowed. + */ + test( + name: string, + message: TestOptionsMessage, + test: TestFunction, + ): this; + test( + name: string, + message: TestOptionsMessage, + test: AssertingTestFunction, + ): StringSchema; + test( + options: AssertingTestOptions>, + ): StringSchema; + test(options: TestOptions>): this; + optional(): StringSchema; +} + +export interface NumberSchemaConstructor { + (): NumberSchema; + new (): NumberSchema; +} + +export interface NumberSchema< + T extends number | null | undefined = number | undefined +> extends Schema { + min(limit: number | Ref, message?: NumberLocale['min']): NumberSchema; + max(limit: number | Ref, message?: NumberLocale['max']): NumberSchema; + lessThan( + limit: number | Ref, + message?: NumberLocale['lessThan'], + ): NumberSchema; + moreThan( + limit: number | Ref, + message?: NumberLocale['moreThan'], + ): NumberSchema; + positive(message?: NumberLocale['positive']): NumberSchema; + negative(message?: NumberLocale['negative']): NumberSchema; + integer(message?: NumberLocale['integer']): NumberSchema; + truncate(): NumberSchema; + round(type: 'floor' | 'ceil' | 'trunc' | 'round'): NumberSchema; + nullable(isNullable?: true): NumberSchema; + nullable(isNullable: false): NumberSchema>; + nullable(isNullable?: boolean): NumberSchema; + required( + message?: TestOptionsMessage, + ): NumberSchema>; + defined(): NumberSchema>; + notRequired(): NumberSchema; + oneOf( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): NumberSchema>; + equals( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): NumberSchema>; + test( + name: string, + message: TestOptionsMessage, + test: TestFunction, + ): this; + test( + name: string, + message: TestOptionsMessage, + test: AssertingTestFunction, + ): NumberSchema; + test( + options: AssertingTestOptions>, + ): NumberSchema; + test(options: TestOptions>): this; + optional(): NumberSchema; +} + +export interface BooleanSchemaConstructor { + (): BooleanSchema; + new (): BooleanSchema; +} + +export interface BooleanSchema< + T extends boolean | null | undefined = boolean | undefined +> extends Schema { + nullable(isNullable?: true): BooleanSchema; + nullable(isNullable: false): BooleanSchema>; + nullable(isNullable?: boolean): BooleanSchema; + required( + message?: TestOptionsMessage, + ): BooleanSchema>; + defined(): BooleanSchema>; + notRequired(): BooleanSchema; + oneOf( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): BooleanSchema>; + equals( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): BooleanSchema>; + test( + name: string, + message: TestOptionsMessage, + test: TestFunction, + ): this; + test( + name: string, + message: TestOptionsMessage, + test: AssertingTestFunction, + ): BooleanSchema; + test( + options: AssertingTestOptions>, + ): BooleanSchema; + test(options: TestOptions>): this; + optional(): BooleanSchema; +} + +export interface DateSchemaConstructor { + (): DateSchema; + new (): DateSchema; +} + +export interface DateSchema< + T extends Date | null | undefined = Date | undefined +> extends Schema { + min(limit: Date | string | Ref, message?: DateLocale['min']): DateSchema; + max(limit: Date | string | Ref, message?: DateLocale['max']): DateSchema; + nullable(isNullable?: true): DateSchema; + nullable(isNullable: false): DateSchema>; + nullable(isNullable?: boolean): DateSchema; + required( + message?: TestOptionsMessage, + ): DateSchema>; + defined(): DateSchema>; + notRequired(): DateSchema; + oneOf( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): DateSchema>; + equals( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): DateSchema>; + test( + name: string, + message: TestOptionsMessage, + test: TestFunction, + ): this; + test( + name: string, + message: TestOptionsMessage, + test: AssertingTestFunction, + ): DateSchema; + test( + options: AssertingTestOptions>, + ): DateSchema; + test(options: TestOptions>): this; + optional(): DateSchema; +} + +export interface ArraySchemaConstructor { + (schema?: Schema): NotRequiredArraySchema; + new (): NotRequiredArraySchema<{}>; +} + +export interface BasicArraySchema + extends Schema { + min(limit: number | Ref, message?: ArrayLocale['min']): this; + max(limit: number | Ref, message?: ArrayLocale['max']): this; + ensure(): this; + compact( + rejector?: ( + value: InferredArrayType, + index: number, + array: Array>, + ) => boolean, + ): this; + + // This doesn't narrow the type of the schema like the more primitive oneOf calls + // it would be very complex to do that accurately with the subtypes, and it's not + // really worth it because the oneOf check is a shallow (==) comparison so it rarely + // applies to arrays anyway. + oneOf( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): this; + equals( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): this; + test( + name: string, + message: TestOptionsMessage, + test: TestFunction, + ): this; + test(options: TestOptions>): this; + innerType: Schema; +} + +export interface NotRequiredNullableArraySchema + extends BasicArraySchema { + of(type: Schema): NotRequiredNullableArraySchema; + nullable(isNullable?: true): NotRequiredNullableArraySchema; + nullable(isNullable: false): NotRequiredArraySchema; + nullable(isNullable?: boolean): ArraySchema; + required(message?: TestOptionsMessage): ArraySchema; + defined(): NullableArraySchema; + notRequired(): NotRequiredNullableArraySchema; + optional(): NotRequiredNullableArraySchema; +} + +export interface NullableArraySchema + extends BasicArraySchema { + of(type: Schema): NullableArraySchema; + nullable(isNullable?: true): NullableArraySchema; + nullable(isNullable: false): ArraySchema; + nullable(isNullable?: boolean): ArraySchema; + required(message?: TestOptionsMessage): ArraySchema; + defined(): NullableArraySchema; + notRequired(): NotRequiredNullableArraySchema; + optional(): NotRequiredNullableArraySchema; +} + +export interface NotRequiredArraySchema + extends BasicArraySchema { + of(type: Schema): NotRequiredArraySchema; + nullable(isNullable?: true): NotRequiredNullableArraySchema; + nullable(isNullable: false): NotRequiredArraySchema; + nullable(isNullable: boolean): ArraySchema; + required(message?: TestOptionsMessage): ArraySchema; + defined(): ArraySchema; + notRequired(): NotRequiredArraySchema; + optional(): NotRequiredArraySchema; +} + +export interface ArraySchema extends BasicArraySchema { + of(type: Schema): ArraySchema; + nullable(isNullable?: true): NullableArraySchema; + nullable(isNullable: false | boolean): ArraySchema; + required(message?: TestOptionsMessage): ArraySchema; + defined(): ArraySchema; + notRequired(): NotRequiredArraySchema; + optional(): NotRequiredArraySchema; +} + +export type ObjectSchemaDefinition = { + // This shouldn't be necessary because MixedSchema extends Schema, but type + // inference only works with it this way - otherwise when you use a mixed + // field in object schema, it will type as `unknown`. Not sure why that is - + // maybe some sort of inference depth limit? + [field in keyof T]: Schema | MixedSchema | Ref; +}; + +/** + * Merges two interfaces. For properties in common, property types from `U` trump those of `T`. + * This is conducive to the functionality of + * [yup's `object.shape()` method](https://www.npmjs.com/package/yup#objectshapefields-object-nosortedges-arraystring-string-schema). + */ +export type Shape = + | ({ [P in keyof T]: P extends keyof U ? U[P] : T[P] } & U) + | PreserveOptionals; + +export interface ObjectSchemaConstructor { + (fields?: ObjectSchemaDefinition): ObjectSchema< + T | undefined + >; + new (): ObjectSchema<{}>; +} + +export interface ObjectSchema< + T extends object | null | undefined = object | undefined +> extends Schema { + fields: { + [k in keyof T]: Schema; + }; + shape( + fields: ObjectSchemaDefinition, + noSortEdges?: Array<[string, string]>, + ): ObjectSchema>; + from(fromKey: string, toKey: string, alias?: boolean): ObjectSchema; + noUnknown( + onlyKnownKeys?: boolean, + message?: ObjectLocale['noUnknown'], + ): ObjectSchema; + unknown( + allow?: boolean, + message?: ObjectLocale['noUnknown'], + ): ObjectSchema; + transformKeys(callback: (key: any) => any): void; + camelCase(): ObjectSchema; + snakeCase(): ObjectSchema; + constantCase(): ObjectSchema; + nullable(isNullable?: true): ObjectSchema; + nullable(isNullable: false): ObjectSchema>; + nullable(isNullable?: boolean): ObjectSchema; + required( + message?: TestOptionsMessage, + ): ObjectSchema>; + defined(): ObjectSchema>; + notRequired(): ObjectSchema; + optional(): ObjectSchema; + concat(schema: this): this; + concat(schema: ObjectSchema): ObjectSchema; + oneOf( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): ObjectSchema; + equals( + arrayOfValues: ReadonlyArray, + message?: MixedLocale['oneOf'], + ): ObjectSchema; + test( + name: string, + message: TestOptionsMessage, + test: TestFunction, + ): this; + test( + name: string, + message: TestOptionsMessage, + test: AssertingTestFunction, + ): ObjectSchema; + test( + options: AssertingTestOptions>, + ): ObjectSchema; + test(options: TestOptions>): this; +} + +export type TestFunction = ( + this: TestContext, + value: T, +) => boolean | ValidationError | Promise; +export type AssertingTestFunction = ( + this: TestContext, + value: any, +) => value is T; + +export type TransformFunction = ( + this: T, + value: any, + originalValue: any, +) => any; + +export interface WhenOptionsBuilderFunction { + (value: any, schema: T): T; + (v1: any, v2: any, schema: T): T; + (v1: any, v2: any, v3: any, schema: T): T; + (v1: any, v2: any, v3: any, v4: any, schema: T): T; +} + +export type WhenOptionsBuilderObjectIs = + | ((...values: any[]) => boolean) + | boolean + | number + | null + | object + | string; + +export type WhenOptionsBuilderObject = + | { + is: WhenOptionsBuilderObjectIs; + then: any; + otherwise: any; + } + | object; + +export type WhenOptions = + | WhenOptionsBuilderFunction + | WhenOptionsBuilderObject; + +export interface TestContext { + path: string; + options: ValidateOptions; + parent: any; + schema: Schema; + resolve: (value: any) => any; + createError: (params?: { + path?: string; + message?: string; + params?: object; + }) => ValidationError; +} + +export interface ValidateOptions { + /** + * Only validate the input, and skip and coercion or transformation. Default - false + */ + strict?: boolean; + /** + * Return from validation methods on the first error rather than after all validations run. Default - true + */ + abortEarly?: boolean; + /** + * Remove unspecified keys from objects. Default - false + */ + stripUnknown?: boolean; + /** + * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true + */ + recursive?: boolean; + /** + * Any context needed for validating schema conditions (see: when()) + */ + context?: object; +} + +export interface TestMessageParams { + path: string; + value: any; + originalValue: any; + label: string; +} + +interface BaseTestOptions

> { + /** + * Unique name identifying the test. Required for exclusive tests. + */ + name?: string; + + /** + * Test function, determines schema validity + */ + test: TestFunction; + + /** + * The validation error message + */ + message?: TestOptionsMessage

; + + /** + * Values passed to message for interpolation + */ + params?: P; + + /** + * Mark the test as exclusive, meaning only one of the same can be active at once + */ + exclusive?: boolean; +} + +interface NonExclusiveTestOptions

> + extends BaseTestOptions

{ + exclusive?: false; +} + +interface ExclusiveTestOptions

> + extends BaseTestOptions

{ + exclusive: true; + name: string; +} + +interface NonExclusiveAssertingTestOptions> + extends NonExclusiveTestOptions

{ + test: AssertingTestFunction; +} + +interface ExclusiveAssertingTestOptions> + extends ExclusiveTestOptions

{ + test: AssertingTestFunction; +} + +export type TestOptions

= {}> = + | NonExclusiveTestOptions

+ | ExclusiveTestOptions

; + +export type AssertingTestOptions = {}> = + | NonExclusiveAssertingTestOptions + | ExclusiveAssertingTestOptions; + +export interface SchemaFieldRefDescription { + type: 'ref'; + key: string; +} + +export interface SchemaFieldInnerTypeDescription + extends Pick> { + innerType?: SchemaFieldDescription; +} + +export type SchemaFieldDescription = + | SchemaDescription + | SchemaFieldRefDescription + | SchemaFieldInnerTypeDescription; + +export interface SchemaDescription { + type: string; + label: string; + meta: object; + tests: Array<{ name: string; params: { [k: string]: any } }>; + fields: Record; +} + +// ValidationError works a lot more like a class vs. a constructor +// function that returns an interface. It's also got a couple of +// static methods and it inherits from the generic Error class in +// the [yup codebase][1]. +// [1]: (https://github.com/jquense/yup/blob/master/src/ValidationError.js) +export class ValidationError extends Error { + name: string; + message: string; + value: any; + /** + * A string, indicating where there error was thrown. path is empty at the root level. + */ + path: string; + type: any; + /** + * array of error messages + */ + errors: string[]; + + /** + * In the case of aggregate errors, inner is an array of ValidationErrors throw earlier in the validation chain. + */ + inner: ValidationError[]; + params?: object; + + static isError(err: any): err is ValidationError; + static formatError( + message: string | ((params?: any) => string), + params?: any, + ): string | ((params?: any) => string); + + constructor(errors: string | string[], value: any, path: string, type?: any); +} + +// It is tempting to declare `Ref` very simply, but there are problems with these approaches: +// +// * `type Ref = Record;` - This is essentially how it was originally declared, but +// just about any object satisfies this contract, which makes the type declaration too loose to +// be useful. +// +// * `type Ref = object;` - This is a variation on the previous bullet in that it is too loose. +// +// * `class Ref {}` - This is yet another variation that is too loose. +// +// * `type Ref = void;` - This works and the emitted JavaScript is just fine, but it results in some +// confusing IntelliSense, e.g it looks like the `ref()` returns `void`, which is not the case. +// +// The solution is twofold. 1.) Declare it as a class with a private constructor to prevent it from +// being instantiated by anything but the `ref()` factory function, and; 2.) declare a private +// readonly property (that yup actually places on the prototype) to force it to be structurally +// incompatible with any other object type. + +/** + * `Ref` is an opaque type that is internal to yup. Creating a `Ref` instance is accomplished via the `ref()` factory + * function. + */ +export class Ref { + private constructor(); + private readonly __isYupRef: true; +} + +// tslint:disable-next-line:no-empty-interface +export interface Lazy extends Schema {} + +export interface FormatErrorParams { + path: string; + type: string; + value?: any; + originalValue?: any; +} + +export type LocaleValue = string | ((params: FormatErrorParams) => string); + +export interface MixedLocale { + default?: TestOptionsMessage; + required?: TestOptionsMessage; + oneOf?: TestOptionsMessage<{ values: any }>; + notOneOf?: TestOptionsMessage<{ values: any }>; + notType?: LocaleValue; +} + +export interface StringLocale { + length?: TestOptionsMessage<{ length: number }>; + min?: TestOptionsMessage<{ min: number }>; + max?: TestOptionsMessage<{ max: number }>; + matches?: TestOptionsMessage<{ regex: RegExp }>; + email?: TestOptionsMessage<{ regex: RegExp }>; + url?: TestOptionsMessage<{ regex: RegExp }>; + uuid?: TestOptionsMessage<{ regex: RegExp }>; + trim?: TestOptionsMessage; + lowercase?: TestOptionsMessage; + uppercase?: TestOptionsMessage; +} + +export interface NumberLocale { + min?: TestOptionsMessage<{ min: number }>; + max?: TestOptionsMessage<{ max: number }>; + lessThan?: TestOptionsMessage<{ less: number }>; + moreThan?: TestOptionsMessage<{ more: number }>; + positive?: TestOptionsMessage<{ more: number }>; + negative?: TestOptionsMessage<{ less: number }>; + integer?: TestOptionsMessage; +} + +export interface DateLocale { + min?: TestOptionsMessage<{ min: Date | string }>; + max?: TestOptionsMessage<{ max: Date | string }>; +} + +export interface ObjectLocale { + noUnknown?: TestOptionsMessage; +} + +export interface ArrayLocale { + min?: TestOptionsMessage<{ min: number }>; + max?: TestOptionsMessage<{ max: number }>; +} + +export interface LocaleObject { + mixed?: MixedLocale; + string?: StringLocale; + number?: NumberLocale; + date?: DateLocale; + boolean?: {}; + object?: ObjectLocale; + array?: ArrayLocale; +} + +export type InferType = T extends Schema + ? InnerInferType

+ : never; + +// Shut off automatic exporting after this statement +export {}; + +type KeyOfUndefined = { + [P in keyof T]-?: undefined extends T[P] ? P : never; +}[keyof T]; + +type PreserveNull = T extends null ? null : never; +type PreserveUndefined = T extends undefined ? undefined : never; +type PreserveOptionals = PreserveNull | PreserveUndefined; +type Id = { + [K in keyof T]: T[K] extends object ? InnerInferType : T[K]; +}; +type RequiredProps = Pick>>; +type NotRequiredProps = Partial>>; +type InnerInferType = + | (T extends Array + ? InnerInferTypeArray + : Id & RequiredProps>) + | PreserveOptionals; +interface InnerInferTypeArray extends Array> {} +type InferredArrayType = T extends Array ? U : T; +/** If `T` is optional, returns optional `U`. */ +type MaintainOptionality = T extends undefined ? U | undefined : U; From aa0e65e19192cdd88cfde1a8acb57963018600c8 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Fri, 4 Sep 2020 14:07:33 -0400 Subject: [PATCH 06/31] WIP --- src/mixed.ts | 36 ++++++------- src/number.ts | 29 ++++++++++- src/object.ts | 129 ++++++++++++++++++++++++++++++---------------- src/string.ts | 53 ++----------------- src/util/types.ts | 17 ++++-- test/types.ts | 52 +++++++++++-------- 6 files changed, 180 insertions(+), 136 deletions(-) diff --git a/src/mixed.ts b/src/mixed.ts index d1db776f4..f0c5b1779 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -21,7 +21,8 @@ import { TransformFunction, Message, Callback, - InternalOptions,, Maybe + InternalOptions, + Maybe, } from './types'; import Schema, { CastOptions, @@ -29,7 +30,12 @@ import Schema, { SchemaDescription, } from './Schema'; import { ValidationError } from '.'; -import { ResolveInput, ResolveOutput, SetNullability, TypeDef } from './util/types'; +import { + ResolveInput, + ResolveOutput, + SetNullability, + TypeDef, +} from './util/types'; class RefSet { list: Set; @@ -85,17 +91,9 @@ class RefSet { } } -const UNSET = '@@UNSET_DEFAULT'; - -// export type TypeDef = { -// nullable: boolean; -// required: boolean; -// default: T; -// }; - -export type SchemaSpec = { +export type SchemaSpec = { nullable?: boolean; - default: TDefault; + default: TDefault | (() => TDefault); hasDefault?: boolean; abortEarly?: boolean; strip?: boolean; @@ -105,9 +103,9 @@ export type SchemaSpec = { meta?: any; }; -export type SchemaOptions = { +export type SchemaOptions = { type?: string; - spec: SchemaSpec; + spec?: SchemaSpec; }; export function create() { @@ -117,7 +115,7 @@ export function create() { export default class MixedSchema< TType = any, TDef extends TypeDef = 'optional' | 'nonnullable', - TDefault extends Maybe = undefined, + TDefault = any > implements Schema { readonly type: string; @@ -149,7 +147,7 @@ export default class MixedSchema< optional!: () => MixedSchema; - spec: SchemaSpec; + spec: SchemaSpec; // static create( // this: new (...args: any[]) => T, @@ -158,7 +156,7 @@ export default class MixedSchema< // return new this(...args); // } - constructor(options?: SchemaOptions) { + constructor(options?: SchemaOptions) { this.tests = []; this.transforms = []; @@ -344,7 +342,7 @@ export default class MixedSchema< rawValue, ); - if (value === undefined && this.spec.default !== UNSET) { + if (value === undefined) { value = this.default(); } @@ -476,7 +474,7 @@ export default class MixedSchema< default(): TDefault; // FIXME(ts): typed default default>( def: TNextDefault | (() => TNextDefault), - ): MixedSchema + ): MixedSchema; default(def?: TDefault | (() => TDefault)) { if (arguments.length === 0) { let defaultValue = this.spec.default; diff --git a/src/number.ts b/src/number.ts index 8d5d0b5a4..fd62d6392 100644 --- a/src/number.ts +++ b/src/number.ts @@ -3,6 +3,7 @@ import { number as locale } from './locale'; import isAbsent from './util/isAbsent'; import { Maybe } from './types'; import Reference from './Reference'; +import { SetNullability, SetPresence, TypeDef } from './util/types'; let isNaN = (value: Maybe) => value != +value!; @@ -10,7 +11,11 @@ export function create() { return new NumberSchema(); } -export default class NumberSchema extends MixedSchema { +export default class NumberSchema< + TType extends number, + TDef extends TypeDef = 'optional' | 'nonnullable', + TDefault extends Maybe = undefined +> extends MixedSchema { constructor() { super({ type: 'number' }); @@ -123,3 +128,25 @@ export default class NumberSchema extends MixedSchema { ); } } + +// @ts-ignore +export default interface NumberSchema< + TType extends number, + TDef extends TypeDef, + TDefault extends Maybe +> extends MixedSchema { + default(): TDefault; + default>( + def: TNextDefault | (() => TNextDefault), + ): NumberSchema; + + required(): NumberSchema, TDefault>; + notRequired(): NumberSchema, TDefault>; + + nullable( + isNullable?: true, + ): NumberSchema, TDefault>; + nullable( + isNullable: false, + ): NumberSchema, TDefault>; +} diff --git a/src/object.ts b/src/object.ts index 23a6744cf..f520fe1f6 100644 --- a/src/object.ts +++ b/src/object.ts @@ -11,8 +11,16 @@ import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; import Schema, { CastOptions, SchemaObjectDescription } from './Schema'; -import { InternalOptions, Callback } from './types'; +import { InternalOptions, Callback, Maybe } from './types'; import { ValidationError } from '.'; +import { + ResolveInput, + ResolveOutput, + TypeDef, + SetNullability, + SetPresence, + TypedSchema, +} from './util/types'; let isObject = (obj: any): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; @@ -22,35 +30,36 @@ function unknown(ctx: ObjectSchema, value: any) { return Object.keys(value).filter((key) => known.indexOf(key) === -1); } -type ObjectShape = Record; +type ObjectShape = Record; // type Obj = Record; export function create(spec?: TShape) { - return new ObjectSchema(spec); + return new ObjectSchema(spec); } -// export type ObjectSchemaSpec = ; - -// type TypeOfShape< -// T extends TypedSchema, -// Shape extends ObjectShape, -// TT = Asserts -// > = TT extends {} -// ? TT & -// { -// [K in keyof Asserts]: Shape extends Record -// ? ReturnType -// : Asserts[K]; -// } -// : TT; +export type TypeFromShape = { + [K in keyof Shape]: Shape[K] extends MixedSchema ? TType : never; +}; + +export type DefaultFromShape = { + [K in keyof Shape]: Shape[K] extends MixedSchema< + infer _, + infer __, + infer TDefault + > + ? TDefault extends undefined + ? never + : TDefault + : never; +}; export type TypeOfShape = { - [K in keyof Shape]: ReturnType; + [K in keyof Shape]: Shape[K]['__inputType']; }; export type AssertsShape = { - [K in keyof Shape]: ReturnType; + [K in keyof Shape]: Shape[K]['__outputType']; }; // export default interface ObjectSchema @@ -61,27 +70,46 @@ export type AssertsShape = { // } export default class ObjectSchema< - TShape extends ObjectShape = ObjectShape -> extends MixedSchema { + TShape extends ObjectShape = ObjectShape, + TDef extends TypeDef = 'optional' | 'nonnullable', + TDefault extends Maybe> = undefined +> extends MixedSchema, TDef, TDefault> { fields: TShape; - _shape!: TShape; - _tsType!: TypeOfShape; - _tsValidate!: AssertsShape; + __inputType!: ResolveInput, TDef, TDefault>; + __outputType!: ResolveOutput, TDef, TDefault>; - spec!: SchemaSpec & { - noUnknown: boolean; - }; + // _shape!: TShape; + // _tsType!: TypeOfShape; + // _tsValidate!: AssertsShape; - private _sortErrors: ( - a: import('/Users/jquense/src/yup/src/ValidationError').default, - b: import('/Users/jquense/src/yup/src/ValidationError').default, - ) => number; + // spec!: SchemaSpec & { + // noUnknown: boolean; + // }; + + private _sortErrors: (a: ValidationError, b: ValidationError) => number; private _nodes: string[]; private _excludedEdges: string[]; constructor(spec?: TShape) { - super({ type: 'object' }); + super({ + type: 'object', + spec: { + noUnknown: false, + default(this: ObjectSchema) { + if (!this._nodes.length) return undefined; + + let dft = {} as Record; + this._nodes.forEach((key) => { + dft[key] = + 'default' in this.fields[key] + ? this.fields[key].default() + : undefined; + }); + return dft as any; + }, + }, + }); this.fields = Object.create(null); @@ -91,18 +119,7 @@ export default class ObjectSchema< this._excludedEdges = []; this.withMutation(() => { - this.spec.default = () => { - if (!this._nodes.length) return undefined; - - let dft = {} as Record; - this._nodes.forEach((key) => { - dft[key] = - 'default' in this.fields[key] - ? this.fields[key].default() - : undefined; - }); - return dft as any; - }; + // this.spec.default = () => {}; this.transform(function coerce(value) { if (typeof value === 'string') { @@ -379,3 +396,27 @@ export default class ObjectSchema< return base; } } + +type AnyObject = Record; + +// @ts-ignore +export default interface ObjectSchema< + TShape extends ObjectShape, + TDef extends TypeDef, + TDefault extends Maybe> = undefined +> extends MixedSchema, TDef, TDefault> { + default(): TDefault; + default>( + def: TNextDefault | (() => TNextDefault), + ): ObjectSchema; + + required(): ObjectSchema>; + notRequired(): ObjectSchema>; + + nullable( + isNullable?: true, + ): ObjectSchema>; + nullable( + isNullable: false, + ): ObjectSchema>; +} diff --git a/src/string.ts b/src/string.ts index 4ba930691..e2b07dc8b 100644 --- a/src/string.ts +++ b/src/string.ts @@ -25,7 +25,6 @@ export function create() { return new StringSchema(); } -// @ts-ignore export default class StringSchema< TType extends string = string, TDef extends TypeDef = 'optional' | 'nonnullable', @@ -35,7 +34,7 @@ export default class StringSchema< _tsValidate!: string | undefined; constructor() { - super({ type: 'string', spec: {} }); + super({ type: 'string' }); this.withMutation(() => { this.transform(function (value) { @@ -205,55 +204,13 @@ export default interface StringSchema< def: TNextDefault | (() => TNextDefault), ): StringSchema; - required(): StringSchema>; - notRequired(): StringSchema>; + required(): StringSchema, TDefault>; + notRequired(): StringSchema, TDefault>; nullable( isNullable?: true, - ): StringSchema>; + ): StringSchema, TDefault>; nullable( isNullable: false, - ): StringSchema>; + ): StringSchema, TDefault>; } - -// export default interface StringSchema< -// TType extends Maybe = string, -// TSpec extends SchemaSpec = SchemaSpec -// > { -// nullable(isNullable?: true): StringSchema; -// nullable(isNullable: false): StringSchema>; -// required(msg?: any): StringSchema; - -// // required( -// // message?: TestOptionsMessage, -// // ): StringSchema>; -// // defined(): StringSchema>; -// // notRequired(): StringSchema; -// // oneOf( -// // arrayOfValues: ReadonlyArray, -// // message?: MixedLocale['oneOf'], -// // ): StringSchema>; -// // equals( -// // arrayOfValues: ReadonlyArray, -// // message?: MixedLocale['oneOf'], -// // ): StringSchema>; -// // /* -// // All TestFunction generics are intentionally T with (undefined | null) as previous .required / .defined / .nullable -// // will narrow out those types, and tests run for (undefined | null) even if they're not allowed. -// // */ -// // test( -// // name: string, -// // message: TestOptionsMessage, -// // test: TestFunction, -// // ): this; -// // test( -// // name: string, -// // message: TestOptionsMessage, -// // test: AssertingTestFunction, -// // ): StringSchema; -// // test( -// // options: AssertingTestOptions>, -// // ): StringSchema; -// // test(options: TestOptions>): this; -// // optional(): StringSchema; -// } diff --git a/src/util/types.ts b/src/util/types.ts index aeab2ca8a..708a91b1b 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -35,7 +35,7 @@ export type ResolveDefault = undefined> = export type ResolveInput< TType, Def extends TypeDef, - TDefault extends Maybe = undefined + TDefault = undefined > = Def extends 'nullable' ? TType | null | TDefault : StrictNonNullable; @@ -43,12 +43,23 @@ export type ResolveInput< export type ResolveOutput< TType, Def extends TypeDef, - TDefault extends Maybe = undefined + TDefault = undefined > = Pluck extends never ? ResolveInput // : NonNullable>; -export type TypedSchema = { __inputType: any; __outputType: any }; +export type TypedSchema< + _TType = any, + _TDef extends TypeDef = any, + _TDefault = any +> = { + __inputType: any; + __outputType: any; + cast(...args: any[]): any; + validateSync(...args: any[]): any; +}; + +// declare class Schema {} export type TypeOf = TSchema['__inputType']; diff --git a/test/types.ts b/test/types.ts index 84b906ca9..a53f0454f 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,5 +1,6 @@ // import { Asserts } from '../src/mixed'; import { string, object, mixed, number } from '../src'; +import { TypeOfShape } from '../src/object'; import { Asserts, ResolveInput, @@ -63,41 +64,50 @@ string().required().nullable(); // $ExpectError number is not a MaybeString type _e1 = ResolveOutput; } +{ + const strNullableRequired = string().nullable().required(); -const strNullableRequired = string().nullable().required(); + // $ExpectType string | null | undefined + type _strNullableRequired1 = TypeOf; -// $ExpectType string | null | undefined -type _strNullableRequired1 = TypeOf; + // $ExpectType string + type _strNullableRequired2 = Asserts; -// $ExpectType string -type _strNullableRequired2 = Asserts; + const strNullable = string().nullable(); -const strNullable = string().nullable(); + // $ExpectType string | null | undefined + type _strNullable = Asserts; -// $ExpectType string | null | undefined -type _strNullable = Asserts; + const strDefined = string().default(''); -const strDefined = string().default(''); + // $ExpectType "" + const _strDefined = strDefined.default(); -// $ExpectType "" -const _strDefined = strDefined.default(); + const strDefault = string().nullable().default(''); -const strDefault = string().nullable().default(''); + // $ExpectType string | null + type _strDefault1 = TypeOf; -// $ExpectType string | null -type _strDefault1 = TypeOf; + // $ExpectType string | null + type _strDefault2 = Asserts; -// $ExpectType string | null -type _strDefault2 = Asserts; + const strDefaultRequired = string().nullable().required().default(''); -const strDefaultRequired = string().nullable().required().default(''); + // $ExpectType string | null + type _strDefaultRequired1 = TypeOf; -// $ExpectType string | null -type _strDefaultRequired1 = TypeOf; + // $ExpectType string + type _strDefaultRequired2 = Asserts; +} -// $ExpectType string -type _strDefaultRequired2 = Asserts; +{ + const obj = object({ + string: string().required(), + number: number().default(1), + }); + type ia = TypeOf; +} // const strPlain = string(); // type fff = typeof strPlain['spec']['hasDefault']; From 00d09aede3576aac33f0de1a15dcd6da8ffce7d8 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Fri, 4 Sep 2020 15:24:02 -0400 Subject: [PATCH 07/31] WIP --- src/mixed.ts | 6 ++-- src/object.ts | 31 ++++++++++---------- src/util/types.ts | 4 +-- test/types.ts | 73 ++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 83 insertions(+), 31 deletions(-) diff --git a/src/mixed.ts b/src/mixed.ts index f0c5b1779..1a1f0fa76 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -301,7 +301,7 @@ export default class MixedSchema< * @param {*=} options.parent * @param {*=} options.context */ - cast(value: any, options: CastOptions = {}): ResolveInput { + cast(value: any, options: CastOptions = {}): this['__inputType'] { let resolvedSchema = this.resolve({ value, ...options, @@ -417,7 +417,7 @@ export default class MixedSchema< validate( value: any, options?: ValidateOptions, - ): Promise>; + ): Promise; validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { let schema = this.resolve({ ...options, value }); @@ -435,7 +435,7 @@ export default class MixedSchema< validateSync( value: any, options: ValidateOptions = {}, - ): ResolveOutput { + ): this['__outputType'] { let schema = this.resolve({ ...options, value }); let result: any; diff --git a/src/object.ts b/src/object.ts index f520fe1f6..f672757b2 100644 --- a/src/object.ts +++ b/src/object.ts @@ -39,18 +39,17 @@ export function create(spec?: TShape) { } export type TypeFromShape = { - [K in keyof Shape]: Shape[K] extends MixedSchema ? TType : never; + [K in keyof Shape]: Shape[K] extends MixedSchema + ? TType + : // not sure why this is necessary + Shape[K] extends ObjectSchema + ? TypeFromShape + : never; }; export type DefaultFromShape = { - [K in keyof Shape]: Shape[K] extends MixedSchema< - infer _, - infer __, - infer TDefault - > - ? TDefault extends undefined - ? never - : TDefault + [K in keyof Shape]: Shape[K] extends MixedSchema + ? TDefault : never; }; @@ -72,7 +71,7 @@ export type AssertsShape = { export default class ObjectSchema< TShape extends ObjectShape = ObjectShape, TDef extends TypeDef = 'optional' | 'nonnullable', - TDefault extends Maybe> = undefined + TDefault extends Maybe> = DefaultFromShape > extends MixedSchema, TDef, TDefault> { fields: TShape; @@ -403,20 +402,20 @@ type AnyObject = Record; export default interface ObjectSchema< TShape extends ObjectShape, TDef extends TypeDef, - TDefault extends Maybe> = undefined + TDefault extends Maybe> > extends MixedSchema, TDef, TDefault> { default(): TDefault; - default>( + default>>( def: TNextDefault | (() => TNextDefault), ): ObjectSchema; - required(): ObjectSchema>; - notRequired(): ObjectSchema>; + required(): ObjectSchema, TDefault>; + notRequired(): ObjectSchema, TDefault>; nullable( isNullable?: true, - ): ObjectSchema>; + ): ObjectSchema, TDefault>; nullable( isNullable: false, - ): ObjectSchema>; + ): ObjectSchema, TDefault>; } diff --git a/src/util/types.ts b/src/util/types.ts index 708a91b1b..ef084aba5 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -6,7 +6,7 @@ import { Maybe } from '../types'; export type Presence = 'required' | 'optional'; export type Nullability = 'nullable' | 'nonnullable'; -type Pluck = T extends O ? T : never; +type Pluck = T extends O ? T : never; type MaintainOptionality = T extends undefined ? U | undefined : U; @@ -37,7 +37,7 @@ export type ResolveInput< Def extends TypeDef, TDefault = undefined > = Def extends 'nullable' - ? TType | null | TDefault + ? TType | TDefault | null : StrictNonNullable; export type ResolveOutput< diff --git a/test/types.ts b/test/types.ts index a53f0454f..79db83496 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,6 +1,7 @@ +/* eslint-disable no-unused-expressions */ // import { Asserts } from '../src/mixed'; import { string, object, mixed, number } from '../src'; -import { TypeOfShape } from '../src/object'; +import { AssertsShape, DefaultFromShape, TypeOfShape } from '../src/object'; import { Asserts, ResolveInput, @@ -65,18 +66,25 @@ string().required().nullable(); type _e1 = ResolveOutput; } { - const strNullableRequired = string().nullable().required(); + const strRequired = string().required(); + + // $ExpectType string | undefined + strRequired.cast(undefined); + // + const strNullableRequired = string().nullable().required(); // $ExpectType string | null | undefined - type _strNullableRequired1 = TypeOf; + strNullableRequired.cast(''); // $ExpectType string - type _strNullableRequired2 = Asserts; + strNullableRequired.validateSync(''); + // + // const strNullable = string().nullable(); // $ExpectType string | null | undefined - type _strNullable = Asserts; + strNullable.validateSync(''); const strDefined = string().default(''); @@ -86,28 +94,73 @@ string().required().nullable(); const strDefault = string().nullable().default(''); // $ExpectType string | null - type _strDefault1 = TypeOf; + strDefault.cast(''); // $ExpectType string | null - type _strDefault2 = Asserts; + strDefault.validateSync(''); + // + // const strDefaultRequired = string().nullable().required().default(''); // $ExpectType string | null - type _strDefaultRequired1 = TypeOf; + strDefaultRequired.cast(''); // $ExpectType string - type _strDefaultRequired2 = Asserts; + strDefaultRequired.validateSync(null); } { const obj = object({ string: string().required(), number: number().default(1), + nest: object({ + other: string(), + }), }); - type ia = TypeOf; + // const f = obj.cast({}); + // f!.number; + // f!.string; + type ia = typeof obj['fields']['nest']['__inputType']; + + // $ExpectType number + type _i1 = TypeOfShape['number']; + + // $ExpectType string | undefined + type _i2 = TypeOfShape['string']; + + // $ExpectType number + type _i3 = AssertsShape['number']; + + // $ExpectType string + type _i4 = AssertsShape['string']; + + const cast1 = obj.cast({}); + + // $ExpectType string | undefined + cast1!.nest!.other; + + // $ExpectType string | undefined + cast1!.string; + + // $ExpectType number + cast1!.number; + + // + // Object Defaults + // + const dflt1 = obj.default(); + // $ExpectType 1 + dflt1.number; + + // $ExpectType undefined + dflt1.string; + + // $ExpectType undefined + dflt1.nest.other; } + // const strPlain = string(); // type fff = typeof strPlain['spec']['hasDefault']; From 5389b2e100e60fa9468f80cca8fd289f01ba1d5d Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 8 Sep 2020 15:18:17 -0400 Subject: [PATCH 08/31] wip --- src/Reference.ts | 5 +- src/array.ts | 91 ++- src/boolean.ts | 6 +- src/date.ts | 4 +- src/mixed.ts | 54 +- src/number.ts | 2 +- src/object.ts | 88 ++- src/string.ts | 10 - src/util/sortFields.ts | 3 +- test/types.ts | 89 ++- types/index.d.ts | 1646 ++++++++++++++++++++-------------------- 11 files changed, 1072 insertions(+), 926 deletions(-) diff --git a/src/Reference.ts b/src/Reference.ts index d6c0d8af4..3c46e5c9a 100644 --- a/src/Reference.ts +++ b/src/Reference.ts @@ -1,4 +1,5 @@ import { getter } from 'property-expr'; +import { SchemaRefDescription } from './Schema'; const prefixes = { context: '$', @@ -9,7 +10,7 @@ export type ReferenceOptions = { map?: (value: unknown) => unknown; }; -export function create(key: string, options: ReferenceOptions) { +export function create(key: string, options?: ReferenceOptions) { return new Reference(key, options); } @@ -73,7 +74,7 @@ export default class Reference { return this; } - describe() { + describe(): SchemaRefDescription { return { type: 'ref', key: this.key, diff --git a/src/array.ts b/src/array.ts index 4f876a69f..8348ac05d 100644 --- a/src/array.ts +++ b/src/array.ts @@ -4,30 +4,56 @@ import printValue from './util/printValue'; import MixedSchema from './mixed'; import { array as locale } from './locale'; import runTests, { RunTest } from './util/runTests'; -import Schema, { SchemaInnerTypeDescription } from './Schema'; -import { InternalOptions, Callback, Message } from './types'; +import { SchemaInnerTypeDescription } from './Schema'; +import { InternalOptions, Callback, Message, Maybe } from './types'; import ValidationError from './ValidationError'; -import { Test } from './util/createValidation'; import Reference from './Reference'; +import { + Asserts, + ResolveInput, + ResolveOutput, + SetNullability, + SetPresence, + TypeDef, + TypeOf, +} from './util/types'; type RefectorFn = (value: any, index: number, array: any[]) => boolean; -export function create(type: TInner) { +export function create( + type?: TInner, +) { return new ArraySchema(type); } -export default class ArraySchema extends MixedSchema { +type Type = T extends MixedSchema + ? TType + : never; + +export default class ArraySchema< + T extends MixedSchema = MixedSchema, + TDef extends TypeDef = 'optional' | 'nonnullable', + TDefault extends Maybe>[]> = undefined +> extends MixedSchema< + Type[], + TDef, + TDefault, + ResolveInput[], TDef, TDefault>, + ResolveOutput[], TDef, TDefault> +> { + // + private _subType?: T; innerType: T | undefined; - constructor(type: T) { + constructor(type?: T) { super({ type: 'array' }); // `undefined` specifically means uninitialized, as opposed to // "no subtype" - this._subType = undefined; - this.innerType = undefined; + this._subType = type; + this.innerType = type; this.withMutation(() => { this.transform(function (values) { @@ -40,8 +66,6 @@ export default class ArraySchema extends MixedSchema { return this.isType(values) ? values : null; }); - - if (type) this.of(type); }); } @@ -118,11 +142,12 @@ export default class ArraySchema extends MixedSchema { }; tests[idx] = (_, cb) => - // XXX: ??? - // innerType!.validate - // ? - innerType!.validate(item, innerOptions, cb); - // : cb(null); + innerType!.validate( + item, + innerOptions, + // @ts-expect-error + cb, + ); } runTests( @@ -143,7 +168,10 @@ export default class ArraySchema extends MixedSchema { return super._isPresent(value) && value.length > 0; } - of(schema: TInner | false) { + of( + schema: TInner | false, + ): ArraySchema { + // FIXME: this should return a new instance of array without the default to be var next = this.clone(); if (schema !== false && !isSchema(schema)) @@ -156,7 +184,7 @@ export default class ArraySchema extends MixedSchema { next._subType = schema as any; next.innerType = schema as any; - return next; + return next as any; } min(min: number | Reference, message?: Message<{ min: number }>) { @@ -189,7 +217,7 @@ export default class ArraySchema extends MixedSchema { } ensure() { - return this.default(() => []).transform((val, original) => { + return this.default(() => [] as Type[]).transform((val, original) => { // We don't want to return `null` for nullable schema if (this._typeCheck(val)) return val; return original == null ? [] : [].concat(original); @@ -212,3 +240,30 @@ export default class ArraySchema extends MixedSchema { return base; } } + +export default interface ArraySchema< + T extends MixedSchema, + TDef extends TypeDef, + TDefault extends Maybe>[]> +> extends MixedSchema< + Type[], + TDef, + TDefault, + ResolveInput[], TDef, TDefault>, + ResolveOutput[], TDef, TDefault> + > { + default(): TDefault; + default( + def: TNext | (() => TNext), + ): ArraySchema; + + required(): ArraySchema, TDefault>; + notRequired(): ArraySchema, TDefault>; + + nullable( + isNullable?: true, + ): ArraySchema, TDefault>; + nullable( + isNullable: false, + ): ArraySchema, TDefault>; +} diff --git a/src/boolean.ts b/src/boolean.ts index ab8d29dc8..e91d24239 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -4,7 +4,9 @@ export function create() { return new BooleanSchema(); } -export default class BooleanSchema extends MixedSchema { +export default class BooleanSchema extends MixedSchema< + TType +> { constructor() { super({ type: 'boolean' }); @@ -19,7 +21,7 @@ export default class BooleanSchema extends MixedSchema { }); } - protected _typeCheck(v: any) { + protected _typeCheck(v: any): v is TType { if (v instanceof Boolean) v = v.valueOf(); return typeof v === 'boolean'; diff --git a/src/date.ts b/src/date.ts index 4ff185b12..faf0dd90a 100644 --- a/src/date.ts +++ b/src/date.ts @@ -14,7 +14,7 @@ export function create() { return new DateSchema(); } -export default class DateSchema extends MixedSchema { +export default class DateSchema extends MixedSchema { constructor() { super({ type: 'date' }); @@ -30,7 +30,7 @@ export default class DateSchema extends MixedSchema { }); } - protected _typeCheck(v: any) { + protected _typeCheck(v: any): v is Date { return isDate(v) && !isNaN(v.getTime()); } diff --git a/src/mixed.ts b/src/mixed.ts index 1a1f0fa76..0218d053e 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -115,12 +115,14 @@ export function create() { export default class MixedSchema< TType = any, TDef extends TypeDef = 'optional' | 'nonnullable', - TDefault = any + TDefault = any, + TInput = ResolveInput, + TOutput = ResolveOutput > implements Schema { readonly type: string; - readonly __inputType!: ResolveInput; - readonly __outputType!: ResolveOutput; + readonly __inputType!: TInput; + readonly __outputType!: TOutput; readonly __isYupSchema__ = true; @@ -189,7 +191,7 @@ export default class MixedSchema< return this.type; } - protected _typeCheck(_: any) { + protected _typeCheck(v: any): v is TType { return true; } @@ -211,11 +213,13 @@ export default class MixedSchema< return next; } - meta(obj: {}) { - if (arguments.length === 0) return this._meta; + meta(): Record | undefined; + meta(obj: Record): void; + meta(...args: [Record?]) { + if (args.length === 0) return this._meta; - var next = this.clone(); - next._meta = Object.assign(next._meta || {}, obj); + let next = this.clone(); + next._meta = Object.assign(next._meta || {}, args[0]); return next; } @@ -414,10 +418,7 @@ export default class MixedSchema< ); } - validate( - value: any, - options?: ValidateOptions, - ): Promise; + validate(value: any, options?: ValidateOptions): Promise; validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { let schema = this.resolve({ ...options, value }); @@ -738,12 +739,29 @@ export default class MixedSchema< } } -for (const method of ['validate', 'validateSync']) - MixedSchema.prototype[`${method}At`] = function ( +export default interface MixedSchema< + TType, + TDef extends TypeDef, + TDefault, + TInput, + TOutput +> { + validateAt( path: string, value: any, - options: ValidateOptions = {}, - ) { + options?: ValidateOptions, + ): Promise; + validateSyncAt(path: string, value: any, options?: ValidateOptions): TOutput; + equals: MixedSchema['oneOf']; + is: MixedSchema['oneOf']; + not: MixedSchema['notOneOf']; + nope: MixedSchema['notOneOf']; +} + +for (const method of ['validate', 'validateSync']) + MixedSchema.prototype[ + `${method}At` as 'validateAt' | 'validateSyncAt' + ] = function (path: string, value: any, options: ValidateOptions = {}) { const { parent, parentPath, schema } = getIn( this, path, @@ -764,7 +782,3 @@ for (const alias of ['not', 'nope'] as const) MixedSchema.prototype[alias] = MixedSchema.prototype.notOneOf; MixedSchema.prototype.optional = MixedSchema.prototype.notRequired; - -type WithSpec> = T & { - spec: T['spec'] & TSpec; -}; diff --git a/src/number.ts b/src/number.ts index fd62d6392..eded5d996 100644 --- a/src/number.ts +++ b/src/number.ts @@ -129,7 +129,7 @@ export default class NumberSchema< } } -// @ts-ignore +// @ts-expect-error export default interface NumberSchema< TType extends number, TDef extends TypeDef, diff --git a/src/object.ts b/src/object.ts index f672757b2..98413ed3d 100644 --- a/src/object.ts +++ b/src/object.ts @@ -21,6 +21,7 @@ import { SetPresence, TypedSchema, } from './util/types'; +import Reference from './Reference'; let isObject = (obj: any): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; @@ -30,16 +31,23 @@ function unknown(ctx: ObjectSchema, value: any) { return Object.keys(value).filter((key) => known.indexOf(key) === -1); } -type ObjectShape = Record; +type AnyObject = Record; -// type Obj = Record; +type ObjectShape = Record; + +type AssignShape = { + [P in keyof T]: P extends keyof U ? U[P] : T[P]; +} & + U; export function create(spec?: TShape) { return new ObjectSchema(spec); } export type TypeFromShape = { - [K in keyof Shape]: Shape[K] extends MixedSchema + [K in keyof Shape]: Shape[K] extends Reference + ? unknown + : Shape[K] extends MixedSchema ? TType : // not sure why this is necessary Shape[K] extends ObjectSchema @@ -50,41 +58,43 @@ export type TypeFromShape = { export type DefaultFromShape = { [K in keyof Shape]: Shape[K] extends MixedSchema ? TDefault + : Shape[K] extends Reference + ? undefined : never; }; export type TypeOfShape = { - [K in keyof Shape]: Shape[K]['__inputType']; + [K in keyof Shape]: Shape[K] extends MixedSchema + ? Shape[K]['__inputType'] + : Shape[K] extends Reference + ? unknown + : never; }; export type AssertsShape = { - [K in keyof Shape]: Shape[K]['__outputType']; + [K in keyof Shape]: Shape[K] extends MixedSchema + ? Shape[K]['__outputType'] + : Shape[K] extends Reference + ? unknown + : never; }; -// export default interface ObjectSchema -// extends MixedSchema { -// cast(value: any, options?: any): TypeOfShape; - -// // validate(value: any, options?: any): Promise>; -// } - export default class ObjectSchema< TShape extends ObjectShape = ObjectShape, TDef extends TypeDef = 'optional' | 'nonnullable', - TDefault extends Maybe> = DefaultFromShape -> extends MixedSchema, TDef, TDefault> { + TDefault extends Maybe> = DefaultFromShape +> extends MixedSchema< + TypeFromShape, + TDef, + TDefault, + ResolveInput, TDef, TDefault>, + ResolveOutput, TDef, TDefault> +> { fields: TShape; - __inputType!: ResolveInput, TDef, TDefault>; - __outputType!: ResolveOutput, TDef, TDefault>; - - // _shape!: TShape; - // _tsType!: TypeOfShape; - // _tsValidate!: AssertsShape; - - // spec!: SchemaSpec & { - // noUnknown: boolean; - // }; + spec!: SchemaSpec & { + noUnknown?: boolean; + }; private _sortErrors: (a: ValidationError, b: ValidationError) => number; private _nodes: string[]; @@ -94,7 +104,6 @@ export default class ObjectSchema< super({ type: 'object', spec: { - noUnknown: false, default(this: ObjectSchema) { if (!this._nodes.length) return undefined; @@ -138,7 +147,7 @@ export default class ObjectSchema< }); } - protected _typeCheck(value: any): value is Record { + protected _typeCheck(value: any): value is TypeFromShape { return isObject(value) || typeof value === 'function'; } @@ -172,8 +181,9 @@ export default class ObjectSchema< let exists = has(value, prop); if (field) { + let fieldSpec = 'spec' in field ? field.spec : undefined; let fieldValue; - let strict = field.spec?.strict; + let strict = fieldSpec?.strict; let inputValue = value[prop]; @@ -187,7 +197,7 @@ export default class ObjectSchema< parent: intermediateValue, }); - if (field.spec?.strip) { + if (fieldSpec?.strip) { isChanged = isChanged || prop in value; continue; } @@ -258,8 +268,8 @@ export default class ObjectSchema< let field = this.fields[key]; - if (field && field.validate) { - (field as Schema).validate( + if (field && 'validate' in field) { + field.validate( value[key], { ...opts, @@ -272,6 +282,7 @@ export default class ObjectSchema< parent: value, originalValue: originalValue[key], }, + // @ts-expect-error cb, ); return; @@ -306,7 +317,7 @@ export default class ObjectSchema< shape( additions: TNextShape, excludes: [string, string][] = [], - ): ObjectSchema { + ): ObjectSchema> { let next = this.clone(); let fields = Object.assign(next.fields, additions); @@ -396,16 +407,19 @@ export default class ObjectSchema< } } -type AnyObject = Record; - -// @ts-ignore export default interface ObjectSchema< TShape extends ObjectShape, TDef extends TypeDef, - TDefault extends Maybe> -> extends MixedSchema, TDef, TDefault> { + TDefault extends Maybe> +> extends MixedSchema< + TypeFromShape, + TDef, + TDefault, + ResolveInput, TDef, TDefault>, + ResolveOutput, TDef, TDefault> + > { default(): TDefault; - default>>( + default>>( def: TNextDefault | (() => TNextDefault), ): ObjectSchema; diff --git a/src/string.ts b/src/string.ts index e2b07dc8b..b434f25cd 100644 --- a/src/string.ts +++ b/src/string.ts @@ -181,16 +181,6 @@ export default class StringSchema< isAbsent(value) || value === value.toUpperCase(), }); } - - // required(msg?: any): StringSchema { - // return super.required(msg) as any; - // } - - // nullable(isNullable?: true): StringSchema; - // nullable(isNullable: false): StringSchema>; - // nullable(isNullable?: boolean): StringSchema> { - // return super.nullable(isNullable); - // } } // @ts-ignore diff --git a/src/util/sortFields.ts b/src/util/sortFields.ts index e676a8b93..723c777dc 100644 --- a/src/util/sortFields.ts +++ b/src/util/sortFields.ts @@ -6,9 +6,10 @@ import { split } from 'property-expr'; import Ref from '../Reference'; import isSchema from './isSchema'; import type MixedSchema from '../mixed'; +import Reference from '../Reference'; export default function sortFields( - fields: Record, + fields: Record, excludes: string[] = [], ) { let edges = [] as Array<[string, string]>; diff --git a/test/types.ts b/test/types.ts index 79db83496..f9859f5e3 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,13 +1,8 @@ /* eslint-disable no-unused-expressions */ // import { Asserts } from '../src/mixed'; -import { string, object, mixed, number } from '../src'; +import { array, string, object, mixed, number, ref } from '../src'; import { AssertsShape, DefaultFromShape, TypeOfShape } from '../src/object'; -import { - Asserts, - ResolveInput, - ResolveOutput, - TypeOf, -} from '../src/util/types'; +import { ResolveInput, ResolveOutput } from '../src/util/types'; // let schema = object({ // str: string().nullable(), @@ -114,6 +109,7 @@ string().required().nullable(); const obj = object({ string: string().required(), number: number().default(1), + ref: ref('string'), nest: object({ other: string(), }), @@ -122,19 +118,23 @@ string().required().nullable(); // const f = obj.cast({}); // f!.number; // f!.string; - type ia = typeof obj['fields']['nest']['__inputType']; + // type ia = typeof obj['fields']['nest']['__inputType']; + type _d1 = DefaultFromShape; // $ExpectType number type _i1 = TypeOfShape['number']; // $ExpectType string | undefined type _i2 = TypeOfShape['string']; + // $ExpectType unknown + type _i3 = TypeOfShape['ref']; + // $ExpectType number - type _i3 = AssertsShape['number']; + type _i4 = AssertsShape['number']; // $ExpectType string - type _i4 = AssertsShape['string']; + type _i5 = AssertsShape['string']; const cast1 = obj.cast({}); @@ -151,16 +151,85 @@ string().required().nullable(); // Object Defaults // const dflt1 = obj.default(); + // $ExpectType 1 dflt1.number; + // $ExpectType undefined + dflt1.ref; + // $ExpectType undefined dflt1.string; // $ExpectType undefined dflt1.nest.other; + + const merge = object({ + field: string().required(), + other: string().default(''), + }).shape({ + field: number(), + }); + + // $ExpectType number | undefined + merge.cast({}).field; + + // $ExpectType string + merge.cast({}).other; } +{ + // $ExpectType (string | undefined)[] | undefined + array(string()).cast(null); + + // $ExpectType string[] | undefined + array(string().required()).validateSync(null); + + // $ExpectType string[] + array(string().default('')).required().validateSync(null); + + // $ExpectType string[] | undefined + array(string().default('')).validateSync(null); + + // $ExpectType (string | null)[] | undefined + array(string().nullable().default('')).validateSync(null); + + // $ExpectType number[] + array() + .default([] as number[]) + .default(); + + // $ExpectType string[] | (string | null)[] | null + array(string().nullable().default('')) + .nullable() + .default(() => [] as string[]) + .validateSync(null); + + // $ExpectType number[] + array(number()) + .default(() => []) + .default(); + + const a1 = object({ + list: array(number().required()).required(), + nested: array( + object({ + name: string().default(''), + }), + ), + }) + .required() + .validateSync(undefined); + + // $ExpectType number[] + a1.list; + + // $ExpectType string | undefined + a1.nested?.[0].name; + + // $ExpectType string + a1.nested![0].name; +} // const strPlain = string(); // type fff = typeof strPlain['spec']['hasDefault']; diff --git a/types/index.d.ts b/types/index.d.ts index 512bff878..eb52f1609 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,823 +1,823 @@ -// Type definitions for yup 0.29 -// Project: https://github.com/jquense/yup -// Definitions by: Dominik Hardtke , -// Vladyslav Tserman , -// Moreton Bay Regional Council , -// Sindre Seppola -// Yash Kulshrestha -// Vincent Pizzo -// Robert Bullen -// Yusuke Sato -// Desmond Koh -// Maurice de Beijer -// Kalley Powell -// Elías García -// Ian Sanders -// Jay Fong -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 3.6 - -export function reach( - schema: Schema, - path: string, - value?: any, - context?: any, -): Schema; -export function addMethod>( - schemaCtor: AnySchemaConstructor, - name: string, - method: (this: T, ...args: any[]) => T, -): void; -export function ref(path: string, options?: { contextPrefix: string }): Ref; -export function lazy(fn: (value: T) => Schema): Lazy; -export function setLocale(customLocale: LocaleObject): void; -export function isSchema(obj: any): obj is Schema; - -export const mixed: MixedSchemaConstructor; -export const string: StringSchemaConstructor; -export const number: NumberSchemaConstructor; -export const boolean: BooleanSchemaConstructor; -export const bool: BooleanSchemaConstructor; -export const date: DateSchemaConstructor; -export const array: ArraySchemaConstructor; -export const object: ObjectSchemaConstructor; - -export type AnySchemaConstructor = - | MixedSchemaConstructor - | StringSchemaConstructor - | NumberSchemaConstructor - | BooleanSchemaConstructor - | DateSchemaConstructor - | ArraySchemaConstructor - | ObjectSchemaConstructor; - -export type TestOptionsMessage< - Extra extends Record = {}, - R = any -> = string | ((params: Extra & Partial) => R); - -export interface Schema { - type: string; - clone(): this; - label(label: string): this; - meta(metadata: any): this; - meta(): any; - describe(): SchemaDescription; - concat(schema: this): this; - validate(value: any, options?: ValidateOptions): Promise; - validateSync(value: any, options?: ValidateOptions): T; - validateAt(path: string, value: T, options?: ValidateOptions): Promise; - validateSyncAt(path: string, value: T, options?: ValidateOptions): T; - isValid(value: any, options?: any): Promise; - isValidSync(value: any, options?: any): value is T; - cast(value?: any, options?: any): T; - isType(value: any): value is T; - strict(isStrict: boolean): this; - strip(strip: boolean): this; - withMutation(fn: (current: this) => void): void; - default(value: any): this; - default(): T; - typeError(message?: TestOptionsMessage): this; - notOneOf(arrayOfValues: any[], message?: MixedLocale['notOneOf']): this; - when(keys: string | any[], builder: WhenOptions): this; - transform(fn: TransformFunction): this; -} - -// Note: Using `{} | null | undefined` allows excluding `null` & `undefined` -// whereas using `any` as the default type would mean that `nullable(false)`, -// `defined`, and `required` would all have no effect. - -export interface MixedSchemaConstructor { - // tslint:disable-next-line:no-unnecessary-generics - (): MixedSchema; - // tslint:disable-next-line:no-unnecessary-generics - new (options?: { - type?: string; - [key: string]: any; - }): MixedSchema; -} - -export interface MixedSchema - extends Schema { - nullable(isNullable?: true): MixedSchema; - nullable(isNullable: false): MixedSchema>; - nullable(isNullable?: boolean): MixedSchema; - required( - message?: TestOptionsMessage, - ): MixedSchema>; - defined(): MixedSchema>; - notRequired(): MixedSchema; - optional(): MixedSchema; - concat(schema: this): this; - concat(schema: Schema): MixedSchema; - oneOf( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): MixedSchema>; - equals( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): MixedSchema>; - test( - name: string, - message: TestOptionsMessage, - test: AssertingTestFunction, - ): MixedSchema; - test(name: string, message: TestOptionsMessage, test: TestFunction): this; - test( - options: AssertingTestOptions>, - ): MixedSchema; - test(options: TestOptions>): this; -} - -export interface StringSchemaConstructor { - (): StringSchema; - new (): StringSchema; -} - -export interface StringSchema< - T extends string | null | undefined = string | undefined -> extends Schema { - length( - limit: number | Ref, - message?: StringLocale['length'], - ): StringSchema; - min(limit: number | Ref, message?: StringLocale['min']): StringSchema; - max(limit: number | Ref, message?: StringLocale['max']): StringSchema; - matches( - regex: RegExp, - messageOrOptions?: - | StringLocale['matches'] - | { message?: StringLocale['matches']; excludeEmptyString?: boolean }, - ): StringSchema; - email(message?: StringLocale['email']): StringSchema; - url(message?: StringLocale['url']): StringSchema; - uuid(message?: StringLocale['uuid']): StringSchema; - ensure(): StringSchema; - trim(message?: StringLocale['trim']): StringSchema; - lowercase(message?: StringLocale['lowercase']): StringSchema; - uppercase(message?: StringLocale['uppercase']): StringSchema; - nullable(isNullable?: true): StringSchema; - nullable(isNullable: false): StringSchema>; - nullable(isNullable?: boolean): StringSchema; - required( - message?: TestOptionsMessage, - ): StringSchema>; - defined(): StringSchema>; - notRequired(): StringSchema; - oneOf( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): StringSchema>; - equals( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): StringSchema>; - /* - All TestFunction generics are intentionally T with (undefined | null) as previous .required / .defined / .nullable - will narrow out those types, and tests run for (undefined | null) even if they're not allowed. - */ - test( - name: string, - message: TestOptionsMessage, - test: TestFunction, - ): this; - test( - name: string, - message: TestOptionsMessage, - test: AssertingTestFunction, - ): StringSchema; - test( - options: AssertingTestOptions>, - ): StringSchema; - test(options: TestOptions>): this; - optional(): StringSchema; -} - -export interface NumberSchemaConstructor { - (): NumberSchema; - new (): NumberSchema; -} - -export interface NumberSchema< - T extends number | null | undefined = number | undefined -> extends Schema { - min(limit: number | Ref, message?: NumberLocale['min']): NumberSchema; - max(limit: number | Ref, message?: NumberLocale['max']): NumberSchema; - lessThan( - limit: number | Ref, - message?: NumberLocale['lessThan'], - ): NumberSchema; - moreThan( - limit: number | Ref, - message?: NumberLocale['moreThan'], - ): NumberSchema; - positive(message?: NumberLocale['positive']): NumberSchema; - negative(message?: NumberLocale['negative']): NumberSchema; - integer(message?: NumberLocale['integer']): NumberSchema; - truncate(): NumberSchema; - round(type: 'floor' | 'ceil' | 'trunc' | 'round'): NumberSchema; - nullable(isNullable?: true): NumberSchema; - nullable(isNullable: false): NumberSchema>; - nullable(isNullable?: boolean): NumberSchema; - required( - message?: TestOptionsMessage, - ): NumberSchema>; - defined(): NumberSchema>; - notRequired(): NumberSchema; - oneOf( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): NumberSchema>; - equals( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): NumberSchema>; - test( - name: string, - message: TestOptionsMessage, - test: TestFunction, - ): this; - test( - name: string, - message: TestOptionsMessage, - test: AssertingTestFunction, - ): NumberSchema; - test( - options: AssertingTestOptions>, - ): NumberSchema; - test(options: TestOptions>): this; - optional(): NumberSchema; -} - -export interface BooleanSchemaConstructor { - (): BooleanSchema; - new (): BooleanSchema; -} - -export interface BooleanSchema< - T extends boolean | null | undefined = boolean | undefined -> extends Schema { - nullable(isNullable?: true): BooleanSchema; - nullable(isNullable: false): BooleanSchema>; - nullable(isNullable?: boolean): BooleanSchema; - required( - message?: TestOptionsMessage, - ): BooleanSchema>; - defined(): BooleanSchema>; - notRequired(): BooleanSchema; - oneOf( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): BooleanSchema>; - equals( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): BooleanSchema>; - test( - name: string, - message: TestOptionsMessage, - test: TestFunction, - ): this; - test( - name: string, - message: TestOptionsMessage, - test: AssertingTestFunction, - ): BooleanSchema; - test( - options: AssertingTestOptions>, - ): BooleanSchema; - test(options: TestOptions>): this; - optional(): BooleanSchema; -} - -export interface DateSchemaConstructor { - (): DateSchema; - new (): DateSchema; -} - -export interface DateSchema< - T extends Date | null | undefined = Date | undefined -> extends Schema { - min(limit: Date | string | Ref, message?: DateLocale['min']): DateSchema; - max(limit: Date | string | Ref, message?: DateLocale['max']): DateSchema; - nullable(isNullable?: true): DateSchema; - nullable(isNullable: false): DateSchema>; - nullable(isNullable?: boolean): DateSchema; - required( - message?: TestOptionsMessage, - ): DateSchema>; - defined(): DateSchema>; - notRequired(): DateSchema; - oneOf( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): DateSchema>; - equals( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): DateSchema>; - test( - name: string, - message: TestOptionsMessage, - test: TestFunction, - ): this; - test( - name: string, - message: TestOptionsMessage, - test: AssertingTestFunction, - ): DateSchema; - test( - options: AssertingTestOptions>, - ): DateSchema; - test(options: TestOptions>): this; - optional(): DateSchema; -} - -export interface ArraySchemaConstructor { - (schema?: Schema): NotRequiredArraySchema; - new (): NotRequiredArraySchema<{}>; -} - -export interface BasicArraySchema - extends Schema { - min(limit: number | Ref, message?: ArrayLocale['min']): this; - max(limit: number | Ref, message?: ArrayLocale['max']): this; - ensure(): this; - compact( - rejector?: ( - value: InferredArrayType, - index: number, - array: Array>, - ) => boolean, - ): this; - - // This doesn't narrow the type of the schema like the more primitive oneOf calls - // it would be very complex to do that accurately with the subtypes, and it's not - // really worth it because the oneOf check is a shallow (==) comparison so it rarely - // applies to arrays anyway. - oneOf( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): this; - equals( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): this; - test( - name: string, - message: TestOptionsMessage, - test: TestFunction, - ): this; - test(options: TestOptions>): this; - innerType: Schema; -} - -export interface NotRequiredNullableArraySchema - extends BasicArraySchema { - of(type: Schema): NotRequiredNullableArraySchema; - nullable(isNullable?: true): NotRequiredNullableArraySchema; - nullable(isNullable: false): NotRequiredArraySchema; - nullable(isNullable?: boolean): ArraySchema; - required(message?: TestOptionsMessage): ArraySchema; - defined(): NullableArraySchema; - notRequired(): NotRequiredNullableArraySchema; - optional(): NotRequiredNullableArraySchema; -} - -export interface NullableArraySchema - extends BasicArraySchema { - of(type: Schema): NullableArraySchema; - nullable(isNullable?: true): NullableArraySchema; - nullable(isNullable: false): ArraySchema; - nullable(isNullable?: boolean): ArraySchema; - required(message?: TestOptionsMessage): ArraySchema; - defined(): NullableArraySchema; - notRequired(): NotRequiredNullableArraySchema; - optional(): NotRequiredNullableArraySchema; -} - -export interface NotRequiredArraySchema - extends BasicArraySchema { - of(type: Schema): NotRequiredArraySchema; - nullable(isNullable?: true): NotRequiredNullableArraySchema; - nullable(isNullable: false): NotRequiredArraySchema; - nullable(isNullable: boolean): ArraySchema; - required(message?: TestOptionsMessage): ArraySchema; - defined(): ArraySchema; - notRequired(): NotRequiredArraySchema; - optional(): NotRequiredArraySchema; -} - -export interface ArraySchema extends BasicArraySchema { - of(type: Schema): ArraySchema; - nullable(isNullable?: true): NullableArraySchema; - nullable(isNullable: false | boolean): ArraySchema; - required(message?: TestOptionsMessage): ArraySchema; - defined(): ArraySchema; - notRequired(): NotRequiredArraySchema; - optional(): NotRequiredArraySchema; -} - -export type ObjectSchemaDefinition = { - // This shouldn't be necessary because MixedSchema extends Schema, but type - // inference only works with it this way - otherwise when you use a mixed - // field in object schema, it will type as `unknown`. Not sure why that is - - // maybe some sort of inference depth limit? - [field in keyof T]: Schema | MixedSchema | Ref; -}; - -/** - * Merges two interfaces. For properties in common, property types from `U` trump those of `T`. - * This is conducive to the functionality of - * [yup's `object.shape()` method](https://www.npmjs.com/package/yup#objectshapefields-object-nosortedges-arraystring-string-schema). - */ -export type Shape = - | ({ [P in keyof T]: P extends keyof U ? U[P] : T[P] } & U) - | PreserveOptionals; - -export interface ObjectSchemaConstructor { - (fields?: ObjectSchemaDefinition): ObjectSchema< - T | undefined - >; - new (): ObjectSchema<{}>; -} - -export interface ObjectSchema< - T extends object | null | undefined = object | undefined -> extends Schema { - fields: { - [k in keyof T]: Schema; - }; - shape( - fields: ObjectSchemaDefinition, - noSortEdges?: Array<[string, string]>, - ): ObjectSchema>; - from(fromKey: string, toKey: string, alias?: boolean): ObjectSchema; - noUnknown( - onlyKnownKeys?: boolean, - message?: ObjectLocale['noUnknown'], - ): ObjectSchema; - unknown( - allow?: boolean, - message?: ObjectLocale['noUnknown'], - ): ObjectSchema; - transformKeys(callback: (key: any) => any): void; - camelCase(): ObjectSchema; - snakeCase(): ObjectSchema; - constantCase(): ObjectSchema; - nullable(isNullable?: true): ObjectSchema; - nullable(isNullable: false): ObjectSchema>; - nullable(isNullable?: boolean): ObjectSchema; - required( - message?: TestOptionsMessage, - ): ObjectSchema>; - defined(): ObjectSchema>; - notRequired(): ObjectSchema; - optional(): ObjectSchema; - concat(schema: this): this; - concat(schema: ObjectSchema): ObjectSchema; - oneOf( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): ObjectSchema; - equals( - arrayOfValues: ReadonlyArray, - message?: MixedLocale['oneOf'], - ): ObjectSchema; - test( - name: string, - message: TestOptionsMessage, - test: TestFunction, - ): this; - test( - name: string, - message: TestOptionsMessage, - test: AssertingTestFunction, - ): ObjectSchema; - test( - options: AssertingTestOptions>, - ): ObjectSchema; - test(options: TestOptions>): this; -} - -export type TestFunction = ( - this: TestContext, - value: T, -) => boolean | ValidationError | Promise; -export type AssertingTestFunction = ( - this: TestContext, - value: any, -) => value is T; - -export type TransformFunction = ( - this: T, - value: any, - originalValue: any, -) => any; - -export interface WhenOptionsBuilderFunction { - (value: any, schema: T): T; - (v1: any, v2: any, schema: T): T; - (v1: any, v2: any, v3: any, schema: T): T; - (v1: any, v2: any, v3: any, v4: any, schema: T): T; -} - -export type WhenOptionsBuilderObjectIs = - | ((...values: any[]) => boolean) - | boolean - | number - | null - | object - | string; - -export type WhenOptionsBuilderObject = - | { - is: WhenOptionsBuilderObjectIs; - then: any; - otherwise: any; - } - | object; - -export type WhenOptions = - | WhenOptionsBuilderFunction - | WhenOptionsBuilderObject; - -export interface TestContext { - path: string; - options: ValidateOptions; - parent: any; - schema: Schema; - resolve: (value: any) => any; - createError: (params?: { - path?: string; - message?: string; - params?: object; - }) => ValidationError; -} - -export interface ValidateOptions { - /** - * Only validate the input, and skip and coercion or transformation. Default - false - */ - strict?: boolean; - /** - * Return from validation methods on the first error rather than after all validations run. Default - true - */ - abortEarly?: boolean; - /** - * Remove unspecified keys from objects. Default - false - */ - stripUnknown?: boolean; - /** - * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true - */ - recursive?: boolean; - /** - * Any context needed for validating schema conditions (see: when()) - */ - context?: object; -} - -export interface TestMessageParams { - path: string; - value: any; - originalValue: any; - label: string; -} - -interface BaseTestOptions

> { - /** - * Unique name identifying the test. Required for exclusive tests. - */ - name?: string; - - /** - * Test function, determines schema validity - */ - test: TestFunction; - - /** - * The validation error message - */ - message?: TestOptionsMessage

; - - /** - * Values passed to message for interpolation - */ - params?: P; - - /** - * Mark the test as exclusive, meaning only one of the same can be active at once - */ - exclusive?: boolean; -} - -interface NonExclusiveTestOptions

> - extends BaseTestOptions

{ - exclusive?: false; -} - -interface ExclusiveTestOptions

> - extends BaseTestOptions

{ - exclusive: true; - name: string; -} - -interface NonExclusiveAssertingTestOptions> - extends NonExclusiveTestOptions

{ - test: AssertingTestFunction; -} - -interface ExclusiveAssertingTestOptions> - extends ExclusiveTestOptions

{ - test: AssertingTestFunction; -} - -export type TestOptions

= {}> = - | NonExclusiveTestOptions

- | ExclusiveTestOptions

; - -export type AssertingTestOptions = {}> = - | NonExclusiveAssertingTestOptions - | ExclusiveAssertingTestOptions; - -export interface SchemaFieldRefDescription { - type: 'ref'; - key: string; -} - -export interface SchemaFieldInnerTypeDescription - extends Pick> { - innerType?: SchemaFieldDescription; -} - -export type SchemaFieldDescription = - | SchemaDescription - | SchemaFieldRefDescription - | SchemaFieldInnerTypeDescription; - -export interface SchemaDescription { - type: string; - label: string; - meta: object; - tests: Array<{ name: string; params: { [k: string]: any } }>; - fields: Record; -} - -// ValidationError works a lot more like a class vs. a constructor -// function that returns an interface. It's also got a couple of -// static methods and it inherits from the generic Error class in -// the [yup codebase][1]. -// [1]: (https://github.com/jquense/yup/blob/master/src/ValidationError.js) -export class ValidationError extends Error { - name: string; - message: string; - value: any; - /** - * A string, indicating where there error was thrown. path is empty at the root level. - */ - path: string; - type: any; - /** - * array of error messages - */ - errors: string[]; - - /** - * In the case of aggregate errors, inner is an array of ValidationErrors throw earlier in the validation chain. - */ - inner: ValidationError[]; - params?: object; - - static isError(err: any): err is ValidationError; - static formatError( - message: string | ((params?: any) => string), - params?: any, - ): string | ((params?: any) => string); - - constructor(errors: string | string[], value: any, path: string, type?: any); -} - -// It is tempting to declare `Ref` very simply, but there are problems with these approaches: -// -// * `type Ref = Record;` - This is essentially how it was originally declared, but -// just about any object satisfies this contract, which makes the type declaration too loose to -// be useful. -// -// * `type Ref = object;` - This is a variation on the previous bullet in that it is too loose. -// -// * `class Ref {}` - This is yet another variation that is too loose. -// -// * `type Ref = void;` - This works and the emitted JavaScript is just fine, but it results in some -// confusing IntelliSense, e.g it looks like the `ref()` returns `void`, which is not the case. -// -// The solution is twofold. 1.) Declare it as a class with a private constructor to prevent it from -// being instantiated by anything but the `ref()` factory function, and; 2.) declare a private -// readonly property (that yup actually places on the prototype) to force it to be structurally -// incompatible with any other object type. - -/** - * `Ref` is an opaque type that is internal to yup. Creating a `Ref` instance is accomplished via the `ref()` factory - * function. - */ -export class Ref { - private constructor(); - private readonly __isYupRef: true; -} - -// tslint:disable-next-line:no-empty-interface -export interface Lazy extends Schema {} - -export interface FormatErrorParams { - path: string; - type: string; - value?: any; - originalValue?: any; -} - -export type LocaleValue = string | ((params: FormatErrorParams) => string); - -export interface MixedLocale { - default?: TestOptionsMessage; - required?: TestOptionsMessage; - oneOf?: TestOptionsMessage<{ values: any }>; - notOneOf?: TestOptionsMessage<{ values: any }>; - notType?: LocaleValue; -} - -export interface StringLocale { - length?: TestOptionsMessage<{ length: number }>; - min?: TestOptionsMessage<{ min: number }>; - max?: TestOptionsMessage<{ max: number }>; - matches?: TestOptionsMessage<{ regex: RegExp }>; - email?: TestOptionsMessage<{ regex: RegExp }>; - url?: TestOptionsMessage<{ regex: RegExp }>; - uuid?: TestOptionsMessage<{ regex: RegExp }>; - trim?: TestOptionsMessage; - lowercase?: TestOptionsMessage; - uppercase?: TestOptionsMessage; -} - -export interface NumberLocale { - min?: TestOptionsMessage<{ min: number }>; - max?: TestOptionsMessage<{ max: number }>; - lessThan?: TestOptionsMessage<{ less: number }>; - moreThan?: TestOptionsMessage<{ more: number }>; - positive?: TestOptionsMessage<{ more: number }>; - negative?: TestOptionsMessage<{ less: number }>; - integer?: TestOptionsMessage; -} - -export interface DateLocale { - min?: TestOptionsMessage<{ min: Date | string }>; - max?: TestOptionsMessage<{ max: Date | string }>; -} - -export interface ObjectLocale { - noUnknown?: TestOptionsMessage; -} - -export interface ArrayLocale { - min?: TestOptionsMessage<{ min: number }>; - max?: TestOptionsMessage<{ max: number }>; -} - -export interface LocaleObject { - mixed?: MixedLocale; - string?: StringLocale; - number?: NumberLocale; - date?: DateLocale; - boolean?: {}; - object?: ObjectLocale; - array?: ArrayLocale; -} - -export type InferType = T extends Schema - ? InnerInferType

- : never; - -// Shut off automatic exporting after this statement -export {}; - -type KeyOfUndefined = { - [P in keyof T]-?: undefined extends T[P] ? P : never; -}[keyof T]; - -type PreserveNull = T extends null ? null : never; -type PreserveUndefined = T extends undefined ? undefined : never; -type PreserveOptionals = PreserveNull | PreserveUndefined; -type Id = { - [K in keyof T]: T[K] extends object ? InnerInferType : T[K]; -}; -type RequiredProps = Pick>>; -type NotRequiredProps = Partial>>; -type InnerInferType = - | (T extends Array - ? InnerInferTypeArray - : Id & RequiredProps>) - | PreserveOptionals; -interface InnerInferTypeArray extends Array> {} -type InferredArrayType = T extends Array ? U : T; -/** If `T` is optional, returns optional `U`. */ -type MaintainOptionality = T extends undefined ? U | undefined : U; +// // Type definitions for yup 0.29 +// // Project: https://github.com/jquense/yup +// // Definitions by: Dominik Hardtke , +// // Vladyslav Tserman , +// // Moreton Bay Regional Council , +// // Sindre Seppola +// // Yash Kulshrestha +// // Vincent Pizzo +// // Robert Bullen +// // Yusuke Sato +// // Desmond Koh +// // Maurice de Beijer +// // Kalley Powell +// // Elías García +// // Ian Sanders +// // Jay Fong +// // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// // TypeScript Version: 3.6 + +// export function reach( +// schema: Schema, +// path: string, +// value?: any, +// context?: any, +// ): Schema; +// export function addMethod>( +// schemaCtor: AnySchemaConstructor, +// name: string, +// method: (this: T, ...args: any[]) => T, +// ): void; +// export function ref(path: string, options?: { contextPrefix: string }): Ref; +// export function lazy(fn: (value: T) => Schema): Lazy; +// export function setLocale(customLocale: LocaleObject): void; +// export function isSchema(obj: any): obj is Schema; + +// export const mixed: MixedSchemaConstructor; +// export const string: StringSchemaConstructor; +// export const number: NumberSchemaConstructor; +// export const boolean: BooleanSchemaConstructor; +// export const bool: BooleanSchemaConstructor; +// export const date: DateSchemaConstructor; +// export const array: ArraySchemaConstructor; +// export const object: ObjectSchemaConstructor; + +// export type AnySchemaConstructor = +// | MixedSchemaConstructor +// | StringSchemaConstructor +// | NumberSchemaConstructor +// | BooleanSchemaConstructor +// | DateSchemaConstructor +// | ArraySchemaConstructor +// | ObjectSchemaConstructor; + +// export type TestOptionsMessage< +// Extra extends Record = {}, +// R = any +// > = string | ((params: Extra & Partial) => R); + +// export interface Schema { +// type: string; +// clone(): this; +// label(label: string): this; +// meta(metadata: any): this; +// meta(): any; +// describe(): SchemaDescription; +// concat(schema: this): this; +// validate(value: any, options?: ValidateOptions): Promise; +// validateSync(value: any, options?: ValidateOptions): T; +// validateAt(path: string, value: T, options?: ValidateOptions): Promise; +// validateSyncAt(path: string, value: T, options?: ValidateOptions): T; +// isValid(value: any, options?: any): Promise; +// isValidSync(value: any, options?: any): value is T; +// cast(value?: any, options?: any): T; +// isType(value: any): value is T; +// strict(isStrict: boolean): this; +// strip(strip: boolean): this; +// withMutation(fn: (current: this) => void): void; +// default(value: any): this; +// default(): T; +// typeError(message?: TestOptionsMessage): this; +// notOneOf(arrayOfValues: any[], message?: MixedLocale['notOneOf']): this; +// when(keys: string | any[], builder: WhenOptions): this; +// transform(fn: TransformFunction): this; +// } + +// // Note: Using `{} | null | undefined` allows excluding `null` & `undefined` +// // whereas using `any` as the default type would mean that `nullable(false)`, +// // `defined`, and `required` would all have no effect. + +// export interface MixedSchemaConstructor { +// // tslint:disable-next-line:no-unnecessary-generics +// (): MixedSchema; +// // tslint:disable-next-line:no-unnecessary-generics +// new (options?: { +// type?: string; +// [key: string]: any; +// }): MixedSchema; +// } + +// export interface MixedSchema +// extends Schema { +// nullable(isNullable?: true): MixedSchema; +// nullable(isNullable: false): MixedSchema>; +// nullable(isNullable?: boolean): MixedSchema; +// required( +// message?: TestOptionsMessage, +// ): MixedSchema>; +// defined(): MixedSchema>; +// notRequired(): MixedSchema; +// optional(): MixedSchema; +// concat(schema: this): this; +// concat(schema: Schema): MixedSchema; +// oneOf( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): MixedSchema>; +// equals( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): MixedSchema>; +// test( +// name: string, +// message: TestOptionsMessage, +// test: AssertingTestFunction, +// ): MixedSchema; +// test(name: string, message: TestOptionsMessage, test: TestFunction): this; +// test( +// options: AssertingTestOptions>, +// ): MixedSchema; +// test(options: TestOptions>): this; +// } + +// export interface StringSchemaConstructor { +// (): StringSchema; +// new (): StringSchema; +// } + +// export interface StringSchema< +// T extends string | null | undefined = string | undefined +// > extends Schema { +// length( +// limit: number | Ref, +// message?: StringLocale['length'], +// ): StringSchema; +// min(limit: number | Ref, message?: StringLocale['min']): StringSchema; +// max(limit: number | Ref, message?: StringLocale['max']): StringSchema; +// matches( +// regex: RegExp, +// messageOrOptions?: +// | StringLocale['matches'] +// | { message?: StringLocale['matches']; excludeEmptyString?: boolean }, +// ): StringSchema; +// email(message?: StringLocale['email']): StringSchema; +// url(message?: StringLocale['url']): StringSchema; +// uuid(message?: StringLocale['uuid']): StringSchema; +// ensure(): StringSchema; +// trim(message?: StringLocale['trim']): StringSchema; +// lowercase(message?: StringLocale['lowercase']): StringSchema; +// uppercase(message?: StringLocale['uppercase']): StringSchema; +// nullable(isNullable?: true): StringSchema; +// nullable(isNullable: false): StringSchema>; +// nullable(isNullable?: boolean): StringSchema; +// required( +// message?: TestOptionsMessage, +// ): StringSchema>; +// defined(): StringSchema>; +// notRequired(): StringSchema; +// oneOf( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): StringSchema>; +// equals( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): StringSchema>; +// /* +// All TestFunction generics are intentionally T with (undefined | null) as previous .required / .defined / .nullable +// will narrow out those types, and tests run for (undefined | null) even if they're not allowed. +// */ +// test( +// name: string, +// message: TestOptionsMessage, +// test: TestFunction, +// ): this; +// test( +// name: string, +// message: TestOptionsMessage, +// test: AssertingTestFunction, +// ): StringSchema; +// test( +// options: AssertingTestOptions>, +// ): StringSchema; +// test(options: TestOptions>): this; +// optional(): StringSchema; +// } + +// export interface NumberSchemaConstructor { +// (): NumberSchema; +// new (): NumberSchema; +// } + +// export interface NumberSchema< +// T extends number | null | undefined = number | undefined +// > extends Schema { +// min(limit: number | Ref, message?: NumberLocale['min']): NumberSchema; +// max(limit: number | Ref, message?: NumberLocale['max']): NumberSchema; +// lessThan( +// limit: number | Ref, +// message?: NumberLocale['lessThan'], +// ): NumberSchema; +// moreThan( +// limit: number | Ref, +// message?: NumberLocale['moreThan'], +// ): NumberSchema; +// positive(message?: NumberLocale['positive']): NumberSchema; +// negative(message?: NumberLocale['negative']): NumberSchema; +// integer(message?: NumberLocale['integer']): NumberSchema; +// truncate(): NumberSchema; +// round(type: 'floor' | 'ceil' | 'trunc' | 'round'): NumberSchema; +// nullable(isNullable?: true): NumberSchema; +// nullable(isNullable: false): NumberSchema>; +// nullable(isNullable?: boolean): NumberSchema; +// required( +// message?: TestOptionsMessage, +// ): NumberSchema>; +// defined(): NumberSchema>; +// notRequired(): NumberSchema; +// oneOf( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): NumberSchema>; +// equals( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): NumberSchema>; +// test( +// name: string, +// message: TestOptionsMessage, +// test: TestFunction, +// ): this; +// test( +// name: string, +// message: TestOptionsMessage, +// test: AssertingTestFunction, +// ): NumberSchema; +// test( +// options: AssertingTestOptions>, +// ): NumberSchema; +// test(options: TestOptions>): this; +// optional(): NumberSchema; +// } + +// export interface BooleanSchemaConstructor { +// (): BooleanSchema; +// new (): BooleanSchema; +// } + +// export interface BooleanSchema< +// T extends boolean | null | undefined = boolean | undefined +// > extends Schema { +// nullable(isNullable?: true): BooleanSchema; +// nullable(isNullable: false): BooleanSchema>; +// nullable(isNullable?: boolean): BooleanSchema; +// required( +// message?: TestOptionsMessage, +// ): BooleanSchema>; +// defined(): BooleanSchema>; +// notRequired(): BooleanSchema; +// oneOf( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): BooleanSchema>; +// equals( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): BooleanSchema>; +// test( +// name: string, +// message: TestOptionsMessage, +// test: TestFunction, +// ): this; +// test( +// name: string, +// message: TestOptionsMessage, +// test: AssertingTestFunction, +// ): BooleanSchema; +// test( +// options: AssertingTestOptions>, +// ): BooleanSchema; +// test(options: TestOptions>): this; +// optional(): BooleanSchema; +// } + +// export interface DateSchemaConstructor { +// (): DateSchema; +// new (): DateSchema; +// } + +// export interface DateSchema< +// T extends Date | null | undefined = Date | undefined +// > extends Schema { +// min(limit: Date | string | Ref, message?: DateLocale['min']): DateSchema; +// max(limit: Date | string | Ref, message?: DateLocale['max']): DateSchema; +// nullable(isNullable?: true): DateSchema; +// nullable(isNullable: false): DateSchema>; +// nullable(isNullable?: boolean): DateSchema; +// required( +// message?: TestOptionsMessage, +// ): DateSchema>; +// defined(): DateSchema>; +// notRequired(): DateSchema; +// oneOf( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): DateSchema>; +// equals( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): DateSchema>; +// test( +// name: string, +// message: TestOptionsMessage, +// test: TestFunction, +// ): this; +// test( +// name: string, +// message: TestOptionsMessage, +// test: AssertingTestFunction, +// ): DateSchema; +// test( +// options: AssertingTestOptions>, +// ): DateSchema; +// test(options: TestOptions>): this; +// optional(): DateSchema; +// } + +// export interface ArraySchemaConstructor { +// (schema?: Schema): NotRequiredArraySchema; +// new (): NotRequiredArraySchema<{}>; +// } + +// export interface BasicArraySchema +// extends Schema { +// min(limit: number | Ref, message?: ArrayLocale['min']): this; +// max(limit: number | Ref, message?: ArrayLocale['max']): this; +// ensure(): this; +// compact( +// rejector?: ( +// value: InferredArrayType, +// index: number, +// array: Array>, +// ) => boolean, +// ): this; + +// // This doesn't narrow the type of the schema like the more primitive oneOf calls +// // it would be very complex to do that accurately with the subtypes, and it's not +// // really worth it because the oneOf check is a shallow (==) comparison so it rarely +// // applies to arrays anyway. +// oneOf( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): this; +// equals( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): this; +// test( +// name: string, +// message: TestOptionsMessage, +// test: TestFunction, +// ): this; +// test(options: TestOptions>): this; +// innerType: Schema; +// } + +// export interface NotRequiredNullableArraySchema +// extends BasicArraySchema { +// of(type: Schema): NotRequiredNullableArraySchema; +// nullable(isNullable?: true): NotRequiredNullableArraySchema; +// nullable(isNullable: false): NotRequiredArraySchema; +// nullable(isNullable?: boolean): ArraySchema; +// required(message?: TestOptionsMessage): ArraySchema; +// defined(): NullableArraySchema; +// notRequired(): NotRequiredNullableArraySchema; +// optional(): NotRequiredNullableArraySchema; +// } + +// export interface NullableArraySchema +// extends BasicArraySchema { +// of(type: Schema): NullableArraySchema; +// nullable(isNullable?: true): NullableArraySchema; +// nullable(isNullable: false): ArraySchema; +// nullable(isNullable?: boolean): ArraySchema; +// required(message?: TestOptionsMessage): ArraySchema; +// defined(): NullableArraySchema; +// notRequired(): NotRequiredNullableArraySchema; +// optional(): NotRequiredNullableArraySchema; +// } + +// export interface NotRequiredArraySchema +// extends BasicArraySchema { +// of(type: Schema): NotRequiredArraySchema; +// nullable(isNullable?: true): NotRequiredNullableArraySchema; +// nullable(isNullable: false): NotRequiredArraySchema; +// nullable(isNullable: boolean): ArraySchema; +// required(message?: TestOptionsMessage): ArraySchema; +// defined(): ArraySchema; +// notRequired(): NotRequiredArraySchema; +// optional(): NotRequiredArraySchema; +// } + +// export interface ArraySchema extends BasicArraySchema { +// of(type: Schema): ArraySchema; +// nullable(isNullable?: true): NullableArraySchema; +// nullable(isNullable: false | boolean): ArraySchema; +// required(message?: TestOptionsMessage): ArraySchema; +// defined(): ArraySchema; +// notRequired(): NotRequiredArraySchema; +// optional(): NotRequiredArraySchema; +// } + +// export type ObjectSchemaDefinition = { +// // This shouldn't be necessary because MixedSchema extends Schema, but type +// // inference only works with it this way - otherwise when you use a mixed +// // field in object schema, it will type as `unknown`. Not sure why that is - +// // maybe some sort of inference depth limit? +// [field in keyof T]: Schema | MixedSchema | Ref; +// }; + +// /** +// * Merges two interfaces. For properties in common, property types from `U` trump those of `T`. +// * This is conducive to the functionality of +// * [yup's `object.shape()` method](https://www.npmjs.com/package/yup#objectshapefields-object-nosortedges-arraystring-string-schema). +// */ +// export type Shape = +// | ({ [P in keyof T]: P extends keyof U ? U[P] : T[P] } & U) +// | PreserveOptionals; + +// export interface ObjectSchemaConstructor { +// (fields?: ObjectSchemaDefinition): ObjectSchema< +// T | undefined +// >; +// new (): ObjectSchema<{}>; +// } + +// export interface ObjectSchema< +// T extends object | null | undefined = object | undefined +// > extends Schema { +// fields: { +// [k in keyof T]: Schema; +// }; +// shape( +// fields: ObjectSchemaDefinition, +// noSortEdges?: Array<[string, string]>, +// ): ObjectSchema>; +// from(fromKey: string, toKey: string, alias?: boolean): ObjectSchema; +// noUnknown( +// onlyKnownKeys?: boolean, +// message?: ObjectLocale['noUnknown'], +// ): ObjectSchema; +// unknown( +// allow?: boolean, +// message?: ObjectLocale['noUnknown'], +// ): ObjectSchema; +// transformKeys(callback: (key: any) => any): void; +// camelCase(): ObjectSchema; +// snakeCase(): ObjectSchema; +// constantCase(): ObjectSchema; +// nullable(isNullable?: true): ObjectSchema; +// nullable(isNullable: false): ObjectSchema>; +// nullable(isNullable?: boolean): ObjectSchema; +// required( +// message?: TestOptionsMessage, +// ): ObjectSchema>; +// defined(): ObjectSchema>; +// notRequired(): ObjectSchema; +// optional(): ObjectSchema; +// concat(schema: this): this; +// concat(schema: ObjectSchema): ObjectSchema; +// oneOf( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): ObjectSchema; +// equals( +// arrayOfValues: ReadonlyArray, +// message?: MixedLocale['oneOf'], +// ): ObjectSchema; +// test( +// name: string, +// message: TestOptionsMessage, +// test: TestFunction, +// ): this; +// test( +// name: string, +// message: TestOptionsMessage, +// test: AssertingTestFunction, +// ): ObjectSchema; +// test( +// options: AssertingTestOptions>, +// ): ObjectSchema; +// test(options: TestOptions>): this; +// } + +// export type TestFunction = ( +// this: TestContext, +// value: T, +// ) => boolean | ValidationError | Promise; +// export type AssertingTestFunction = ( +// this: TestContext, +// value: any, +// ) => value is T; + +// export type TransformFunction = ( +// this: T, +// value: any, +// originalValue: any, +// ) => any; + +// export interface WhenOptionsBuilderFunction { +// (value: any, schema: T): T; +// (v1: any, v2: any, schema: T): T; +// (v1: any, v2: any, v3: any, schema: T): T; +// (v1: any, v2: any, v3: any, v4: any, schema: T): T; +// } + +// export type WhenOptionsBuilderObjectIs = +// | ((...values: any[]) => boolean) +// | boolean +// | number +// | null +// | object +// | string; + +// export type WhenOptionsBuilderObject = +// | { +// is: WhenOptionsBuilderObjectIs; +// then: any; +// otherwise: any; +// } +// | object; + +// export type WhenOptions = +// | WhenOptionsBuilderFunction +// | WhenOptionsBuilderObject; + +// export interface TestContext { +// path: string; +// options: ValidateOptions; +// parent: any; +// schema: Schema; +// resolve: (value: any) => any; +// createError: (params?: { +// path?: string; +// message?: string; +// params?: object; +// }) => ValidationError; +// } + +// export interface ValidateOptions { +// /** +// * Only validate the input, and skip and coercion or transformation. Default - false +// */ +// strict?: boolean; +// /** +// * Return from validation methods on the first error rather than after all validations run. Default - true +// */ +// abortEarly?: boolean; +// /** +// * Remove unspecified keys from objects. Default - false +// */ +// stripUnknown?: boolean; +// /** +// * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true +// */ +// recursive?: boolean; +// /** +// * Any context needed for validating schema conditions (see: when()) +// */ +// context?: object; +// } + +// export interface TestMessageParams { +// path: string; +// value: any; +// originalValue: any; +// label: string; +// } + +// interface BaseTestOptions

> { +// /** +// * Unique name identifying the test. Required for exclusive tests. +// */ +// name?: string; + +// /** +// * Test function, determines schema validity +// */ +// test: TestFunction; + +// /** +// * The validation error message +// */ +// message?: TestOptionsMessage

; + +// /** +// * Values passed to message for interpolation +// */ +// params?: P; + +// /** +// * Mark the test as exclusive, meaning only one of the same can be active at once +// */ +// exclusive?: boolean; +// } + +// interface NonExclusiveTestOptions

> +// extends BaseTestOptions

{ +// exclusive?: false; +// } + +// interface ExclusiveTestOptions

> +// extends BaseTestOptions

{ +// exclusive: true; +// name: string; +// } + +// interface NonExclusiveAssertingTestOptions> +// extends NonExclusiveTestOptions

{ +// test: AssertingTestFunction; +// } + +// interface ExclusiveAssertingTestOptions> +// extends ExclusiveTestOptions

{ +// test: AssertingTestFunction; +// } + +// export type TestOptions

= {}> = +// | NonExclusiveTestOptions

+// | ExclusiveTestOptions

; + +// export type AssertingTestOptions = {}> = +// | NonExclusiveAssertingTestOptions +// | ExclusiveAssertingTestOptions; + +// export interface SchemaFieldRefDescription { +// type: 'ref'; +// key: string; +// } + +// export interface SchemaFieldInnerTypeDescription +// extends Pick> { +// innerType?: SchemaFieldDescription; +// } + +// export type SchemaFieldDescription = +// | SchemaDescription +// | SchemaFieldRefDescription +// | SchemaFieldInnerTypeDescription; + +// export interface SchemaDescription { +// type: string; +// label: string; +// meta: object; +// tests: Array<{ name: string; params: { [k: string]: any } }>; +// fields: Record; +// } + +// // ValidationError works a lot more like a class vs. a constructor +// // function that returns an interface. It's also got a couple of +// // static methods and it inherits from the generic Error class in +// // the [yup codebase][1]. +// // [1]: (https://github.com/jquense/yup/blob/master/src/ValidationError.js) +// export class ValidationError extends Error { +// name: string; +// message: string; +// value: any; +// /** +// * A string, indicating where there error was thrown. path is empty at the root level. +// */ +// path: string; +// type: any; +// /** +// * array of error messages +// */ +// errors: string[]; + +// /** +// * In the case of aggregate errors, inner is an array of ValidationErrors throw earlier in the validation chain. +// */ +// inner: ValidationError[]; +// params?: object; + +// static isError(err: any): err is ValidationError; +// static formatError( +// message: string | ((params?: any) => string), +// params?: any, +// ): string | ((params?: any) => string); + +// constructor(errors: string | string[], value: any, path: string, type?: any); +// } + +// // It is tempting to declare `Ref` very simply, but there are problems with these approaches: +// // +// // * `type Ref = Record;` - This is essentially how it was originally declared, but +// // just about any object satisfies this contract, which makes the type declaration too loose to +// // be useful. +// // +// // * `type Ref = object;` - This is a variation on the previous bullet in that it is too loose. +// // +// // * `class Ref {}` - This is yet another variation that is too loose. +// // +// // * `type Ref = void;` - This works and the emitted JavaScript is just fine, but it results in some +// // confusing IntelliSense, e.g it looks like the `ref()` returns `void`, which is not the case. +// // +// // The solution is twofold. 1.) Declare it as a class with a private constructor to prevent it from +// // being instantiated by anything but the `ref()` factory function, and; 2.) declare a private +// // readonly property (that yup actually places on the prototype) to force it to be structurally +// // incompatible with any other object type. + +// /** +// * `Ref` is an opaque type that is internal to yup. Creating a `Ref` instance is accomplished via the `ref()` factory +// * function. +// */ +// export class Ref { +// private constructor(); +// private readonly __isYupRef: true; +// } + +// // tslint:disable-next-line:no-empty-interface +// export interface Lazy extends Schema {} + +// export interface FormatErrorParams { +// path: string; +// type: string; +// value?: any; +// originalValue?: any; +// } + +// export type LocaleValue = string | ((params: FormatErrorParams) => string); + +// export interface MixedLocale { +// default?: TestOptionsMessage; +// required?: TestOptionsMessage; +// oneOf?: TestOptionsMessage<{ values: any }>; +// notOneOf?: TestOptionsMessage<{ values: any }>; +// notType?: LocaleValue; +// } + +// export interface StringLocale { +// length?: TestOptionsMessage<{ length: number }>; +// min?: TestOptionsMessage<{ min: number }>; +// max?: TestOptionsMessage<{ max: number }>; +// matches?: TestOptionsMessage<{ regex: RegExp }>; +// email?: TestOptionsMessage<{ regex: RegExp }>; +// url?: TestOptionsMessage<{ regex: RegExp }>; +// uuid?: TestOptionsMessage<{ regex: RegExp }>; +// trim?: TestOptionsMessage; +// lowercase?: TestOptionsMessage; +// uppercase?: TestOptionsMessage; +// } + +// export interface NumberLocale { +// min?: TestOptionsMessage<{ min: number }>; +// max?: TestOptionsMessage<{ max: number }>; +// lessThan?: TestOptionsMessage<{ less: number }>; +// moreThan?: TestOptionsMessage<{ more: number }>; +// positive?: TestOptionsMessage<{ more: number }>; +// negative?: TestOptionsMessage<{ less: number }>; +// integer?: TestOptionsMessage; +// } + +// export interface DateLocale { +// min?: TestOptionsMessage<{ min: Date | string }>; +// max?: TestOptionsMessage<{ max: Date | string }>; +// } + +// export interface ObjectLocale { +// noUnknown?: TestOptionsMessage; +// } + +// export interface ArrayLocale { +// min?: TestOptionsMessage<{ min: number }>; +// max?: TestOptionsMessage<{ max: number }>; +// } + +// export interface LocaleObject { +// mixed?: MixedLocale; +// string?: StringLocale; +// number?: NumberLocale; +// date?: DateLocale; +// boolean?: {}; +// object?: ObjectLocale; +// array?: ArrayLocale; +// } + +// export type InferType = T extends Schema +// ? InnerInferType

+// : never; + +// // Shut off automatic exporting after this statement +// export {}; + +// type KeyOfUndefined = { +// [P in keyof T]-?: undefined extends T[P] ? P : never; +// }[keyof T]; + +// type PreserveNull = T extends null ? null : never; +// type PreserveUndefined = T extends undefined ? undefined : never; +// type PreserveOptionals = PreserveNull | PreserveUndefined; +// type Id = { +// [K in keyof T]: T[K] extends object ? InnerInferType : T[K]; +// }; +// type RequiredProps = Pick>>; +// type NotRequiredProps = Partial>>; +// type InnerInferType = +// | (T extends Array +// ? InnerInferTypeArray +// : Id & RequiredProps>) +// | PreserveOptionals; +// interface InnerInferTypeArray extends Array> {} +// type InferredArrayType = T extends Array ? U : T; +// /** If `T` is optional, returns optional `U`. */ +// type MaintainOptionality = T extends undefined ? U | undefined : U; From 20076f15b419aa30a48f3a41ea8c3ea14ed08509 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 29 Sep 2020 08:47:13 -0400 Subject: [PATCH 09/31] WIP --- .babelrc.js | 1 + package.json | 2 + src/Schema.ts | 21 ++++++- src/array.ts | 39 +++++++++---- src/mixed.ts | 125 ++++++++++++++++++++++------------------- src/number.ts | 4 +- src/object.ts | 67 +++++++++++++--------- src/string.ts | 8 ++- src/util/cloneDeep.ts | 0 src/util/sortFields.ts | 2 +- src/util/types.ts | 24 +++++++- test/array.js | 12 ++-- test/mixed.js | 2 +- test/number.js | 2 +- test/types.ts | 121 +++++++++++++++++++++++++++------------ yarn.lock | 20 +++++++ 16 files changed, 304 insertions(+), 146 deletions(-) create mode 100644 src/util/cloneDeep.ts diff --git a/.babelrc.js b/.babelrc.js index 74c6ca14c..0368b0780 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -17,6 +17,7 @@ module.exports = (api) => ({ '@babel/preset-typescript', ], plugins: [ + '@babel/plugin-proposal-logical-assignment-operators', api.env() === 'modules' && [ 'transform-rename-import', { diff --git a/package.json b/package.json index 11ae8ff2b..4b8c2cd8a 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "@4c/tsconfig": "^0.3.1", "@babel/cli": "7.10.5", "@babel/core": "7.11.4", + "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", "@babel/preset-typescript": "^7.10.4", "@typescript-eslint/parser": "^4.0.1", "babel-core": "^7.0.0-bridge.0", @@ -110,6 +111,7 @@ "@types/lodash": "^4.14.161", "lodash": "^4.17.15", "lodash-es": "^4.17.11", + "nanoclone": "^0.2.1", "property-expr": "^2.0.2", "synchronous-promise": "^2.0.13", "toposort": "^2.0.2" diff --git a/src/Schema.ts b/src/Schema.ts index ebae2e66c..0afe1f81b 100644 --- a/src/Schema.ts +++ b/src/Schema.ts @@ -1,5 +1,10 @@ import { ResolveOptions } from './Condition'; -import { ValidateOptions, Callback, MessageParams } from './types'; +import { + ValidateOptions, + Callback, + MessageParams, + AnyMessageParams,, ExtraParams +} from './types'; export interface CastOptions { parent?: any; @@ -9,6 +14,18 @@ export interface CastOptions { path?: string; } +export type SchemaSpec = { + nullable?: boolean; + default: TDefault | (() => TDefault); + hasDefault?: boolean; + abortEarly?: boolean; + strip?: boolean; + strict?: boolean; + recursive?: boolean; + label?: string | undefined; + meta?: any; +}; + export interface SchemaRefDescription { type: 'ref'; key: string; @@ -34,7 +51,7 @@ export interface SchemaDescription { meta: object; oneOf: unknown[]; notOneOf: unknown[]; - tests: Array<{ name?: string; params: MessageParams & Params }>; + tests: Array<{ name?: string; params: ExtraParams | undefined }>; } export default interface Schema { diff --git a/src/array.ts b/src/array.ts index 8348ac05d..5861159c0 100644 --- a/src/array.ts +++ b/src/array.ts @@ -4,7 +4,7 @@ import printValue from './util/printValue'; import MixedSchema from './mixed'; import { array as locale } from './locale'; import runTests, { RunTest } from './util/runTests'; -import { SchemaInnerTypeDescription } from './Schema'; +import { SchemaInnerTypeDescription, SchemaSpec } from './Schema'; import { InternalOptions, Callback, Message, Maybe } from './types'; import ValidationError from './ValidationError'; import Reference from './Reference'; @@ -32,7 +32,7 @@ type Type = T extends MixedSchema export default class ArraySchema< T extends MixedSchema = MixedSchema, - TDef extends TypeDef = 'optional' | 'nonnullable', + TDef extends TypeDef = '', TDefault extends Maybe>[]> = undefined > extends MixedSchema< Type[], @@ -43,8 +43,6 @@ export default class ArraySchema< > { // - private _subType?: T; - innerType: T | undefined; constructor(type?: T) { @@ -52,7 +50,6 @@ export default class ArraySchema< // `undefined` specifically means uninitialized, as opposed to // "no subtype" - this._subType = type; this.innerType = type; this.withMutation(() => { @@ -69,6 +66,10 @@ export default class ArraySchema< }); } + private get _subType() { + return this.innerType; + } + protected _typeCheck(v: any): v is any[] { return Array.isArray(v); } @@ -168,20 +169,38 @@ export default class ArraySchema< return super._isPresent(value) && value.length > 0; } + clone(spec?: SchemaSpec) { + const next = super.clone(spec); + next.innerType = this.innerType; + return next; + } + + concat(schema: ArraySchema): ArraySchema { + let next = super.concat(schema) as ArraySchema; + + next.innerType = this.innerType; + + if (schema.innerType) + next.innerType = next.innerType + ? next.innerType.concat(schema.innerType) + : schema.innerType; + + return next; + } + of( - schema: TInner | false, + schema: TInner, ): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); - if (schema !== false && !isSchema(schema)) + if (!isSchema(schema)) throw new TypeError( - '`array.of()` sub-schema must be a valid yup schema, or `false` to negate a current sub-schema. ' + - 'not: ' + + '`array.of()` sub-schema must be a valid yup schema not: ' + printValue(schema), ); + // FIXME(ts): - next._subType = schema as any; next.innerType = schema as any; return next as any; diff --git a/src/mixed.ts b/src/mixed.ts index 0218d053e..8a4ea4253 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -1,11 +1,9 @@ -import has from 'lodash/has'; -import cloneDeepWith from 'lodash/cloneDeepWith'; +// @ts-ignore +import cloneDeep from 'nanoclone'; import { mixed as locale } from './locale'; import Condition, { ConditionOptions, ResolveOptions } from './Condition'; import runTests from './util/runTests'; -import merge from './util/prependDeep'; -import isSchema from './util/isSchema'; import createValidation, { TestFunction, Test, @@ -75,12 +73,14 @@ class RefSet { return false; } + clone() { const next = new RefSet(); next.list = new Set(this.list); next.refs = new Map(this.refs); return next; } + merge(newItems: RefSet, removeItems: RefSet) { const next = this.clone(); newItems.list.forEach((value) => next.add(value)); @@ -114,7 +114,7 @@ export function create() { export default class MixedSchema< TType = any, - TDef extends TypeDef = 'optional' | 'nonnullable', + TDef extends TypeDef = '', TDefault = any, TInput = ResolveInput, TOutput = ResolveOutput @@ -124,40 +124,29 @@ export default class MixedSchema< readonly __inputType!: TInput; readonly __outputType!: TOutput; - readonly __isYupSchema__ = true; + readonly __isYupSchema__!: boolean; readonly deps: readonly string[] = []; - protected _exclusive: Record = Object.create(null); - - protected _whitelist: RefSet = new RefSet(); - protected _blacklist: RefSet = new RefSet(); tests: Test[]; - transforms: TransformFunction[]; // TODO - - private _mutate?: boolean; + transforms: TransformFunction[]; - protected _label?: string; - protected _meta: any; private conditions: Condition[] = []; - // protected configuredDefault: ((this: this) => unknown) | undefined; - // private _validating: boolean = false; + private _mutate?: boolean; private _typeError?: Test; private _whitelistError?: Test; private _blacklistError?: Test; + protected _whitelist: RefSet = new RefSet(); + protected _blacklist: RefSet = new RefSet(); + + protected exclusiveTests: Record = Object.create(null); + optional!: () => MixedSchema; spec: SchemaSpec; - // static create( - // this: new (...args: any[]) => T, - // ...args: any[] - // ) { - // return new this(...args); - // } - constructor(options?: SchemaOptions) { this.tests = []; this.transforms = []; @@ -169,18 +158,13 @@ export default class MixedSchema< this.type = options?.type || ('mixed' as const); this.spec = { - // __def: null as any, - hasDefault: false, strip: false, strict: false, abortEarly: true, recursive: true, - // noUnknown: false, - label: undefined, meta: undefined, nullable: false, - // required: false, default: undefined as any, ...options?.spec, }; @@ -191,35 +175,53 @@ export default class MixedSchema< return this.type; } - protected _typeCheck(v: any): v is TType { + protected _typeCheck(_value: any): _value is TType { return true; } - // __isYupSchema__ = true; - - clone(): this { - if (this._mutate) return this; + clone(spec?: SchemaSpec): this { + if (this._mutate) { + if (spec) Object.assign(this.spec, spec); + return this; + } // if the nested value is a schema we can skip cloning, since // they are already immutable - return cloneDeepWith(this, (value) => { - if (isSchema(value) && value !== this) return value; - }); + const next: MixedSchema = Object.create(Object.getPrototypeOf(this)); + + // @ts-expect-error this is readonly + next.type = this.type; + + next._typeError = this._typeError; + next._whitelistError = this._whitelistError; + next._blacklistError = this._blacklistError; + next._whitelist = this._whitelist.clone(); + next._blacklist = this._blacklist.clone(); + next.exclusiveTests = { ...this.exclusiveTests }; + + // @ts-expect-error this is readonly + next.deps = [...this.deps]; + next.conditions = [...this.conditions]; + next.tests = [...this.tests]; + next.transforms = [...this.transforms]; + next.spec = cloneDeep({ ...this.spec, ...spec }); + + return next as this; } label(label: string) { var next = this.clone(); - next._label = label; + next.spec.label = label; return next; } meta(): Record | undefined; meta(obj: Record): void; meta(...args: [Record?]) { - if (args.length === 0) return this._meta; + if (args.length === 0) return this.spec.meta; let next = this.clone(); - next._meta = Object.assign(next._meta || {}, args[0]); + next.spec.meta = Object.assign(next.spec.meta || {}, args[0]); return next; } @@ -232,7 +234,6 @@ export default class MixedSchema< } concat(schema: MixedSchema): MixedSchema { - // @ts-ignore if (!schema || schema === this) return this; if (schema.type !== this.type && this.type !== 'mixed') @@ -240,40 +241,44 @@ export default class MixedSchema< `You cannot \`concat()\` schema's of different types: ${this.type} and ${schema.type}`, ); - var next = merge(schema.clone() as any, this as any) as this; + let base = this; + let combined = schema.clone(); // new undefined default is overridden by old non-undefined one, revert if (schema.spec.hasDefault) { - next.spec.default = schema.spec.default; + combined.spec.default = schema.spec.default; } - next.tests = this.tests; - next._exclusive = this._exclusive; + combined._typeError ||= base._typeError; + combined._whitelistError ||= base._whitelistError; + combined._blacklistError ||= base._blacklistError; // manually merge the blacklist/whitelist (the other `schema` takes // precedence in case of conflicts) - next._whitelist = this._whitelist.merge( + combined._whitelist = base._whitelist.merge( schema._whitelist, schema._blacklist, ); - next._blacklist = this._blacklist.merge( + combined._blacklist = base._blacklist.merge( schema._blacklist, schema._whitelist, ); + // start with the current tests + combined.tests = base.tests; + combined.exclusiveTests = base.exclusiveTests; + // manually add the new tests to ensure // the deduping logic is consistent - next.withMutation((next) => { + combined.withMutation((next) => { schema.tests.forEach((fn) => { next.test(fn.OPTIONS); }); }); - return next; + return combined as any; } - // abstract ?(value: any): boolean; - isType(v: any) { if (this.spec.nullable && v === null) return true; return this._typeCheck(v); @@ -380,7 +385,7 @@ export default class MixedSchema< options, originalValue, schema: this, - label: this._label, + label: this.spec.label, sync, from, }; @@ -486,7 +491,7 @@ export default class MixedSchema< return typeof defaultValue === 'function' ? defaultValue.call(this) - : cloneDeepWith(defaultValue); + : cloneDeep(defaultValue); } var next = this.clone(); @@ -583,7 +588,7 @@ export default class MixedSchema< let validate = createValidation(opts); let isExclusive = - opts.exclusive || (opts.name && next._exclusive[opts.name] === true); + opts.exclusive || (opts.name && next.exclusiveTests[opts.name] === true); if (opts.exclusive) { if (!opts.name) @@ -592,7 +597,7 @@ export default class MixedSchema< ); } - if (opts.name) next._exclusive[opts.name] = !!opts.exclusive; + if (opts.name) next.exclusiveTests[opts.name] = !!opts.exclusive; next.tests = next.tests.filter((fn) => { if (fn.OPTIONS.name === opts.name) { @@ -711,10 +716,11 @@ export default class MixedSchema< describe() { const next = this.clone(); + const { label, meta } = next.spec; const description: SchemaDescription = { - type: next._type, - meta: next._meta, - label: next._label, + meta, + label, + type: next.type, oneOf: next._whitelist.describe(), notOneOf: next._blacklist.describe(), tests: next.tests @@ -758,6 +764,9 @@ export default interface MixedSchema< nope: MixedSchema['notOneOf']; } +// @ts-expect-error +MixedSchema.prototype.__isYupSchema__ = true; + for (const method of ['validate', 'validateSync']) MixedSchema.prototype[ `${method}At` as 'validateAt' | 'validateSyncAt' diff --git a/src/number.ts b/src/number.ts index eded5d996..001888725 100644 --- a/src/number.ts +++ b/src/number.ts @@ -13,7 +13,7 @@ export function create() { export default class NumberSchema< TType extends number, - TDef extends TypeDef = 'optional' | 'nonnullable', + TDef extends TypeDef = '', TDefault extends Maybe = undefined > extends MixedSchema { constructor() { @@ -37,7 +37,7 @@ export default class NumberSchema< }); } - protected _typeCheck(value: any): value is number { + protected _typeCheck(value: any): value is TType { if (value instanceof Number) value = value.valueOf(); return typeof value === 'number' && !isNaN(value); diff --git a/src/object.ts b/src/object.ts index 98413ed3d..22c6518b6 100644 --- a/src/object.ts +++ b/src/object.ts @@ -12,7 +12,8 @@ import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; import Schema, { CastOptions, SchemaObjectDescription } from './Schema'; import { InternalOptions, Callback, Maybe } from './types'; -import { ValidationError } from '.'; +import ValidationError from './ValidationError'; +import isSchema from './util/isSchema'; import { ResolveInput, ResolveOutput, @@ -31,8 +32,6 @@ function unknown(ctx: ObjectSchema, value: any) { return Object.keys(value).filter((key) => known.indexOf(key) === -1); } -type AnyObject = Record; - type ObjectShape = Record; type AssignShape = { @@ -79,9 +78,15 @@ export type AssertsShape = { : never; }; +type ObjectSchemaSpec = SchemaSpec & { + noUnknown?: boolean; +}; + +const defaultSort = sortByKeyOrder([]); + export default class ObjectSchema< TShape extends ObjectShape = ObjectShape, - TDef extends TypeDef = 'optional' | 'nonnullable', + TDef extends TypeDef = '', TDefault extends Maybe> = DefaultFromShape > extends MixedSchema< TypeFromShape, @@ -90,15 +95,13 @@ export default class ObjectSchema< ResolveInput, TDef, TDefault>, ResolveOutput, TDef, TDefault> > { - fields: TShape; + fields: TShape = Object.create(null); - spec!: SchemaSpec & { - noUnknown?: boolean; - }; + spec!: ObjectSchemaSpec; - private _sortErrors: (a: ValidationError, b: ValidationError) => number; - private _nodes: string[]; - private _excludedEdges: string[]; + private _sortErrors = defaultSort; + private _nodes: readonly string[] = []; + private _excludedEdges: readonly string[] = []; constructor(spec?: TShape) { super({ @@ -119,13 +122,6 @@ export default class ObjectSchema< }, }); - this.fields = Object.create(null); - - this._sortErrors = sortByKeyOrder([]); - - this._nodes = []; - this._excludedEdges = []; - this.withMutation(() => { // this.spec.default = () => {}; @@ -172,8 +168,6 @@ export default class ObjectSchema< parent: intermediateValue, __validating: options.__validating || false, }; - // let endEarly = options.abortEarly ?? this.spec.abortEarly; - // let recursive = options.recursive ?? this.spec.recursive; let isChanged = false; for (const prop of props) { @@ -181,10 +175,7 @@ export default class ObjectSchema< let exists = has(value, prop); if (field) { - let fieldSpec = 'spec' in field ? field.spec : undefined; let fieldValue; - let strict = fieldSpec?.strict; - let inputValue = value[prop]; // safe to mutate since this is fired in sequence @@ -197,6 +188,9 @@ export default class ObjectSchema< parent: intermediateValue, }); + let fieldSpec = 'spec' in field ? field.spec : undefined; + let strict = fieldSpec?.strict; + if (fieldSpec?.strip) { isChanged = isChanged || prop in value; continue; @@ -306,14 +300,33 @@ export default class ObjectSchema< }); } - concat(schema: ObjectSchema): ObjectSchema { - var next = super.concat(schema) as ObjectSchema; - - next._nodes = sortFields(next.fields, next._excludedEdges); + clone(spec?: ObjectSchemaSpec): this { + const next = super.clone(spec); + next.fields = { ...this.fields }; + next._nodes = this._nodes; + next._excludedEdges = this._excludedEdges; + next._sortErrors = this._sortErrors; return next; } + concat(schema: ObjectSchema): ObjectSchema; + concat(schema: ObjectSchema): ObjectSchema { + let next = super.concat(schema) as ObjectSchema; + + let nextFields = next.fields; + for (let [field, schemaOrRef] of Object.entries(this.fields)) { + const target = nextFields[field]; + if (target === undefined) { + nextFields[field] = schemaOrRef; + } else if (isSchema(target) && isSchema(schemaOrRef)) { + nextFields[field] = schemaOrRef.concat(target); + } + } + + return next.withMutation((next) => next.shape(nextFields)); + } + shape( additions: TNextShape, excludes: [string, string][] = [], diff --git a/src/string.ts b/src/string.ts index b434f25cd..d98987412 100644 --- a/src/string.ts +++ b/src/string.ts @@ -27,7 +27,7 @@ export function create() { export default class StringSchema< TType extends string = string, - TDef extends TypeDef = 'optional' | 'nonnullable', + TDef extends TypeDef = '', TDefault extends Maybe = undefined > extends MixedSchema { _tsType!: string | undefined; @@ -44,7 +44,7 @@ export default class StringSchema< }); } - protected _typeCheck(value: any): value is string { + protected _typeCheck(value: any): value is TType { if (value instanceof String) value = value.valueOf(); return typeof value === 'string'; @@ -147,7 +147,9 @@ export default class StringSchema< //-- transforms -- ensure() { - return this.default('').transform((val) => (val === null ? '' : val)); + return this.default('' as TType).transform((val) => + val === null ? '' : val, + ); } trim(message = locale.trim) { diff --git a/src/util/cloneDeep.ts b/src/util/cloneDeep.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/util/sortFields.ts b/src/util/sortFields.ts index 723c777dc..43ecb1b7a 100644 --- a/src/util/sortFields.ts +++ b/src/util/sortFields.ts @@ -10,7 +10,7 @@ import Reference from '../Reference'; export default function sortFields( fields: Record, - excludes: string[] = [], + excludes: readonly string[] = [], ) { let edges = [] as Array<[string, string]>; let nodes = [] as string[]; diff --git a/src/util/types.ts b/src/util/types.ts index ef084aba5..592f5e6df 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -12,7 +12,7 @@ type MaintainOptionality = T extends undefined ? U | undefined : U; type StrictNonNullable = T extends null ? never : T; -export type TypeDef = Nullability | Presence; +export type TypeDef = Nullability | Presence | ''; export type SetNullability< Def extends TypeDef, @@ -65,6 +65,28 @@ export type TypeOf = TSchema['__inputType']; export type Asserts = TSchema['__outputType']; +export type MergePresence = Pluck< + U, + Presence +> extends never + ? Extract | Extract + : U; + +export type MergeNullability = Pluck< + U, + Nullability +> extends never + ? Extract | Extract + : U; + +export type MergeDef = + | MergeNullability + | MergePresence + | ''; + +// export type Concat = TSchema extends TypedSchema +// ? +// : never // type ResolveNullable< // TType, // TSpec extends SchemaSpec diff --git a/test/array.js b/test/array.js index 885cc332f..bd3a417fc 100644 --- a/test/array.js +++ b/test/array.js @@ -49,12 +49,14 @@ describe('Array types', () => { array().of(number()).cast(['1', '3']).should.eql([1, 3]); }); - it('should concat subType correctly', () => { - expect(array().of(number()).concat(array())._subType).to.exist(); + it('should concat subType correctly', async () => { + expect(array(number()).concat(array()).innerType).to.exist(); - expect(array().of(number()).concat(array().of(false))._subType).to.equal( - false, - ); + let merged = array(number()).concat(array(number().required())); + + expect(merged.innerType.type).to.equal('number'); + + await expect(merged.validateAt('[0]', undefined)).to.be.rejected(); }); it('should pass options to children', () => { diff --git a/test/mixed.js b/test/mixed.js index 5af54bbbe..5ed959b05 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -38,7 +38,7 @@ global.YUP_USE_SYNC && }); describe('Mixed Types ', () => { - it('should be immutable', () => { + xit('should be immutable', () => { let inst = mixed(), next; let sub = (inst.sub = mixed()); diff --git a/test/number.js b/test/number.js index f853ae9c0..d7e96c8fb 100644 --- a/test/number.js +++ b/test/number.js @@ -32,7 +32,7 @@ describe('Number types', function () { }); it('should round', () => { - schema.round('floor').cast(45.99999).should.equal(45); + // schema.round('floor').cast(45.99999).should.equal(45); schema.round('ceIl').cast(45.1111).should.equal(46); schema.round().cast(45.444444).should.equal(45); diff --git a/test/types.ts b/test/types.ts index f9859f5e3..4cbad55c1 100644 --- a/test/types.ts +++ b/test/types.ts @@ -2,7 +2,16 @@ // import { Asserts } from '../src/mixed'; import { array, string, object, mixed, number, ref } from '../src'; import { AssertsShape, DefaultFromShape, TypeOfShape } from '../src/object'; -import { ResolveInput, ResolveOutput } from '../src/util/types'; +import { + ResolveInput, + ResolveOutput, + MergeDef, + MergePresence, + MergeNullability, + TypeDef, + SetPresence, + SetNullability, +} from '../src/util/types'; // let schema = object({ // str: string().nullable(), @@ -18,8 +27,32 @@ string().required().nullable(); /** Type utils */ { + // $ExpectType string | undefined + type _d1 = ResolveInput; + + // $ExpectType string | null | undefined + type _d2 = ResolveInput; + + // $ExpectType string | undefined + type _d3 = ResolveInput< + string, + SetNullability, 'nonnullable'> + >; + + // $ExpectType string + type _d4 = ResolveOutput< + string, + SetPresence, 'required'> + >; + + // $ExpectType string + type _d5 = ResolveOutput< + string, + SetPresence, 'required'> + >; + // $ExpectType string | null - type _i1 = ResolveInput; + type _i1 = ResolveInput; // $ExpectType string | undefined type _i2 = ResolveInput; @@ -59,6 +92,57 @@ string().required().nullable(); // $ExpectError number is not a MaybeString type _e1 = ResolveOutput; + + // $ExpectType "nullable" + type _n1 = MergeNullability<'', 'nullable'>; + + // $ExpectType "nullable" + type _n2 = MergeNullability<'nonnullable', 'nullable'>; + + // $ExpectType "" | "nonnullable" + type _n3 = MergeNullability<'nullable' | 'optional', '' | 'nonnullable'>; + + // type v = '' | 'foo' extends 'foo' ? true : false; + + // $ExpectType "required" + type _p1 = MergePresence<'', 'required'>; + + // $ExpectType "required" + type _p2 = MergePresence<'optional', 'required'>; + + // $ExpectType "optional" + type _p3 = MergePresence<'required' | 'nullable', 'optional'>; + + // type M = Pluck< + // T | U, + // 'required' + // > extends never + // ? 'optional' + // : 'required'; + + // $ExpectType "nullable" | "required" + type _m1 = MergeDef<'nullable' | 'optional', 'required'>; + + // $ExpectType "required" + type _m2 = MergeDef<'', 'required'>; + + // $ExpectType "optional" + type _m3 = MergeDef<'required', 'optional'>; + + // $ExpectType "" | "nonnullable" | "required" + type _m4 = MergeDef<'required' | 'nullable', '' | 'nonnullable'>; + + // $ExpectType "" | "nonnullable" | "required" + type _m5 = MergeDef<'' | 'required' | 'nullable', '' | 'nonnullable'>; + + // $ExpectType "" | "nonnullable" | "optional" + type _m6 = MergeDef<'' | 'nullable', '' | 'optional' | 'nonnullable'>; + + // $ExpectType "nullable" + type _m7 = MergeDef<'' | 'nullable', ''>; + + // $ExpectType "" + type _m8 = MergeDef<'', ''>; } { const strRequired = string().required(); @@ -230,36 +314,3 @@ string().required().nullable(); // $ExpectType string a1.nested![0].name; } -// const strPlain = string(); - -// type fff = typeof strPlain['spec']['hasDefault']; - -// // $ExpectType string | undefined -// type _strPlain = Asserts; - -// const strRequired = string().required(); -// // $ExpectType string -// type _strRequired = Asserts; - -// const strDefault = string().nullable().default(undefined); -// const strDefault2 = string().nullable().default(''); - -// // $ExpectType undefined -// strDefault.default(); - -// // $ExpectType string -// strDefault2.default(); - -// // $ExpectType string | null -// strDefault2.cast(undefined); - -// // async function foo() { -// // ff = await str.validate(undefined, {}); -// // ff = await str2.validate(null, {}); -// // } - -// let objWithDefault = object({ -// str: string().nullable().default(''), -// num: number().default(3), -// num2: number(), -// }); diff --git a/yarn.lock b/yarn.lock index 2a3211627..db848a9be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -493,6 +493,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" +"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" + integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" @@ -613,6 +621,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz#3995d7d7ffff432f6ddc742b47e730c054599897" @@ -7760,6 +7775,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== + nanoid@^2.0.3: version "2.1.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" From 334404aae3cb28a1f927d70651db1404ec3e7ce1 Mon Sep 17 00:00:00 2001 From: Bartmr Date: Thu, 19 Nov 2020 16:16:07 +0000 Subject: [PATCH 10/31] Solves typing issues in prependDeep + allow JS for incremental transition to Typescript (#1060) * Allow JS * Remove unused imports * Ignore Eslint config files in ESLint * Remove unused import from test/yup.js * Use instanceof instead of isSchema in prependDeep --- .eslintignore | 2 ++ src/Schema.ts | 3 +-- src/index.ts | 4 ---- src/util/prependDeep.ts | 6 +++--- test/.eslintignore | 2 ++ test/yup.js | 1 - tsconfig.json | 1 + 7 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 .eslintignore create mode 100644 test/.eslintignore diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..9e2b401dd --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +.eslintrc +.eslintrc.js diff --git a/src/Schema.ts b/src/Schema.ts index 0afe1f81b..3bb30a36a 100644 --- a/src/Schema.ts +++ b/src/Schema.ts @@ -2,8 +2,7 @@ import { ResolveOptions } from './Condition'; import { ValidateOptions, Callback, - MessageParams, - AnyMessageParams,, ExtraParams + ExtraParams } from './types'; export interface CastOptions { diff --git a/src/index.ts b/src/index.ts index cc6ec1dfa..eefb22b7f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,6 @@ import MixedSchema, { create as mixedCreate, - ResolveCast, SchemaSpec, - ResolveRequired, - Asserts, - TypeOf, } from './mixed'; import BoolSchema, { create as boolCreate } from './boolean'; import StringSchema, { create as stringCreate } from './string'; diff --git a/src/util/prependDeep.ts b/src/util/prependDeep.ts index ab875b864..99bfaf317 100644 --- a/src/util/prependDeep.ts +++ b/src/util/prependDeep.ts @@ -1,5 +1,5 @@ import has from 'lodash/has'; -import isSchema from './isSchema'; +import { MixedSchema } from '..'; // function has // (obj: T, prop: Key): obj is T & Record { @@ -22,8 +22,8 @@ export default function merge( target[key] = sourceVal; } else if (targetVal === sourceVal) { continue; - } else if (isSchema(targetVal)) { - if (isSchema(sourceVal)) target[key] = sourceVal.concat(targetVal); + } else if (targetVal instanceof MixedSchema) { + if (sourceVal instanceof MixedSchema) target[key] = sourceVal.concat(targetVal); } else if (isObject(targetVal)) { if (isObject(sourceVal)) target[key] = merge(targetVal, sourceVal); } else if (Array.isArray(targetVal)) { diff --git a/test/.eslintignore b/test/.eslintignore new file mode 100644 index 000000000..9e2b401dd --- /dev/null +++ b/test/.eslintignore @@ -0,0 +1,2 @@ +.eslintrc +.eslintrc.js diff --git a/test/yup.js b/test/yup.js index 69264d5f3..45b3b56b2 100644 --- a/test/yup.js +++ b/test/yup.js @@ -1,6 +1,5 @@ import reach, { getIn } from '../src/util/reach'; import prependDeep from '../src/util/prependDeep'; -import * as Async from '../src/util/async'; import { object, array, string, lazy, number, ValidationError } from '../src'; diff --git a/tsconfig.json b/tsconfig.json index f6f113548..1b619a992 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "@4c/tsconfig/web.json", "compilerOptions": { + "allowJs": true, "rootDir": "src" }, "include": ["src"] From 24a3165eae9b255e251048121cfd095087aeccd7 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Thu, 19 Nov 2020 12:03:43 -0500 Subject: [PATCH 11/31] WIP --- package.json | 14 +- yarn.lock | 392 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 269 insertions(+), 137 deletions(-) diff --git a/package.json b/package.json index 3706df57d..fd319af27 100644 --- a/package.json +++ b/package.json @@ -66,14 +66,14 @@ ] }, "devDependencies": { - "@4c/cli": "^2.1.11", - "@4c/rollout": "^2.1.10", + "@4c/cli": "^2.1.12", + "@4c/rollout": "^2.1.11", "@4c/tsconfig": "^0.3.1", "@babel/cli": "7.12.1", "@babel/core": "7.12.3", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/preset-typescript": "^7.10.4", - "@typescript-eslint/parser": "^4.0.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/preset-typescript": "^7.12.1", + "@typescript-eslint/parser": "^4.8.1", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.6.1", @@ -105,14 +105,14 @@ "sinon": "^9.2.0", "sinon-chai": "^3.5.0", "synchronous-promise": "^2.0.15", - "typescript": "^4.0.2" + "typescript": "^4.0.5" }, "dependencies": { "@babel/runtime": "^7.10.5", "lodash": "^4.17.20", "lodash-es": "^4.17.11", - "property-expr": "^2.0.4", "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", "toposort": "^2.0.2" }, "readme": "ERROR: No README data found!", diff --git a/yarn.lock b/yarn.lock index c00115dce..cb8c42fac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@4c/build@^2.2.7": - version "2.2.7" - resolved "https://registry.yarnpkg.com/@4c/build/-/build-2.2.7.tgz#123bfd73cb78c1fda46eecbddf3bbeaf3f31c94b" - integrity sha512-rBpfwNUrcnshfNSLzc2MVrfQw9k8zyqy6EEpuN8nbhOpDKgK6lvoZwUfaV0dTVwNM/kjLhSwhX8H5WB9RiTAYQ== +"@4c/build@^2.2.8": + version "2.2.8" + resolved "https://registry.yarnpkg.com/@4c/build/-/build-2.2.8.tgz#79c864081c36eda2b97358e2fd5af5f836bd1edd" + integrity sha512-nIuiHRNriiYC+PhBNn7H/J3zSnAFpdd7G1C2n7qOk9wbzYDdF52pX03qt7ZhFS4GOI74BYDmPMizcSMJQ8tbFg== dependencies: - "@4c/cli-core" "^2.1.5" + "@4c/cli-core" "^2.1.6" "@babel/cli" "^7.8.4" "@babel/core" "^7.9.0" "@manypkg/get-packages" "^1.0.0" @@ -15,12 +15,12 @@ execa "^4.0.0" fs-extra "^9.0.0" listr "^0.14.3" - typescript "^3.8.3" + typescript "^4.0.2" -"@4c/cli-core@^2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@4c/cli-core/-/cli-core-2.1.5.tgz#1688b2df03f53c9f8720dcce5ccd576e6a34207e" - integrity sha512-tMG0Guql8Z7XKnJq/wNyTtqCoS+fpRq4+YGu4fZ7xCDlhj/bRzQJ4a12YcFrTyIHcQjeEL3Z1d5mraIbsGWqpw== +"@4c/cli-core@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@4c/cli-core/-/cli-core-2.1.6.tgz#2c1e5ebc1ede2659dab6eab6268fcd2817c5b80a" + integrity sha512-EhEQrbXkdMSKPESchDLwYvT8iEiu1Y3/HWcUF4K8PF+RUpeID5NzDApQFLVkcUuoeKsMttr8141UphxZ+uwwTQ== dependencies: chalk "^4.0.0" execa "^4.0.0" @@ -29,67 +29,67 @@ inquirer "^7.0.0" is-ci "^2.0.0" log-symbols "^4.0.0" - ora "^4.0.2" + ora "^5.0.0" slash "^3.0.0" strip-ansi "^6.0.0" text-table "^0.2.0" - yargs "^15.0.1" - -"@4c/cli@^2.1.11": - version "2.1.11" - resolved "https://registry.yarnpkg.com/@4c/cli/-/cli-2.1.11.tgz#b3837a4ea1cc22dcd801f560ceedf017c06133bf" - integrity sha512-eO0ZvJ+Fk6wrvaQLoxqYtvQihcAajexREGXG2eG0XM6FcNSEP98vJnPrx40lw+U0037YrKJuIxdFbDsPXJ9OgA== - dependencies: - "@4c/build" "^2.2.7" - "@4c/init" "^3.0.7" - "@4c/intl" "^1.2.19" - "@4c/rollout" "^2.1.10" - "@4c/start" "^2.1.2" - pedantic "^5.0.6" - svg2c "^1.4.17" - ts-doctor "^1.2.2" - yargs "^15.0.1" - -"@4c/file-butler@^4.1.7": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@4c/file-butler/-/file-butler-4.1.7.tgz#e878c78e56a6e37c7b6680460a4b15193f839a5d" - integrity sha512-lErbTqgK/iqHcc8WlKRtqoT1fH7p/3NqdIUA3GhqA0XuKmxgLxa7xKVmvaLLe+qT0BS24XVVG9wXwE0swIs5vg== - dependencies: - "@4c/cli-core" "^2.1.5" + yargs "^16.0.3" + +"@4c/cli@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@4c/cli/-/cli-2.1.12.tgz#c1ffa535d365db1ffb886c8e7dc48accd465aef7" + integrity sha512-98gR8fgeqD33xkO9LnM0ss35nY/bkplFIcZpkru+688OCmO0kvjy8yxjtXX30KwJEddl5FUiNi/WfDT+wc48hw== + dependencies: + "@4c/build" "^2.2.8" + "@4c/init" "^3.0.8" + "@4c/intl" "^1.2.20" + "@4c/rollout" "^2.1.11" + "@4c/start" "^2.1.3" + pedantic "^5.0.7" + svg2c "^1.4.18" + ts-doctor "^1.2.3" + yargs "^16.0.3" + +"@4c/file-butler@^4.1.8": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@4c/file-butler/-/file-butler-4.1.8.tgz#d17cf22a45489d796d5e07e598ff44164c37ad1a" + integrity sha512-aLNNtq8WSkRBNNBc1TD6cK1NLjy8qoK+u8hMsDJtWPdQVfPrlq1RQxQZliEb5rUqsVgbSlYwE8a5p1aMKm7drA== + dependencies: + "@4c/cli-core" "^2.1.6" cpy "^7.3.0" fs-extra "^9.0.0" globby "^11.0.0" read-pkg-up "^7.0.1" - yargs "^15.0.1" + yargs "^16.0.3" -"@4c/init@^3.0.7": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@4c/init/-/init-3.0.7.tgz#c336f2a49adb557cb01c873ed7691bb29a9a50c6" - integrity sha512-awfKNqCv1RAFOvN0RS60gSLO9kkLtSl2lA5eYCH2PdVTIZDhq9owS4uk+W4rNoOGVTlc+eV6XpStSp7iOQvQQg== +"@4c/init@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@4c/init/-/init-3.0.8.tgz#324daa169989b566ad3bf7580455a818957f719f" + integrity sha512-RcFip1Y4nw4+oKcPquVErvZWNRKz9O5U9oWUO85gvpHR7kdgcq/uF5NYYpxkyQz3mFYF3LX2akPVv/FCnzDD2A== dependencies: - "@4c/cli-core" "^2.1.5" + "@4c/cli-core" "^2.1.6" find-yarn-workspace-root "^2.0.0" glob "^7.1.3" node-plop "^0.25.0" prettier "^2.0.0" - yargs "^15.0.1" + yargs "^16.0.3" -"@4c/intl@^1.2.19": - version "1.2.19" - resolved "https://registry.yarnpkg.com/@4c/intl/-/intl-1.2.19.tgz#3381e9543d76174cc6ff416a35c52fc298dd2a7f" - integrity sha512-BNV2KFubYHkeSkFJibWwcZgChZ/hdvRywYrgNcnhStacHhpiQ7se4XXa7JMI15W9QYpJgsov4gRUHaiRe1n9bw== +"@4c/intl@^1.2.20": + version "1.2.20" + resolved "https://registry.yarnpkg.com/@4c/intl/-/intl-1.2.20.tgz#c98407da416a08a8b95374deb8f32c3ce73cc053" + integrity sha512-XyeLNW9Gml1o7TLG+pMRIKqC7DR+1eBCVD5V08JkgXiOndFxyeZyKTVDNlHjTrJrAYTARvvmV9ToOJrJDXpRpg== dependencies: - "@4c/cli-core" "^2.1.5" + "@4c/cli-core" "^2.1.6" chalk "^4.0.0" glob "^7.1.3" -"@4c/rollout@^2.1.10": - version "2.1.10" - resolved "https://registry.yarnpkg.com/@4c/rollout/-/rollout-2.1.10.tgz#f7eae1d55ba7ebcd7b28493ba32bbfd3d8be5a67" - integrity sha512-7j6sTILNUwZEhA7KR0pYfn9Kex96531jgLUDcuVOtxTFEvCfkQlmuY2YR5DyEBwUk1GeMjTxj+E47lRClaLA9w== +"@4c/rollout@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@4c/rollout/-/rollout-2.1.11.tgz#d8f00572d5c71575c65226f9265d350c58ee7d84" + integrity sha512-17NSmDKHddJtIGFDa07Rd6dwCLF/BRs4qBaxbFFnHLc0iMDsliNMxV1zEhnNfT12ay9J0rDSZ7oDy7YfAdZ2xg== dependencies: - "@4c/cli-core" "^2.1.5" - "@4c/file-butler" "^4.1.7" + "@4c/cli-core" "^2.1.6" + "@4c/file-butler" "^4.1.8" chalk "^4.0.0" conventional-changelog "^3.0.6" conventional-recommended-bump "^6.0.0" @@ -103,12 +103,12 @@ rxjs "^6.5.2" semver "^7.0.0" -"@4c/start@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@4c/start/-/start-2.1.2.tgz#7e515dbb764b590d2a42ad5668bf7491aa761811" - integrity sha512-n3n8aFNzktzrqTtjDQZRIBI2wuqOx1tIZMFlFNOM9pV5X6JabzahpcHFbLW9yzJEe+ti7emqaSykoXK56nJe/w== +"@4c/start@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@4c/start/-/start-2.1.3.tgz#2bcf7e1f86a040bc788a648306d6794768787ea0" + integrity sha512-syp0j5zUwhc2H4bpAL9XvWFCcfx2c7cDh0Upm0tmgUkByNBarXELDXtHfHAOEMGHxGJ2FyhfOr0K//h7m4/jfg== dependencies: - "@4c/cli-core" "^2.1.5" + "@4c/cli-core" "^2.1.6" "@babel/code-frame" "^7.5.5" dotenv "^8.1.0" exists-case "^0.1.0" @@ -228,6 +228,15 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/generator@^7.11.4", "@babel/generator@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de" + integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== + dependencies: + "@babel/types" "^7.12.5" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" @@ -280,7 +289,7 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": +"@babel/helper-create-class-features-plugin@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== @@ -292,6 +301,17 @@ "@babel/helper-replace-supers" "^7.10.4" "@babel/helper-split-export-declaration" "^7.10.4" +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/helper-create-regexp-features-plugin@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" @@ -355,7 +375,7 @@ dependencies: "@babel/types" "^7.12.1" -"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.12.1": +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0", "@babel/helper-module-transforms@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== @@ -439,6 +459,15 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helpers@^7.10.4": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" + integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + "@babel/helpers@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.1.tgz#8a8261c1d438ec18cb890434df4ec768734c1e79" @@ -462,6 +491,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.3.tgz#a305415ebe7a6c7023b40b5122a0662d928334cd" integrity sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw== +"@babel/parser@^7.11.4", "@babel/parser@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" + integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== + "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" @@ -511,10 +545,10 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" - integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== +"@babel/plugin-proposal-logical-assignment-operators@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz#f2c490d36e1b3c9659241034a5d2cd50263a2751" + integrity sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -695,10 +729,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-typescript@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25" - integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ== +"@babel/plugin-syntax-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz#460ba9d77077653803c3dd2e673f76d66b4029e5" + integrity sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA== dependencies: "@babel/helper-plugin-utils" "^7.10.4" @@ -1004,14 +1038,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-typescript@^7.10.4": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz#2b4879676af37342ebb278216dd090ac67f13abb" - integrity sha512-edJsNzTtvb3MaXQwj8403B7mZoGu9ElDJQZOKjGUnvilquxBA3IQoEIOvkX/1O8xfAsnHS/oQhe2w/IXrr+w0w== +"@babel/plugin-transform-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz#d92cc0af504d510e26a754a7dbc2e5c8cd9c7ab4" + integrity sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-create-class-features-plugin" "^7.12.1" "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-typescript" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.12.1" "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" @@ -1122,13 +1156,13 @@ "@babel/plugin-transform-react-jsx-source" "^7.10.4" "@babel/plugin-transform-react-pure-annotations" "^7.10.4" -"@babel/preset-typescript@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz#7d5d052e52a682480d6e2cc5aa31be61c8c25e36" - integrity sha512-SdYnvGPv+bLlwkF2VkJnaX/ni1sMNetcGI1+nThF1gyv6Ph8Qucc4ZZAjM5yZcE/AKRXIOTZz7eSRDWOEjPyRQ== +"@babel/preset-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.12.1.tgz#86480b483bb97f75036e8864fe404cc782cc311b" + integrity sha512-hNK/DhmoJPsksdHuI/RVrcEws7GN5eamhi28JkO52MqIxU8Z0QpmiSOQxZHWOHV7I3P4UjHV97ay4TcamMA6Kw== dependencies: "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-typescript" "^7.10.4" + "@babel/plugin-transform-typescript" "^7.12.1" "@babel/runtime@^7.10.5", "@babel/runtime@^7.8.4": version "7.10.5" @@ -1168,6 +1202,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.11.0", "@babel/traverse@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.5.tgz#78a0c68c8e8a35e4cacfd31db8bb303d5606f095" + integrity sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.12.5" + "@babel/types" "^7.12.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.12.1", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.1.tgz#e109d9ab99a8de735be287ee3d6a9947a190c4ae" @@ -1177,6 +1226,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.12.5": + version "7.12.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz#ae0e55ef1cce1fbc881cd26f8234eb3e657edc96" + integrity sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1814,14 +1872,14 @@ "@typescript-eslint/typescript-estree" "2.22.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/parser@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.0.1.tgz#73772080db7a7a4534a35d719e006f503e664dc3" - integrity sha512-1+qLmXHNAWSQ7RB6fdSQszAiA7JTwzakj5cNYjBTUmpH2cqilxMZEIV+DRKjVZs8NzP3ALmKexB0w/ExjcK9Iw== +"@typescript-eslint/parser@^4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.8.1.tgz#4fe2fbdbb67485bafc4320b3ae91e34efe1219d1" + integrity sha512-QND8XSVetATHK9y2Ltc/XBl5Ro7Y62YuZKnPEwnNPB8E379fDsvzJ1dMJ46fg/VOmk0hXhatc+GXs5MaXuL5Uw== dependencies: - "@typescript-eslint/scope-manager" "4.0.1" - "@typescript-eslint/types" "4.0.1" - "@typescript-eslint/typescript-estree" "4.0.1" + "@typescript-eslint/scope-manager" "4.8.1" + "@typescript-eslint/types" "4.8.1" + "@typescript-eslint/typescript-estree" "4.8.1" debug "^4.1.1" "@typescript-eslint/scope-manager@4.0.1": @@ -1832,11 +1890,24 @@ "@typescript-eslint/types" "4.0.1" "@typescript-eslint/visitor-keys" "4.0.1" +"@typescript-eslint/scope-manager@4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.8.1.tgz#e343c475f8f1d15801b546cb17d7f309b768fdce" + integrity sha512-r0iUOc41KFFbZdPAdCS4K1mXivnSZqXS5D9oW+iykQsRlTbQRfuFRSW20xKDdYiaCoH+SkSLeIF484g3kWzwOQ== + dependencies: + "@typescript-eslint/types" "4.8.1" + "@typescript-eslint/visitor-keys" "4.8.1" + "@typescript-eslint/types@4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.0.1.tgz#1cf72582f764931f085cb8230ff215980fe467b2" integrity sha512-S+gD3fgbkZYW2rnbjugNMqibm9HpEjqZBZkTiI3PwbbNGWmAcxolWIUwZ0SKeG4Dy2ktpKKaI/6+HGYVH8Qrlg== +"@typescript-eslint/types@4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.8.1.tgz#23829c73c5fc6f4fcd5346a7780b274f72fee222" + integrity sha512-ave2a18x2Y25q5K05K/U3JQIe2Av4+TNi/2YuzyaXLAsDx6UZkz1boZ7nR/N6Wwae2PpudTZmHFXqu7faXfHmA== + "@typescript-eslint/typescript-estree@2.22.0": version "2.22.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.22.0.tgz#a16ed45876abf743e1f5857e2f4a1c3199fd219e" @@ -1877,6 +1948,20 @@ semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.8.1.tgz#7307e3f2c9e95df7daa8dc0a34b8c43b7ec0dd32" + integrity sha512-bJ6Fn/6tW2g7WIkCWh3QRlaSU7CdUUK52shx36/J7T5oTQzANvi6raoTsbwGM11+7eBbeem8hCCKbyvAc0X3sQ== + dependencies: + "@typescript-eslint/types" "4.8.1" + "@typescript-eslint/visitor-keys" "4.8.1" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/visitor-keys@4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.1.tgz#d4e8de62775f2a6db71c7e8539633680039fdd6c" @@ -1885,6 +1970,14 @@ "@typescript-eslint/types" "4.0.1" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.8.1.tgz#794f68ee292d1b2e3aa9690ebedfcb3a8c90e3c3" + integrity sha512-3nrwXFdEYALQh/zW8rFwP4QltqsanCDz4CwWMPiIZmwlk9GlvBeueEIbq05SEq4ganqM0g9nh02xXgv5XI3PeQ== + dependencies: + "@typescript-eslint/types" "4.8.1" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -3206,10 +3299,10 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" - integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== +cli-spinners@^2.4.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" + integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== cli-truncate@^0.2.1: version "0.2.1" @@ -3248,6 +3341,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -4441,6 +4543,11 @@ escalade@^3.0.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ== +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -4569,7 +4676,7 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@^5.0.0, eslint-scope@^5.1.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -4679,7 +4786,7 @@ eslint@^7.12.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^7.3.0: +espree@^7.2.0, espree@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== @@ -5339,7 +5446,7 @@ gensync@^1.0.0-beta.1: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7329,13 +7436,6 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== - dependencies: - chalk "^2.4.2" - log-symbols@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" @@ -8372,16 +8472,16 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.3.tgz#752a1b7b4be4825546a7a3d59256fa523b6b6d05" - integrity sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg== +ora@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.1.0.tgz#b188cf8cd2d4d9b13fd25383bc3e5cba352c94f8" + integrity sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w== dependencies: - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-spinners "^2.2.0" + cli-spinners "^2.4.0" is-interactive "^1.0.0" - log-symbols "^3.0.0" + log-symbols "^4.0.0" mute-stream "0.0.8" strip-ansi "^6.0.0" wcwidth "^1.0.1" @@ -8717,15 +8817,15 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pedantic@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/pedantic/-/pedantic-5.0.6.tgz#b4dabfd009ccab7478d11a91ac1d92532d3c1adb" - integrity sha512-vAeN8jgK6Og/zMZa9SpjC5OHqyvpi3rBamKuOpVNRifFMF6pVZyUA63ZbQaJUa9SddpN3uXooIjNYZ4tllRT6w== +pedantic@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/pedantic/-/pedantic-5.0.7.tgz#d67bab2b914b6f4688d1c79399d72b3da529fdd3" + integrity sha512-puevfhpNm87+yPdMm1WjUBun7RKGrOkScQslM73ZzhYRy90V/JMEWMYNBOT9wLkkP6lNp6qOQ9/z4vkaLagabw== dependencies: - "@4c/cli-core" "^2.1.5" + "@4c/cli-core" "^2.1.6" eslint "^7.0.0" prettier "^2.0.0" - yargs "^15.0.1" + yargs "^16.0.3" performance-now@^2.1.0: version "2.1.0" @@ -10410,12 +10510,12 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" -svg2c@^1.4.17: - version "1.4.17" - resolved "https://registry.yarnpkg.com/svg2c/-/svg2c-1.4.17.tgz#c8d649429efb588b810cec89897b94eb5a6e470c" - integrity sha512-6xcXP7KFm2OUjUNK07MJU5uZujgI5Olbp+F2zfBBLE7Icj8869vtz7eve6M6Aea6McePJpvX70/mJk2yBGpz4A== +svg2c@^1.4.18: + version "1.4.18" + resolved "https://registry.yarnpkg.com/svg2c/-/svg2c-1.4.18.tgz#ebd037141e23963d3293e309171f7a5a1e4eebdc" + integrity sha512-+8zB7w9HbV+p8cRTbQj4Jh/8N1pO3GgQy7Yj/jZT7gM/41Oib3MITn9JJhTSrM5KR5HWjEKlRK6yQl8DFLH7eA== dependencies: - "@4c/cli-core" "^2.1.5" + "@4c/cli-core" "^2.1.6" "@babel/core" "^7.7.0" "@babel/preset-react" "^7.7.0" js-yaml "^3.13.1" @@ -10712,18 +10812,18 @@ trough@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" -ts-doctor@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/ts-doctor/-/ts-doctor-1.2.2.tgz#9620d11717bac66be5b21b9591d0c65772a59ad2" - integrity sha512-vX1ZdK13yzSXeUon2rtMMtHQ8tyHevWeq6RAYq9ng7aIusAvhydSiIQJ8C7PT501knV50eNulzqBaE0Q+k4D2Q== +ts-doctor@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/ts-doctor/-/ts-doctor-1.2.3.tgz#a48857864c83f041fdcc9e45bab5a8afbcbdfc65" + integrity sha512-0oSPCyr3NAg8uwxwFlSdSuAg4PNe39scqizqTSskiiPBHlGcTX0hY5EHnsQHQaWQWlVnGEp825D1RAwN/LHLjw== dependencies: - "@4c/cli-core" "^2.1.5" + "@4c/cli-core" "^2.1.6" "@manypkg/get-packages" "^1.0.0" comment-json "^3.0.2" lodash "^4.17.15" prettier "^2.0.0" - typescript "^3.8.3" - yargs "^15.1.0" + typescript "^4.0.2" + yargs "^16.0.3" tsconfig-paths@^3.9.0: version "3.9.0" @@ -10817,16 +10917,16 @@ typescript-workspace-plugin@^2.0.1: resolved "https://registry.yarnpkg.com/typescript-workspace-plugin/-/typescript-workspace-plugin-2.0.1.tgz#3d88be1c35a7fdf2c0160c8cf569ca8993439a12" integrity sha512-xjIYNFlPIA7IWXvnOFJoAeHPbPJSo0AiQDCRJzaAp3+xZwz6maTgeRLB0oEHVtCqz4Q1CDN6U9kh/2z8sxdDBQ== -typescript@^3.8.3: - version "3.9.7" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" - integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== - typescript@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== +typescript@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" + integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== + uglify-js@^3.1.4: version "3.4.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" @@ -11433,6 +11533,15 @@ wrap-ansi@^6.0.0, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -11488,6 +11597,11 @@ y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -11526,6 +11640,11 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -11542,7 +11661,7 @@ yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^15.0.1, yargs@^15.1.0, yargs@^15.4.1: +yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== @@ -11558,3 +11677,16 @@ yargs@^15.0.1, yargs@^15.1.0, yargs@^15.4.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.2" + +yargs@^16.0.3: + version "16.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.1.1.tgz#5a4a095bd1ca806b0a50d0c03611d38034d219a1" + integrity sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" From 7b78870d20f6fc83a0d16ccd377f04c197eb7cda Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Thu, 19 Nov 2020 14:28:57 -0500 Subject: [PATCH 12/31] WIP --- package.json | 1 + src/Lazy.ts | 39 +++++++++--- src/array.ts | 50 ++++++++------- src/boolean.ts | 33 +++++++++- src/mixed.ts | 117 +++++++++++++++++++--------------- src/number.ts | 27 ++++---- src/object.ts | 85 +++++++++++++------------ src/string.ts | 27 ++++---- src/util/async.ts | 14 ---- src/util/cloneDeep.ts | 0 src/util/createValidation.ts | 1 + src/util/prependDeep.ts | 35 ---------- src/util/runTests.ts | 11 +++- src/util/types.ts | 67 ++++--------------- test/mixed.js | 12 ++++ test/types.ts | 120 ++++++++++------------------------- test/yup.js | 30 --------- yarn.lock | 5 ++ 18 files changed, 303 insertions(+), 371 deletions(-) delete mode 100644 src/util/async.ts delete mode 100644 src/util/cloneDeep.ts delete mode 100644 src/util/prependDeep.ts diff --git a/package.json b/package.json index fd319af27..280093082 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,7 @@ }, "dependencies": { "@babel/runtime": "^7.10.5", + "@types/lodash": "^4.14.165", "lodash": "^4.17.20", "lodash-es": "^4.17.11", "nanoclone": "^0.2.1", diff --git a/src/Lazy.ts b/src/Lazy.ts index 9be3c3190..a3ed2be17 100644 --- a/src/Lazy.ts +++ b/src/Lazy.ts @@ -2,24 +2,37 @@ import isSchema from './util/isSchema'; import Schema, { CastOptions } from './Schema'; import { Callback, ValidateOptions } from './types'; import { ResolveOptions } from './Condition'; +import { ResolveInput, TypedSchema } from './util/types'; +import type MixedSchema from './mixed'; -export type LazyBuilder = ( +export type LazyBuilder = ( value: any, options: ResolveOptions, ) => T; -export function create(builder: LazyBuilder) { +export function create(builder: LazyBuilder) { return new Lazy(builder); } -class Lazy implements Schema { +export type LazyReturnValue = T extends Lazy + ? TSchema + : never; + +export type LazyType = LazyReturnValue extends MixedSchema + ? TType + : never; + +class Lazy implements Schema { type = 'lazy' as const; __isYupSchema__ = true; - constructor(private builder: LazyBuilder) {} + readonly __inputType!: T['__inputType']; + readonly __outputType!: T['__outputType']; - private _resolve = (value: any, options: ResolveOptions) => { + constructor(private builder: LazyBuilder) {} + + private _resolve = (value: any, options: ResolveOptions = {}) => { let schema = this.builder(value, options); if (!isSchema(schema)) @@ -31,20 +44,26 @@ class Lazy implements Schema { resolve(options: ResolveOptions) { return this._resolve(options.value, options); } - cast(value: any, options: CastOptions) { + cast(value: any, options?: CastOptions) { return this._resolve(value, options).cast(value, options); } - validate(value: any, options: ValidateOptions, maybeCb?: Callback) { + + validate( + value: any, + options?: ValidateOptions, + maybeCb?: Callback, + ): T['__outputType'] { + // @ts-expect-error return this._resolve(value, options).validate(value, options, maybeCb); } - validateSync(value: any, options: ValidateOptions) { + validateSync(value: any, options?: ValidateOptions): T['__outputType'] { return this._resolve(value, options).validateSync(value, options); } - validateAt(path: string, value: any, options: ValidateOptions) { + validateAt(path: string, value: any, options?: ValidateOptions) { return this._resolve(value, options).validateAt(path, value, options); } - validateSyncAt(path: string, value: any, options: ValidateOptions) { + validateSyncAt(path: string, value: any, options?: ValidateOptions) { return this._resolve(value, options).validateSyncAt(path, value, options); } describe() { diff --git a/src/array.ts b/src/array.ts index 165e3cb60..4d724e091 100644 --- a/src/array.ts +++ b/src/array.ts @@ -10,16 +10,18 @@ import ValidationError from './ValidationError'; import Reference from './Reference'; import { Asserts, + Nullability, + Presence, ResolveInput, ResolveOutput, - SetNullability, - SetPresence, - TypeDef, TypeOf, + Unset, } from './util/types'; type RefectorFn = (value: any, index: number, array: any[]) => boolean; +type MaybeArray = Maybe[]>; + export function create( type?: TInner, ) { @@ -32,14 +34,16 @@ type Type = T extends MixedSchema export default class ArraySchema< T extends MixedSchema = MixedSchema, - TDef extends TypeDef = '', - TDefault extends Maybe>[]> = undefined + TDefault extends MaybeArray = undefined, + TNullablity extends Nullability = Unset, + TPresence extends Presence = Unset > extends MixedSchema< Type[], - TDef, TDefault, - ResolveInput[], TDef, TDefault>, - ResolveOutput[], TDef, TDefault> + TNullablity, + TPresence, + ResolveInput[], TNullablity, TDefault>, + ResolveOutput[], TNullablity, TPresence, TDefault> > { // @@ -186,7 +190,7 @@ export default class ArraySchema< of( schema: TInner, - ): ArraySchema { + ): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -273,27 +277,27 @@ export default class ArraySchema< export default interface ArraySchema< T extends MixedSchema, - TDef extends TypeDef, - TDefault extends Maybe>[]> + TDefault extends MaybeArray, + TNullablity extends Nullability, + TPresence extends Presence > extends MixedSchema< Type[], - TDef, TDefault, - ResolveInput[], TDef, TDefault>, - ResolveOutput[], TDef, TDefault> + TNullablity, + TPresence, + ResolveInput[], TNullablity, TDefault>, + ResolveOutput[], TNullablity, TPresence, TDefault> > { default(): TDefault; - default( - def: TNext | (() => TNext), - ): ArraySchema; + default( + def: TNextDefault | (() => TNextDefault), + ): ArraySchema; - required(): ArraySchema, TDefault>; - notRequired(): ArraySchema, TDefault>; + required(): ArraySchema; + notRequired(): ArraySchema; - nullable( - isNullable?: true, - ): ArraySchema, TDefault>; + nullable(isNullable?: true): ArraySchema; nullable( isNullable: false, - ): ArraySchema, TDefault>; + ): ArraySchema; } diff --git a/src/boolean.ts b/src/boolean.ts index e91d24239..dc6e0e635 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -1,12 +1,17 @@ import MixedSchema from './mixed'; +import { Maybe } from './types'; +import { Nullability, Presence } from './util/types'; export function create() { return new BooleanSchema(); } -export default class BooleanSchema extends MixedSchema< - TType -> { +export default class BooleanSchema< + TType extends boolean, + TDefault extends Maybe, + TNullablity extends Nullability, + TPresence extends Presence +> extends MixedSchema { constructor() { super({ type: 'boolean' }); @@ -27,3 +32,25 @@ export default class BooleanSchema extends MixedSchema< return typeof v === 'boolean'; } } + +export default interface BooleanSchema< + TType extends boolean, + TDefault extends Maybe, + TNullablity extends Nullability, + TPresence extends Presence +> extends MixedSchema { + default(): TDefault; + default>( + def: TNextDefault | (() => TNextDefault), + ): BooleanSchema; + + required(): BooleanSchema; + notRequired(): BooleanSchema; + + nullable( + isNullable?: true, + ): BooleanSchema; + nullable( + isNullable: false, + ): BooleanSchema; +} diff --git a/src/mixed.ts b/src/mixed.ts index 8a4ea4253..64b53bb97 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -29,10 +29,11 @@ import Schema, { } from './Schema'; import { ValidationError } from '.'; import { + Nullability, + Presence, ResolveInput, ResolveOutput, - SetNullability, - TypeDef, + Unset, } from './util/types'; class RefSet { @@ -92,8 +93,9 @@ class RefSet { } export type SchemaSpec = { - nullable?: boolean; - default: TDefault | (() => TDefault); + nullability: Nullability; + presence: Presence; + default?: TDefault | (() => TDefault); hasDefault?: boolean; abortEarly?: boolean; strip?: boolean; @@ -114,10 +116,11 @@ export function create() { export default class MixedSchema< TType = any, - TDef extends TypeDef = '', TDefault = any, - TInput = ResolveInput, - TOutput = ResolveOutput + TNullablity extends Nullability = Unset, + TPresence extends Presence = Unset, + TInput = ResolveInput, + TOutput = ResolveOutput > implements Schema { readonly type: string; @@ -164,8 +167,8 @@ export default class MixedSchema< recursive: true, label: undefined, meta: undefined, - nullable: false, - default: undefined as any, + nullability: 'unset', + presence: 'unset', ...options?.spec, }; } @@ -179,7 +182,7 @@ export default class MixedSchema< return true; } - clone(spec?: SchemaSpec): this { + clone(spec?: Partial>): this { if (this._mutate) { if (spec) Object.assign(this.spec, spec); return this; @@ -244,10 +247,15 @@ export default class MixedSchema< let base = this; let combined = schema.clone(); - // new undefined default is overridden by old non-undefined one, revert - if (schema.spec.hasDefault) { - combined.spec.default = schema.spec.default; - } + const mergedSpec = { ...base.spec, ...combined.spec }; + + if (combined.spec.nullability === 'unset') + mergedSpec.nullability = base.spec.nullability; + + if (combined.spec.presence === 'unset') + mergedSpec.presence = base.spec.presence; + + combined.spec = mergedSpec; combined._typeError ||= base._typeError; combined._whitelistError ||= base._whitelistError; @@ -280,7 +288,7 @@ export default class MixedSchema< } isType(v: any) { - if (this.spec.nullable && v === null) return true; + if (this.spec.nullability === 'nullable' && v === null) return true; return this._typeCheck(v); } @@ -472,31 +480,33 @@ export default class MixedSchema< } } - getDefault(options = {}) { - let schema = this.resolve(options); - return schema.default(); + private _getDefault() { + let defaultValue = this.spec.default; + + if (defaultValue == null) { + return defaultValue; + } + return typeof defaultValue === 'function' + ? defaultValue.call(this) + : cloneDeep(defaultValue); + } + + getDefault(options?: ResolveOptions) { + let schema = this.resolve(options || {}); + return schema._getDefault(); } - default(): TDefault; // FIXME(ts): typed default + default(): TDefault; default>( def: TNextDefault | (() => TNextDefault), - ): MixedSchema; + ): MixedSchema; default(def?: TDefault | (() => TDefault)) { if (arguments.length === 0) { - let defaultValue = this.spec.default; - - if (defaultValue == null) { - return defaultValue; - } - - return typeof defaultValue === 'function' - ? defaultValue.call(this) - : cloneDeep(defaultValue); + return this._getDefault(); } - var next = this.clone(); - next.spec.hasDefault = true; - next.spec.default = def as any; + let next = this.clone({ default: def }); + return next; } @@ -512,32 +522,36 @@ export default class MixedSchema< required( message = locale.required, - ): MixedSchema | 'required'> { - return this.test({ - message, - name: 'required', - exclusive: true, - test(value) { - return this.schema._isPresent(value); - }, - }) as any; - } - - notRequired(): MixedSchema | 'optional'> { - var next = this.clone(); + ): MixedSchema { + return this.clone({ presence: 'required' }).withMutation((s) => + s.test({ + message, + name: 'required', + exclusive: true, + test(value) { + return this.schema._isPresent(value); + }, + }), + ) as any; + } + + notRequired(): MixedSchema { + var next = this.clone({ presence: 'optional' }); next.tests = next.tests.filter((test) => test.OPTIONS.name !== 'required'); return next as any; } nullable( isNullable?: true, - ): MixedSchema, TDefault>; + ): MixedSchema; nullable( isNullable: false, - ): MixedSchema, TDefault>; - nullable(isNullable = true): MixedSchema { - var next = this.clone(); - next.spec.nullable = isNullable; + ): MixedSchema; + nullable(isNullable = true): MixedSchema { + var next = this.clone({ + nullability: isNullable ? 'nullable' : 'nonnullable', + }); + return next as any; } @@ -747,8 +761,9 @@ export default class MixedSchema< export default interface MixedSchema< TType, - TDef extends TypeDef, TDefault, + TNullablity extends Nullability, + TPresence extends Presence, TInput, TOutput > { diff --git a/src/number.ts b/src/number.ts index 001888725..b8a14410a 100644 --- a/src/number.ts +++ b/src/number.ts @@ -3,7 +3,7 @@ import { number as locale } from './locale'; import isAbsent from './util/isAbsent'; import { Maybe } from './types'; import Reference from './Reference'; -import { SetNullability, SetPresence, TypeDef } from './util/types'; +import { Nullability, Presence, Unset } from './util/types'; let isNaN = (value: Maybe) => value != +value!; @@ -13,9 +13,10 @@ export function create() { export default class NumberSchema< TType extends number, - TDef extends TypeDef = '', - TDefault extends Maybe = undefined -> extends MixedSchema { + TDefault extends Maybe = undefined, + TNullablity extends Nullability = Unset, + TPresence extends Presence = Unset +> extends MixedSchema { constructor() { super({ type: 'number' }); @@ -129,24 +130,24 @@ export default class NumberSchema< } } -// @ts-expect-error export default interface NumberSchema< TType extends number, - TDef extends TypeDef, - TDefault extends Maybe -> extends MixedSchema { + TDefault extends Maybe, + TNullablity extends Nullability, + TPresence extends Presence +> extends MixedSchema { default(): TDefault; default>( def: TNextDefault | (() => TNextDefault), - ): NumberSchema; + ): NumberSchema; - required(): NumberSchema, TDefault>; - notRequired(): NumberSchema, TDefault>; + required(): NumberSchema; + notRequired(): NumberSchema; nullable( isNullable?: true, - ): NumberSchema, TDefault>; + ): NumberSchema; nullable( isNullable: false, - ): NumberSchema, TDefault>; + ): NumberSchema; } diff --git a/src/object.ts b/src/object.ts index 05743168d..287993d64 100644 --- a/src/object.ts +++ b/src/object.ts @@ -3,7 +3,6 @@ import snakeCase from 'lodash/snakeCase'; import camelCase from 'lodash/camelCase'; import mapKeys from 'lodash/mapKeys'; import mapValues from 'lodash/mapValues'; -import pick from 'lodash/pick'; import { getter } from 'property-expr'; import MixedSchema, { SchemaSpec } from './mixed'; @@ -18,12 +17,13 @@ import isSchema from './util/isSchema'; import { ResolveInput, ResolveOutput, - TypeDef, - SetNullability, - SetPresence, + Nullability, + Presence, + Unset, TypedSchema, } from './util/types'; import Reference from './Reference'; +import Lazy, { LazyType } from './Lazy'; let isObject = (obj: any): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; @@ -33,7 +33,7 @@ function unknown(ctx: ObjectSchema, value: any) { return Object.keys(value).filter((key) => known.indexOf(key) === -1); } -type ObjectShape = Record; +type ObjectShape = Record>; type AssignShape = { [P in keyof T]: P extends keyof U ? U[P] : T[P]; @@ -52,11 +52,11 @@ export type TypeFromShape = { : // not sure why this is necessary Shape[K] extends ObjectSchema ? TypeFromShape - : never; + : LazyType; }; export type DefaultFromShape = { - [K in keyof Shape]: Shape[K] extends MixedSchema + [K in keyof Shape]: Shape[K] extends MixedSchema ? TDefault : Shape[K] extends Reference ? undefined @@ -64,7 +64,7 @@ export type DefaultFromShape = { }; export type TypeOfShape = { - [K in keyof Shape]: Shape[K] extends MixedSchema + [K in keyof Shape]: Shape[K] extends TypedSchema ? Shape[K]['__inputType'] : Shape[K] extends Reference ? unknown @@ -72,7 +72,7 @@ export type TypeOfShape = { }; export type AssertsShape = { - [K in keyof Shape]: Shape[K] extends MixedSchema + [K in keyof Shape]: Shape[K] extends TypedSchema ? Shape[K]['__outputType'] : Shape[K] extends Reference ? unknown @@ -87,14 +87,16 @@ const defaultSort = sortByKeyOrder([]); export default class ObjectSchema< TShape extends ObjectShape = ObjectShape, - TDef extends TypeDef = '', - TDefault extends Maybe> = DefaultFromShape + TDefault extends Maybe> = DefaultFromShape, + TNullablity extends Nullability = Unset, + TPresence extends Presence = Unset > extends MixedSchema< TypeFromShape, - TDef, TDefault, - ResolveInput, TDef, TDefault>, - ResolveOutput, TDef, TDefault> + TNullablity, + TPresence, + ResolveInput, TNullablity, TDefault>, + ResolveOutput, TNullablity, TPresence, TDefault> > { fields: TShape = Object.create(null); @@ -107,20 +109,6 @@ export default class ObjectSchema< constructor(spec?: TShape) { super({ type: 'object', - spec: { - default(this: ObjectSchema) { - if (!this._nodes.length) return undefined; - - let dft = {} as Record; - this._nodes.forEach((key) => { - dft[key] = - 'default' in this.fields[key] - ? this.fields[key].default() - : undefined; - }); - return dft as any; - }, - }, }); this.withMutation(() => { @@ -328,6 +316,23 @@ export default class ObjectSchema< return next.withMutation((next) => next.shape(nextFields)); } + default(nextDefault?: any) { + if (arguments.length) return super.default(nextDefault); + if ('default' in this.spec) return super.default(); + + // if there is no default set invent one + if (!this._nodes.length) { + return undefined; + } + + let dft = {} as Record; + this._nodes.forEach((key) => { + const field = this.fields[key]; + dft[key] = 'default' in field ? field.default() : undefined; + }); + return dft as any; + } + shape( additions: TNextShape, excludes: [string, string][] = [], @@ -375,7 +380,7 @@ export default class ObjectSchema< delete fields[key]; } - return next.withMutation((next) => next.shape(fields)); + return next.withMutation((next: any) => next.shape(fields)); } from(from: string, to: keyof TShape, alias?: boolean) { @@ -450,27 +455,29 @@ export default class ObjectSchema< export default interface ObjectSchema< TShape extends ObjectShape, - TDef extends TypeDef, - TDefault extends Maybe> + TDefault extends Maybe>, + TNullablity extends Nullability, + TPresence extends Presence > extends MixedSchema< TypeFromShape, - TDef, TDefault, - ResolveInput, TDef, TDefault>, - ResolveOutput, TDef, TDefault> + TNullablity, + TPresence, + ResolveInput, TNullablity, TDefault>, + ResolveOutput, TNullablity, TPresence, TDefault> > { default(): TDefault; default>>( def: TNextDefault | (() => TNextDefault), - ): ObjectSchema; + ): ObjectSchema; - required(): ObjectSchema, TDefault>; - notRequired(): ObjectSchema, TDefault>; + required(): ObjectSchema; + notRequired(): ObjectSchema; nullable( isNullable?: true, - ): ObjectSchema, TDefault>; + ): ObjectSchema; nullable( isNullable: false, - ): ObjectSchema, TDefault>; + ): ObjectSchema; } diff --git a/src/string.ts b/src/string.ts index 7e3646f97..f3b4ea104 100644 --- a/src/string.ts +++ b/src/string.ts @@ -3,7 +3,7 @@ import { string as locale } from './locale'; import isAbsent from './util/isAbsent'; import Reference from './Reference'; import { Message, Maybe } from './types'; -import { SetNullability, SetPresence, TypeDef } from './util/types'; +import { Nullability, Presence, Unset } from './util/types'; // eslint-disable-next-line let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; @@ -29,9 +29,10 @@ export function create() { export default class StringSchema< TType extends string = string, - TDef extends TypeDef = '', - TDefault extends Maybe = undefined -> extends MixedSchema { + TDefault extends Maybe = undefined, + TNullablity extends Nullability = Unset, + TPresence extends Presence = Unset +> extends MixedSchema { _tsType!: string | undefined; _tsValidate!: string | undefined; @@ -194,24 +195,24 @@ export default class StringSchema< } } -// @ts-ignore export default interface StringSchema< TType extends string, - TDef extends TypeDef, - TDefault extends Maybe -> extends MixedSchema { + TDefault extends Maybe, + TNullablity extends Nullability, + TPresence extends Presence +> extends MixedSchema { default(): TDefault; default>( def: TNextDefault | (() => TNextDefault), - ): StringSchema; + ): StringSchema; - required(): StringSchema, TDefault>; - notRequired(): StringSchema, TDefault>; + required(): StringSchema; + notRequired(): StringSchema; nullable( isNullable?: true, - ): StringSchema, TDefault>; + ): StringSchema; nullable( isNullable: false, - ): StringSchema, TDefault>; + ): StringSchema; } diff --git a/src/util/async.ts b/src/util/async.ts deleted file mode 100644 index cbfdf001e..000000000 --- a/src/util/async.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Callback } from '../types'; - -export function asCallback(promise: Promise, callback: Callback) { - promise.then((result) => callback(null, result), callback); -} - -export const once = any>(cb: T) => { - let fired = false; - return (...args: Parameters) => { - if (fired) return; - fired = true; - cb(...args); - }; -}; diff --git a/src/util/cloneDeep.ts b/src/util/cloneDeep.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts index 821307769..530857e31 100644 --- a/src/util/createValidation.ts +++ b/src/util/createValidation.ts @@ -31,6 +31,7 @@ export type TestContext = { export type TestFunction = ( this: TestContext, value: T, + context: TestContext, ) => boolean | ValidationError | Promise; export type TestOptions = { diff --git a/src/util/prependDeep.ts b/src/util/prependDeep.ts deleted file mode 100644 index 99bfaf317..000000000 --- a/src/util/prependDeep.ts +++ /dev/null @@ -1,35 +0,0 @@ -import has from 'lodash/has'; -import { MixedSchema } from '..'; - -// function has -// (obj: T, prop: Key): obj is T & Record { -// return has(obj, prop) -// } - -let isObject = (obj: any): obj is {} => - Object.prototype.toString.call(obj) === '[object Object]'; - -export default function merge( - target: Record, - source: Record, -) { - for (let key in source) - if (has(source, key)) { - let sourceVal = source[key]; - let targetVal = (target as any)[key]; - - if (targetVal === undefined) { - target[key] = sourceVal; - } else if (targetVal === sourceVal) { - continue; - } else if (targetVal instanceof MixedSchema) { - if (sourceVal instanceof MixedSchema) target[key] = sourceVal.concat(targetVal); - } else if (isObject(targetVal)) { - if (isObject(sourceVal)) target[key] = merge(targetVal, sourceVal); - } else if (Array.isArray(targetVal)) { - if (Array.isArray(sourceVal)) target[key] = sourceVal.concat(targetVal); - } - } - - return target; -} diff --git a/src/util/runTests.ts b/src/util/runTests.ts index 04ee16412..876a617a1 100644 --- a/src/util/runTests.ts +++ b/src/util/runTests.ts @@ -1,5 +1,4 @@ import ValidationError from '../ValidationError'; -import { once } from './async'; import { TestOptions } from './createValidation'; import { Callback } from '../types'; @@ -15,6 +14,16 @@ export type TestRunOptions = { value: any; sync?: boolean; }; + +const once = any>(cb: T) => { + let fired = false; + return (...args: Parameters) => { + if (fired) return; + fired = true; + cb(...args); + }; +}; + export default function runTests(options: TestRunOptions, cb: Callback): void { let { endEarly, tests, args, value, errors, sort, path } = options; diff --git a/src/util/types.ts b/src/util/types.ts index 592f5e6df..7e6e4240e 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -3,60 +3,40 @@ import Schema from '../Schema'; import { Maybe } from '../types'; -export type Presence = 'required' | 'optional'; -export type Nullability = 'nullable' | 'nonnullable'; - -type Pluck = T extends O ? T : never; - -type MaintainOptionality = T extends undefined ? U | undefined : U; +export type Unset = 'unset'; +export type Presence = 'required' | 'optional' | Unset; +export type Nullability = 'nullable' | 'nonnullable' | Unset; type StrictNonNullable = T extends null ? never : T; export type TypeDef = Nullability | Presence | ''; -export type SetNullability< - Def extends TypeDef, - TValue extends Nullability -> = TValue extends 'nullable' - ? Exclude | 'nullable' - : Exclude | 'nonnullable'; - -export type SetPresence< - Def extends TypeDef, - TValue extends Presence -> = TValue extends 'required' - ? Exclude | 'required' - : Exclude | 'optional'; - export type ResolveDefault = undefined> = | TType | TDefault; export type ResolveInput< TType, - Def extends TypeDef, + TNull = 'unset', TDefault = undefined -> = Def extends 'nullable' +> = TNull extends 'nullable' ? TType | TDefault | null : StrictNonNullable; export type ResolveOutput< TType, - Def extends TypeDef, + TNull = 'unset', + TPresent = 'unset', TDefault = undefined -> = Pluck extends never - ? ResolveInput // - : NonNullable>; - -export type TypedSchema< - _TType = any, - _TDef extends TypeDef = any, - _TDefault = any -> = { +> = TPresent extends 'required' + ? NonNullable> + : ResolveInput; // + +export type TypedSchema = { __inputType: any; __outputType: any; - cast(...args: any[]): any; - validateSync(...args: any[]): any; + // cast(...args: any[]): any; + // validateSync(...args: any[]): any; }; // declare class Schema {} @@ -65,25 +45,6 @@ export type TypeOf = TSchema['__inputType']; export type Asserts = TSchema['__outputType']; -export type MergePresence = Pluck< - U, - Presence -> extends never - ? Extract | Extract - : U; - -export type MergeNullability = Pluck< - U, - Nullability -> extends never - ? Extract | Extract - : U; - -export type MergeDef = - | MergeNullability - | MergePresence - | ''; - // export type Concat = TSchema extends TypedSchema // ? // : never diff --git a/test/mixed.js b/test/mixed.js index 51f2150ac..65d188a73 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -675,6 +675,18 @@ describe('Mixed Types ', () => { await inst.isValid('a').should.become(true); }); + it('concat should maintain explicit nullability', async function () { + let inst = string().nullable().concat(string().default('hi')); + + await inst.isValid(null).should.become(true); + }); + + it('concat should maintain explicit presence', async function () { + let inst = string().required().concat(string()); + + await inst.isValid(undefined).should.become(false); + }); + it('gives whitelist precedence to second in concat', async function () { let inst = string() .oneOf(['a', 'b', 'c']) diff --git a/test/types.ts b/test/types.ts index 4cbad55c1..193f98a1d 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,17 +1,8 @@ /* eslint-disable no-unused-expressions */ // import { Asserts } from '../src/mixed'; -import { array, string, object, mixed, number, ref } from '../src'; +import { array, string, object, mixed, number, ref, lazy } from '../src'; import { AssertsShape, DefaultFromShape, TypeOfShape } from '../src/object'; -import { - ResolveInput, - ResolveOutput, - MergeDef, - MergePresence, - MergeNullability, - TypeDef, - SetPresence, - SetNullability, -} from '../src/util/types'; +import { ResolveInput, ResolveOutput } from '../src/util/types'; // let schema = object({ // str: string().nullable(), @@ -30,32 +21,26 @@ string().required().nullable(); // $ExpectType string | undefined type _d1 = ResolveInput; + // type dd = GetNull> extends 'nullable' + // ? true + // : false; // $ExpectType string | null | undefined - type _d2 = ResolveInput; + type _d2 = ResolveInput; // $ExpectType string | undefined - type _d3 = ResolveInput< - string, - SetNullability, 'nonnullable'> - >; + type _d3 = ResolveInput; // $ExpectType string - type _d4 = ResolveOutput< - string, - SetPresence, 'required'> - >; + type _d4 = ResolveOutput; // $ExpectType string - type _d5 = ResolveOutput< - string, - SetPresence, 'required'> - >; + type _d5 = ResolveOutput; // $ExpectType string | null type _i1 = ResolveInput; // $ExpectType string | undefined - type _i2 = ResolveInput; + type _i2 = ResolveInput; // $ExpectType string type _i3 = ResolveInput; @@ -67,84 +52,32 @@ string().required().nullable(); type _i5 = ResolveInput; // $ExpectType string | null | undefined - type _i6 = ResolveInput; + type _i6 = ResolveInput; // $ExpectType string - type _o1 = ResolveOutput; + type _o1 = ResolveOutput; // $ExpectType string | undefined - type _o2 = ResolveOutput; + type _o2 = ResolveOutput; // $ExpectType string - type _o3 = ResolveOutput; + type _o3 = ResolveOutput; // $ExpectType string - type _o4 = ResolveOutput; + type _o4 = ResolveOutput; // $ExpectType string - type _o5 = ResolveOutput; + type _o5 = ResolveOutput; // $ExpectType string | null | undefined - type _o6 = ResolveOutput; + type _o6 = ResolveOutput; // $ExpectType string | null - type _o7 = ResolveOutput; + type _o7 = ResolveOutput; // $ExpectError number is not a MaybeString - type _e1 = ResolveOutput; + type _e1 = ResolveOutput; - // $ExpectType "nullable" - type _n1 = MergeNullability<'', 'nullable'>; - - // $ExpectType "nullable" - type _n2 = MergeNullability<'nonnullable', 'nullable'>; - - // $ExpectType "" | "nonnullable" - type _n3 = MergeNullability<'nullable' | 'optional', '' | 'nonnullable'>; - - // type v = '' | 'foo' extends 'foo' ? true : false; - - // $ExpectType "required" - type _p1 = MergePresence<'', 'required'>; - - // $ExpectType "required" - type _p2 = MergePresence<'optional', 'required'>; - - // $ExpectType "optional" - type _p3 = MergePresence<'required' | 'nullable', 'optional'>; - - // type M = Pluck< - // T | U, - // 'required' - // > extends never - // ? 'optional' - // : 'required'; - - // $ExpectType "nullable" | "required" - type _m1 = MergeDef<'nullable' | 'optional', 'required'>; - - // $ExpectType "required" - type _m2 = MergeDef<'', 'required'>; - - // $ExpectType "optional" - type _m3 = MergeDef<'required', 'optional'>; - - // $ExpectType "" | "nonnullable" | "required" - type _m4 = MergeDef<'required' | 'nullable', '' | 'nonnullable'>; - - // $ExpectType "" | "nonnullable" | "required" - type _m5 = MergeDef<'' | 'required' | 'nullable', '' | 'nonnullable'>; - - // $ExpectType "" | "nonnullable" | "optional" - type _m6 = MergeDef<'' | 'nullable', '' | 'optional' | 'nonnullable'>; - - // $ExpectType "nullable" - type _m7 = MergeDef<'' | 'nullable', ''>; - - // $ExpectType "" - type _m8 = MergeDef<'', ''>; -} -{ const strRequired = string().required(); // $ExpectType string | undefined @@ -197,6 +130,7 @@ string().required().nullable(); nest: object({ other: string(), }), + lazy: lazy(() => number().required()), }); // const f = obj.cast({}); @@ -214,12 +148,18 @@ string().required().nullable(); // $ExpectType unknown type _i3 = TypeOfShape['ref']; + // $ExpectType number | undefined + type _i33 = TypeOfShape['lazy']; + // $ExpectType number type _i4 = AssertsShape['number']; // $ExpectType string type _i5 = AssertsShape['string']; + // $ExpectType number + type _i6 = AssertsShape['lazy']; + const cast1 = obj.cast({}); // $ExpectType string | undefined @@ -314,3 +254,11 @@ string().required().nullable(); // $ExpectType string a1.nested![0].name; } + +{ + // $ExpectType string | undefined + lazy(() => string()).cast(3); + + // $ExpectType string | number | undefined + lazy((v) => (typeof v === 'string' ? string() : number())).cast(3); +} diff --git a/test/yup.js b/test/yup.js index 45b3b56b2..7ef3cb78d 100644 --- a/test/yup.js +++ b/test/yup.js @@ -1,5 +1,4 @@ import reach, { getIn } from '../src/util/reach'; -import prependDeep from '../src/util/prependDeep'; import { object, array, string, lazy, number, ValidationError } from '../src'; @@ -21,35 +20,6 @@ describe('Yup', function () { (() => string().cast(null, { assert: false })).should.not.throw(); }); - it('should prepend deeply', function () { - var a = { a: 4, c: [4, 5, 3], d: { b: 'hello' }, f: { c: 5 }, g: null }; - var b = { a: 1, b: 'hello', c: [1, 2, 3], d: { a: /hi/ }, e: { b: 5 } }; - - prependDeep(a, b).should.deep.eql({ - a: 4, - b: 'hello', - c: [1, 2, 3, 4, 5, 3], - d: { - a: /hi/, - b: 'hello', - }, - e: { b: 5 }, - f: { c: 5 }, - g: null, - }); - }); - - it('should not prepend needlesly', function () { - var schema = string(); - var spy = sinon.spy(schema, 'concat'); - var a = { schema }; - var b = { schema }; - var c = prependDeep(a, b); - - c.schema.should.equal(schema); - spy.should.not.have.been.called(); - }); - it('should getIn correctly', async () => { let num = number(); let shape = object({ 'num-1': num }); diff --git a/yarn.lock b/yarn.lock index cb8c42fac..03aa64eff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1765,6 +1765,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash@^4.14.165": + version "4.14.165" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f" + integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" From 637eb0168cf028e76c215ed74c2e18323620e2b8 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Fri, 20 Nov 2020 11:17:48 -0500 Subject: [PATCH 13/31] WIP --- src/array.ts | 5 +++-- src/boolean.ts | 2 ++ src/mixed.ts | 13 ++++++++----- src/number.ts | 3 ++- src/object.ts | 2 +- src/string.ts | 7 ++----- src/util/types.ts | 6 +++++- test/types.ts | 13 +++++++++++-- 8 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/array.ts b/src/array.ts index d5ed48cf6..210d19539 100644 --- a/src/array.ts +++ b/src/array.ts @@ -299,11 +299,12 @@ export default interface ArraySchema< > { // concat(schema: any): any; - default[] | null | undefined>( + default( def: TNextDefault | (() => TNextDefault), ): ArraySchema; - defined(): ArraySchema; + defined(): ArraySchema; + required(): ArraySchema; notRequired(): ArraySchema; diff --git a/src/boolean.ts b/src/boolean.ts index dc6e0e635..1ec511e46 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -44,6 +44,8 @@ export default interface BooleanSchema< def: TNextDefault | (() => TNextDefault), ): BooleanSchema; + defined(): BooleanSchema; + required(): BooleanSchema; notRequired(): BooleanSchema; diff --git a/src/mixed.ts b/src/mixed.ts index 19c05cf9d..3dcb01412 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -242,11 +242,11 @@ export default class MixedSchema< return result; } - concat>( + concat( schema: TOther, - ): TOther extends MixedSchema + ): TOther extends MixedSchema ? MixedSchema< - TType, + T, D, N extends Unset ? TNullablity : N, P extends Unset ? TPresence : P @@ -712,7 +712,10 @@ export default class MixedSchema< return next as any; } - notOneOf(enums: Maybe[], message = locale.notOneOf) { + notOneOf( + enums: Maybe[], + message = locale.notOneOf, + ): this { var next = this.clone(); enums.forEach((val) => { next._blacklist.add(val); @@ -764,7 +767,7 @@ export default class MixedSchema< defined( message = locale.defined, - ): MixedSchema { + ): MixedSchema { return this.test({ message, name: 'defined', diff --git a/src/number.ts b/src/number.ts index b8a14410a..36639dba5 100644 --- a/src/number.ts +++ b/src/number.ts @@ -136,11 +136,12 @@ export default interface NumberSchema< TNullablity extends Nullability, TPresence extends Presence > extends MixedSchema { - default(): TDefault; default>( def: TNextDefault | (() => TNextDefault), ): NumberSchema; + defined(): NumberSchema; + required(): NumberSchema; notRequired(): NumberSchema; diff --git a/src/object.ts b/src/object.ts index ebbb0dbad..67ab9a084 100644 --- a/src/object.ts +++ b/src/object.ts @@ -471,7 +471,7 @@ export default interface ObjectSchema< def: TNextDefault | (() => TNextDefault), ): ObjectSchema; - defined(): ObjectSchema; + defined(): ObjectSchema; required(): ObjectSchema; notRequired(): ObjectSchema; diff --git a/src/string.ts b/src/string.ts index d9cbede8b..c4b7a2a90 100644 --- a/src/string.ts +++ b/src/string.ts @@ -206,6 +206,8 @@ export default interface StringSchema< def: TNextDefault | (() => TNextDefault), ): StringSchema; + defined(): StringSchema; + required(): StringSchema; notRequired(): StringSchema; @@ -215,9 +217,4 @@ export default interface StringSchema< nullable( isNullable: false, ): StringSchema; - - // oneOf( - // enums: U[], - // message: MixedLocale['oneOf'], - // ): StringSchema; } diff --git a/src/util/types.ts b/src/util/types.ts index 08718e5a8..a7efc1362 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -4,11 +4,13 @@ import Schema from '../Schema'; import { Maybe } from '../types'; export type Unset = 'unset'; -export type Presence = 'required' | 'optional' | Unset; +export type Presence = 'required' | 'defined' | 'optional' | Unset; export type Nullability = 'nullable' | 'nonnullable' | Unset; type StrictNonNullable = T extends null ? never : T; +type Defined = T extends undefined ? never : T; + export type TypeDef = Nullability | Presence | ''; export type ResolveDefault = undefined> = @@ -30,6 +32,8 @@ export type ResolveOutput< TDefault = undefined > = TPresent extends 'required' ? NonNullable> + : TPresent extends 'defined' + ? Defined> : ResolveInput; // export type TypedSchema = { diff --git a/test/types.ts b/test/types.ts index d33fc6d1d..a52d0dfe8 100644 --- a/test/types.ts +++ b/test/types.ts @@ -57,6 +57,9 @@ string().required().nullable(); // $ExpectType string | undefined type _o2 = ResolveOutput; + // $ExpectType string + type _o22 = ResolveOutput; + // $ExpectType string type _o3 = ResolveOutput; @@ -72,6 +75,12 @@ string().required().nullable(); // $ExpectType string | null type _o7 = ResolveOutput; + // $ExpectType string | null + type _o8 = ResolveOutput; + + // $ExpectType string + type _o9 = ResolveOutput; + // $ExpectError number is not a MaybeString type _e1 = ResolveOutput; @@ -221,7 +230,7 @@ string().required().nullable(); .getDefault(); // $ExpectType string[] | (string | null)[] | null - array(string().nullable()) + array(string().nullable().default('')) .nullable() .default(() => [] as string[]) .validateSync(null); @@ -276,5 +285,5 @@ string().required().nullable(); const optional = mixed().required().concat(_f).validateSync(''); // $ExpectType MixedSchema - string().oneOf(['hi' as const, 1]); + // string().oneOf(['hi' as const]); } From fa2447afb6e5438467d1f9960171e6d05182a6e7 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Fri, 20 Nov 2020 14:34:17 -0500 Subject: [PATCH 14/31] WIP --- src/Lazy.ts | 5 ++-- src/array.ts | 26 +++++++++++---------- src/boolean.ts | 24 ++++++++++++-------- src/date.ts | 44 ++++++++++++++++++++++++++++++++---- src/index.ts | 24 ++++---------------- src/mixed.ts | 17 ++++++-------- src/number.ts | 16 ++++++++----- src/object.ts | 40 ++++++++++++++++++++++---------- src/string.ts | 15 +++++++----- src/types.ts | 3 ++- src/util/createValidation.ts | 4 ++-- test-setup.js | 2 +- test/tsconfig.json | 1 + test/types.ts | 10 +++++++- tsconfig.json | 2 +- 15 files changed, 143 insertions(+), 90 deletions(-) diff --git a/src/Lazy.ts b/src/Lazy.ts index a3ed2be17..5870cb143 100644 --- a/src/Lazy.ts +++ b/src/Lazy.ts @@ -1,8 +1,7 @@ import isSchema from './util/isSchema'; import Schema, { CastOptions } from './Schema'; -import { Callback, ValidateOptions } from './types'; -import { ResolveOptions } from './Condition'; -import { ResolveInput, TypedSchema } from './util/types'; +import type { Callback, ValidateOptions } from './types'; +import type { ResolveOptions } from './Condition'; import type MixedSchema from './mixed'; export type LazyBuilder = ( diff --git a/src/array.ts b/src/array.ts index 210d19539..15df31669 100644 --- a/src/array.ts +++ b/src/array.ts @@ -2,12 +2,12 @@ import isAbsent from './util/isAbsent'; import isSchema from './util/isSchema'; import printValue from './util/printValue'; import MixedSchema, { AnyMixed } from './mixed'; -import { array as locale } from './locale'; +import { array as locale, MixedLocale } from './locale'; import runTests, { RunTest } from './util/runTests'; -import { SchemaInnerTypeDescription, SchemaSpec } from './Schema'; -import { InternalOptions, Callback, Message, Maybe } from './types'; +import type { SchemaInnerTypeDescription, SchemaSpec } from './Schema'; +import type { InternalOptions, Callback, Message, Maybe } from './types'; import ValidationError from './ValidationError'; -import Reference from './Reference'; +import type Reference from './Reference'; import { Asserts, Nullability, @@ -43,8 +43,6 @@ export default class ArraySchema< ResolveInput[], TNullablity, TDefault>, ResolveOutput[], TNullablity, TPresence, TDefault> > { - // - innerType: T | undefined; constructor(type?: T) { @@ -185,7 +183,7 @@ export default class ArraySchema< : never; concat(schema: any): any; concat(schema: any): any { - let next = super.concat(schema); + let next = super.concat(schema) as this; next.innerType = this.innerType; @@ -224,7 +222,7 @@ export default class ArraySchema< name: 'length', exclusive: true, params: { length }, - test(value: Maybe) { + test(value) { return isAbsent(value) || value.length === this.resolve(length); }, }); @@ -239,7 +237,7 @@ export default class ArraySchema< exclusive: true, params: { min }, // FIXME(ts): Array - test(value: any[]) { + test(value) { return isAbsent(value) || value.length >= this.resolve(min); }, }); @@ -253,7 +251,7 @@ export default class ArraySchema< exclusive: true, params: { max }, // FIXME(ts): Array - test(value: any[]) { + test(value) { return isAbsent(value) || value.length <= this.resolve(max); }, }); @@ -303,9 +301,13 @@ export default interface ArraySchema< def: TNextDefault | (() => TNextDefault), ): ArraySchema; - defined(): ArraySchema; + defined( + msg?: MixedLocale['defined'], + ): ArraySchema; - required(): ArraySchema; + required( + msg?: MixedLocale['required'], + ): ArraySchema; notRequired(): ArraySchema; nullable(isNullable?: true): ArraySchema; diff --git a/src/boolean.ts b/src/boolean.ts index 1ec511e46..6a1c6c91a 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -1,6 +1,7 @@ +import type { MixedLocale } from './locale'; import MixedSchema from './mixed'; -import { Maybe } from './types'; -import { Nullability, Presence } from './util/types'; +import type { Maybe } from './types'; +import type { Nullability, Presence, Unset } from './util/types'; export function create() { return new BooleanSchema(); @@ -8,9 +9,9 @@ export function create() { export default class BooleanSchema< TType extends boolean, - TDefault extends Maybe, - TNullablity extends Nullability, - TPresence extends Presence + TDefault extends Maybe = undefined, + TNullablity extends Nullability = Unset, + TPresence extends Presence = Unset > extends MixedSchema { constructor() { super({ type: 'boolean' }); @@ -18,8 +19,8 @@ export default class BooleanSchema< this.withMutation(() => { this.transform(function (value) { if (!this.isType(value)) { - if (/^(true|1)$/i.test(value)) return true; - if (/^(false|0)$/i.test(value)) return false; + if (/^(true|1)$/i.test(String(value))) return true; + if (/^(false|0)$/i.test(String(value))) return false; } return value; }); @@ -39,14 +40,17 @@ export default interface BooleanSchema< TNullablity extends Nullability, TPresence extends Presence > extends MixedSchema { - default(): TDefault; default>( def: TNextDefault | (() => TNextDefault), ): BooleanSchema; - defined(): BooleanSchema; + defined( + msg?: MixedLocale['defined'], + ): BooleanSchema; - required(): BooleanSchema; + required( + msg?: MixedLocale['required'], + ): BooleanSchema; notRequired(): BooleanSchema; nullable( diff --git a/src/date.ts b/src/date.ts index faf0dd90a..2333f22b7 100644 --- a/src/date.ts +++ b/src/date.ts @@ -1,9 +1,11 @@ import MixedSchema from './mixed'; // @ts-ignore import isoParse from './util/isodate'; -import { date as locale } from './locale'; +import { date as locale, MixedLocale } from './locale'; import isAbsent from './util/isAbsent'; import Ref from './Reference'; +import type { Maybe } from './types'; +import type { Nullability, Presence, Unset } from './util/types'; let invalidDate = new Date(''); @@ -14,7 +16,12 @@ export function create() { return new DateSchema(); } -export default class DateSchema extends MixedSchema { +export default class DateSchema< + TType extends Date, + TDefault extends Maybe = undefined, + TNullablity extends Nullability = Unset, + TPresence extends Presence = Unset +> extends MixedSchema { constructor() { super({ type: 'date' }); @@ -30,7 +37,7 @@ export default class DateSchema extends MixedSchema { }); } - protected _typeCheck(v: any): v is Date { + protected _typeCheck(v: any): v is TType { return isDate(v) && !isNaN(v.getTime()); } @@ -50,7 +57,7 @@ export default class DateSchema extends MixedSchema { name: 'min', exclusive: true, params: { min }, - test(value: Date) { + test(value) { return isAbsent(value) || value >= this.resolve(limit); }, }); @@ -72,9 +79,36 @@ export default class DateSchema extends MixedSchema { name: 'max', exclusive: true, params: { max }, - test(value: Date) { + test(value) { return isAbsent(value) || value <= this.resolve(limit); }, }); } } + +export default interface DateSchema< + TType extends Date, + TDefault extends Maybe, + TNullablity extends Nullability, + TPresence extends Presence +> extends MixedSchema { + default>( + def: TNextDefault | (() => TNextDefault), + ): DateSchema; + + defined( + msg?: MixedLocale['defined'], + ): DateSchema; + + required( + msg?: MixedLocale['required'], + ): DateSchema; + notRequired(): DateSchema; + + nullable( + isNullable?: true, + ): DateSchema; + nullable( + isNullable: false, + ): DateSchema; +} diff --git a/src/index.ts b/src/index.ts index eefb22b7f..cfb0319e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,12 @@ -import MixedSchema, { - create as mixedCreate, - SchemaSpec, -} from './mixed'; +import MixedSchema, { create as mixedCreate, SchemaSpec } from './mixed'; import BoolSchema, { create as boolCreate } from './boolean'; import StringSchema, { create as stringCreate } from './string'; import NumberSchema, { create as numberCreate } from './number'; import DateSchema, { create as dateCreate } from './date'; -import ObjectSchema, { create as objectCreate, AssertsShape } from './object'; +import ObjectSchema, { create as objectCreate } from './object'; import ArraySchema, { create as arrayCreate } from './array'; -import Ref, { create as refCreate } from './Reference'; -import Lazy, { create as lazyCreate } from './Lazy'; +import { create as refCreate } from './Reference'; +import { create as lazyCreate } from './Lazy'; import ValidationError from './ValidationError'; import reach from './util/reach'; import isSchema from './util/isSchema'; @@ -54,16 +51,3 @@ export { ObjectSchema, ArraySchema, }; - -// type TypeOf = T extends { spec: infer TSpec } -// ? TSpec extends SchemaSpec -// ? ResolveCast -// : never -// : never; - -// type Asserts = TypeOf & -// (T extends { spec: infer TSpec } -// ? TSpec extends SchemaSpec -// ? ResolveRequired, TSpec> -// : never -// : never); diff --git a/src/mixed.ts b/src/mixed.ts index 3dcb01412..a09729dae 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -28,7 +28,7 @@ import Schema, { SchemaDescription, } from './Schema'; import { ValidationError } from '.'; -import { +import type { Nullability, Presence, ResolveInput, @@ -335,7 +335,7 @@ export default class MixedSchema< * @param {*=} options.parent * @param {*=} options.context */ - cast(value: any, options: CastOptions = {}): this['__inputType'] { + cast(value: any, options: CastOptions = {}): TInput { let resolvedSchema = this.resolve({ value, ...options, @@ -463,10 +463,7 @@ export default class MixedSchema< ); } - validateSync( - value: any, - options: ValidateOptions = {}, - ): this['__outputType'] { + validateSync(value: any, options: ValidateOptions = {}): TOutput { let schema = this.resolve({ ...options, value }); let result: any; @@ -589,10 +586,10 @@ export default class MixedSchema< * If an exclusive test is added to a schema with non-exclusive tests of the same name * the previous tests are removed and further tests of the same name will replace each other. */ - test(options: TestConfig): this; - test(test: TestFunction): this; - test(name: string, test: TestFunction): this; - test(name: string, message: Message, test: TestFunction): this; + test(options: TestConfig): this; + test(test: TestFunction): this; + test(name: string, test: TestFunction): this; + test(name: string, message: Message, test: TestFunction): this; test(...args: any[]) { let opts: TestConfig; diff --git a/src/number.ts b/src/number.ts index 36639dba5..457e35c10 100644 --- a/src/number.ts +++ b/src/number.ts @@ -1,9 +1,9 @@ import MixedSchema from './mixed'; -import { number as locale } from './locale'; +import { MixedLocale, number as locale } from './locale'; import isAbsent from './util/isAbsent'; -import { Maybe } from './types'; -import Reference from './Reference'; -import { Nullability, Presence, Unset } from './util/types'; +import type { Maybe } from './types'; +import type Reference from './Reference'; +import type { Nullability, Presence, Unset } from './util/types'; let isNaN = (value: Maybe) => value != +value!; @@ -140,9 +140,13 @@ export default interface NumberSchema< def: TNextDefault | (() => TNextDefault), ): NumberSchema; - defined(): NumberSchema; + defined( + msg?: MixedLocale['defined'], + ): NumberSchema; - required(): NumberSchema; + required( + msg?: MixedLocale['required'], + ): NumberSchema; notRequired(): NumberSchema; nullable( diff --git a/src/object.ts b/src/object.ts index 67ab9a084..4768a1bb8 100644 --- a/src/object.ts +++ b/src/object.ts @@ -6,15 +6,14 @@ import mapValues from 'lodash/mapValues'; import { getter } from 'property-expr'; import MixedSchema, { AnyMixed, SchemaSpec } from './mixed'; -import { object as locale, string } from './locale'; +import { MixedLocale, object as locale } from './locale'; import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; -import Schema, { CastOptions, SchemaObjectDescription } from './Schema'; +import { SchemaObjectDescription } from './Schema'; import { InternalOptions, Callback, Maybe } from './types'; import ValidationError from './ValidationError'; -import isSchema from './util/isSchema'; -import { +import type { ResolveInput, ResolveOutput, Nullability, @@ -22,7 +21,7 @@ import { Unset, TypedSchema, } from './util/types'; -import Reference from './Reference'; +import type Reference from './Reference'; import Lazy, { LazyType } from './Lazy'; let isObject = (obj: any): obj is Record => @@ -256,6 +255,7 @@ export default class ObjectSchema< value[key], { ...opts, + // @ts-ignore path, from, // inner fields are always strict: @@ -298,22 +298,34 @@ export default class ObjectSchema< return next; } - concat(schema: ObjectSchema): ObjectSchema; + concat>( + schema: TOther, + ): TOther extends ObjectSchema + ? ObjectSchema< + TShape & T, + D & TDefault, + N extends Unset ? TNullablity : N, + P extends Unset ? TPresence : P + > + : never; concat(schema: AnyMixed): AnyMixed; - concat(schema: ObjectSchema): ObjectSchema { - let next = super.concat(schema) as ObjectSchema; + concat(schema: any): any { + let next = super.concat(schema) as any; let nextFields = next.fields; for (let [field, schemaOrRef] of Object.entries(this.fields)) { const target = nextFields[field]; if (target === undefined) { nextFields[field] = schemaOrRef; - } else if (isSchema(target) && isSchema(schemaOrRef)) { + } else if ( + target instanceof MixedSchema && + schemaOrRef instanceof MixedSchema + ) { nextFields[field] = schemaOrRef.concat(target); } } - return next.withMutation((next) => next.shape(nextFields)); + return next.withMutation((next: any) => next.shape(nextFields)); } protected _getDefault() { @@ -471,8 +483,12 @@ export default interface ObjectSchema< def: TNextDefault | (() => TNextDefault), ): ObjectSchema; - defined(): ObjectSchema; - required(): ObjectSchema; + defined( + msg?: MixedLocale['defined'], + ): ObjectSchema; + required( + msg?: MixedLocale['required'], + ): ObjectSchema; notRequired(): ObjectSchema; nullable( diff --git a/src/string.ts b/src/string.ts index c4b7a2a90..7ccc39b89 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,9 +1,9 @@ import MixedSchema from './mixed'; import { MixedLocale, string as locale } from './locale'; import isAbsent from './util/isAbsent'; -import Reference from './Reference'; -import { Message, Maybe } from './types'; -import { Nullability, Presence, Unset } from './util/types'; +import type Reference from './Reference'; +import type { Message, Maybe } from './types'; +import type { Nullability, Presence, Unset } from './util/types'; // eslint-disable-next-line let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; @@ -201,14 +201,17 @@ export default interface StringSchema< TNullablity extends Nullability, TPresence extends Presence > extends MixedSchema { - default(): TDefault; default>( def: TNextDefault | (() => TNextDefault), ): StringSchema; - defined(): StringSchema; + defined( + msg?: MixedLocale['defined'], + ): StringSchema; - required(): StringSchema; + required( + msg?: MixedLocale['required'], + ): StringSchema; notRequired(): StringSchema; nullable( diff --git a/src/types.ts b/src/types.ts index a5eae03db..0cea84140 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,8 +1,9 @@ +import type { AnyMixed } from './mixed'; import type Schema from './Schema'; export type Callback = (err: Error | null, value?: T) => void; -export type TransformFunction = ( +export type TransformFunction = ( this: T, value: any, originalValue: any, diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts index 530857e31..19e047ee9 100644 --- a/src/util/createValidation.ts +++ b/src/util/createValidation.ts @@ -44,10 +44,10 @@ export type TestOptions = { sync?: boolean; }; -export type TestConfig = { +export type TestConfig = { name?: string; message?: Message; - test: TestFunction; + test: TestFunction; params?: ExtraParams; exclusive?: boolean; }; diff --git a/test-setup.js b/test-setup.js index 99eb04c94..533818cc6 100644 --- a/test-setup.js +++ b/test-setup.js @@ -20,7 +20,7 @@ Object.defineProperty( global.TestHelpers = require('./test/helpers'); if (global.YUP_USE_SYNC) { - const MixedSchema = require('./src/mixed'); // eslint-disable-line global-require + const { MixedSchema } = require('./src'); // eslint-disable-line global-require const { validate } = MixedSchema.prototype; diff --git a/test/tsconfig.json b/test/tsconfig.json index 55b810895..4b5b346ca 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "noEmit": true, "types": ["node", "jest"], "rootDir": "../" }, diff --git a/test/types.ts b/test/types.ts index a52d0dfe8..4445c8c28 100644 --- a/test/types.ts +++ b/test/types.ts @@ -282,7 +282,15 @@ string().required().nullable(); let _f = mixed().notRequired(); // $ExpectType string | undefined - const optional = mixed().required().concat(_f).validateSync(''); + const _oo = mixed().required().concat(_f).validateSync(''); + + const _o = object({ + str: string(), + }).concat( + object({ + name: string(), + }), + ); // $ExpectType MixedSchema // string().oneOf(['hi' as const]); diff --git a/tsconfig.json b/tsconfig.json index 1b619a992..53149fb50 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@4c/tsconfig/web.json", + "extends": "@4c/tsconfig/web", "compilerOptions": { "allowJs": true, "rootDir": "src" From b1efc7348d728206d5a58c91c32ec7a59343a57c Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Fri, 20 Nov 2020 14:44:28 -0500 Subject: [PATCH 15/31] WIP --- README.md | 9 +- src/index.ts | 16 + src/util/types.ts | 57 ---- types/index.d.ts | 823 ---------------------------------------------- 4 files changed, 22 insertions(+), 883 deletions(-) delete mode 100644 types/index.d.ts diff --git a/README.md b/README.md index 991112b00..255f23cb6 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane + - [Install](#install) - [Usage](#usage) - [Using a custom locale dictionary](#using-a-custom-locale-dictionary) @@ -37,10 +38,10 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane - [`mixed.strip(stripField: boolean = true): Schema`](#mixedstripstripfield-boolean--true-schema) - [`mixed.withMutation(builder: (current: Schema) => void): void`](#mixedwithmutationbuilder-current-schema--void-void) - [`mixed.default(value: any): Schema`](#mixeddefaultvalue-any-schema) - - [`mixed.default(): Any`](#mixeddefault-any) + - [`mixed.getDefault(options?: object): Any`](#mixedgetdefaultoptions-object-any) - [`mixed.nullable(isNullable: boolean = true): Schema`](#mixednullableisnullable-boolean--true-schema) - [`mixed.required(message?: string | function): Schema`](#mixedrequiredmessage-string--function-schema) - - [`mixed.notRequired(): Schema`](#mixednotrequired-schema) + - [`mixed.notRequired(): Schema` Alias: `optional()`](#mixednotrequired-schema-alias-optional) - [`mixed.defined(): Schema`](#mixeddefined-schema) - [`mixed.typeError(message: string): Schema`](#mixedtypeerrormessage-string-schema) - [`mixed.oneOf(arrayOfValues: Array, message?: string | function): Schema` Alias: `equals`](#mixedoneofarrayofvalues-arrayany-message-string--function-schema-alias-equals) @@ -79,7 +80,7 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane - [`date.max(limit: Date | string | Ref, message?: string | function): Schema`](#datemaxlimit-date--string--ref-message-string--function-schema) - [array](#array) - [`array.of(type: Schema): Schema`](#arrayoftype-schema-schema) - - [`array.required(message?: string | function): Schema`](#arrayrequiredmessage-string--function-schema) + - [`array.length(length: number | Ref, message?: string | function): Schema`](#arraylengthlength-number--ref-message-string--function-schema) - [`array.min(limit: number | Ref, message?: string | function): Schema`](#arrayminlimit-number--ref-message-string--function-schema) - [`array.max(limit: number | Ref, message?: string | function): Schema`](#arraymaxlimit-number--ref-message-string--function-schema) - [`array.ensure(): Schema`](#arrayensure-schema) @@ -87,6 +88,8 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane - [object](#object) - [Object schema defaults](#object-schema-defaults) - [`object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema`](#objectshapefields-object-nosortedges-arraystring-string-schema) + - [`object.pick(keys: string[]): Schema`](#objectpickkeys-string-schema) + - [`object.omit(keys: string[]): Schema`](#objectomitkeys-string-schema) - [`object.from(fromKey: string, toKey: string, alias: boolean = false): Schema`](#objectfromfromkey-string-tokey-string-alias-boolean--false-schema) - [`object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema`](#objectnounknownonlyknownkeys-boolean--true-message-string--function-schema) - [`object.camelCase(): Schema`](#objectcamelcase-schema) diff --git a/src/index.ts b/src/index.ts index cfb0319e1..291c69e8f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,13 @@ import ValidationError from './ValidationError'; import reach from './util/reach'; import isSchema from './util/isSchema'; import setLocale from './setLocale'; +import type { + TypeOf, + Asserts, + Nullability, + Presence, + Unset, +} from './util/types'; function addMethod(schemaType: any, name: string, fn: any) { if (!schemaType || !isSchema(schemaType.prototype)) @@ -24,6 +31,15 @@ function addMethod(schemaType: any, name: string, fn: any) { schemaType.prototype[name] = fn; } +export type { + TypeOf, + Asserts, + Nullability, + Presence, + Unset, + Asserts as InferType, +}; + export { mixedCreate as mixed, boolCreate as bool, diff --git a/src/util/types.ts b/src/util/types.ts index a7efc1362..584f6f37b 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -1,6 +1,3 @@ -// type UNSET = { 1: '@@UNSET_DEFAULT' }; - -import Schema from '../Schema'; import { Maybe } from '../types'; export type Unset = 'unset'; @@ -39,62 +36,8 @@ export type ResolveOutput< export type TypedSchema = { __inputType: any; __outputType: any; - // cast(...args: any[]): any; - // validateSync(...args: any[]): any; }; -// declare class Schema {} - export type TypeOf = TSchema['__inputType']; export type Asserts = TSchema['__outputType']; - -// export type Concat = TSchema extends TypedSchema -// ? -// : never -// type ResolveNullable< -// TType, -// TSpec extends SchemaSpec -// > = TSpec['nullable'] extends true ? TType | null : TType; - -// type ResolveDefault = TSpec extends SchemaSpec< -// infer Default -// > -// ? Default extends UNSET -// ? TType -// : Default extends undefined -// ? TType & undefined -// : Exclude -// : never; - -// // type TypeOfShape> = { -// // [K in keyof Shape]: ReturnType; -// // }; - -// export type ResolveCast = ResolveDefault< -// ResolveNullable, -// TSpec -// >; - -// export type ResolveRequired< -// TType, -// TSpec extends SchemaSpec -// > = TSpec['required'] extends true ? NonNullable : TType; - -// export type TypedSchema = { _tsType: any; _tsValidate: any }; - -// // type Keys> = { fields: TShape }; - -// // type CastChildren = T extends Keys ? { } - -// export type TypeOf = T extends { spec: infer TSpec } -// ? TSpec extends SchemaSpec -// ? ResolveCast -// : never -// : never; - -// export type Asserts = T extends { spec: infer TSpec } -// ? TSpec extends SchemaSpec -// ? ResolveRequired, TSpec> -// : never -// : never; diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index eb52f1609..000000000 --- a/types/index.d.ts +++ /dev/null @@ -1,823 +0,0 @@ -// // Type definitions for yup 0.29 -// // Project: https://github.com/jquense/yup -// // Definitions by: Dominik Hardtke , -// // Vladyslav Tserman , -// // Moreton Bay Regional Council , -// // Sindre Seppola -// // Yash Kulshrestha -// // Vincent Pizzo -// // Robert Bullen -// // Yusuke Sato -// // Desmond Koh -// // Maurice de Beijer -// // Kalley Powell -// // Elías García -// // Ian Sanders -// // Jay Fong -// // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// // TypeScript Version: 3.6 - -// export function reach( -// schema: Schema, -// path: string, -// value?: any, -// context?: any, -// ): Schema; -// export function addMethod>( -// schemaCtor: AnySchemaConstructor, -// name: string, -// method: (this: T, ...args: any[]) => T, -// ): void; -// export function ref(path: string, options?: { contextPrefix: string }): Ref; -// export function lazy(fn: (value: T) => Schema): Lazy; -// export function setLocale(customLocale: LocaleObject): void; -// export function isSchema(obj: any): obj is Schema; - -// export const mixed: MixedSchemaConstructor; -// export const string: StringSchemaConstructor; -// export const number: NumberSchemaConstructor; -// export const boolean: BooleanSchemaConstructor; -// export const bool: BooleanSchemaConstructor; -// export const date: DateSchemaConstructor; -// export const array: ArraySchemaConstructor; -// export const object: ObjectSchemaConstructor; - -// export type AnySchemaConstructor = -// | MixedSchemaConstructor -// | StringSchemaConstructor -// | NumberSchemaConstructor -// | BooleanSchemaConstructor -// | DateSchemaConstructor -// | ArraySchemaConstructor -// | ObjectSchemaConstructor; - -// export type TestOptionsMessage< -// Extra extends Record = {}, -// R = any -// > = string | ((params: Extra & Partial) => R); - -// export interface Schema { -// type: string; -// clone(): this; -// label(label: string): this; -// meta(metadata: any): this; -// meta(): any; -// describe(): SchemaDescription; -// concat(schema: this): this; -// validate(value: any, options?: ValidateOptions): Promise; -// validateSync(value: any, options?: ValidateOptions): T; -// validateAt(path: string, value: T, options?: ValidateOptions): Promise; -// validateSyncAt(path: string, value: T, options?: ValidateOptions): T; -// isValid(value: any, options?: any): Promise; -// isValidSync(value: any, options?: any): value is T; -// cast(value?: any, options?: any): T; -// isType(value: any): value is T; -// strict(isStrict: boolean): this; -// strip(strip: boolean): this; -// withMutation(fn: (current: this) => void): void; -// default(value: any): this; -// default(): T; -// typeError(message?: TestOptionsMessage): this; -// notOneOf(arrayOfValues: any[], message?: MixedLocale['notOneOf']): this; -// when(keys: string | any[], builder: WhenOptions): this; -// transform(fn: TransformFunction): this; -// } - -// // Note: Using `{} | null | undefined` allows excluding `null` & `undefined` -// // whereas using `any` as the default type would mean that `nullable(false)`, -// // `defined`, and `required` would all have no effect. - -// export interface MixedSchemaConstructor { -// // tslint:disable-next-line:no-unnecessary-generics -// (): MixedSchema; -// // tslint:disable-next-line:no-unnecessary-generics -// new (options?: { -// type?: string; -// [key: string]: any; -// }): MixedSchema; -// } - -// export interface MixedSchema -// extends Schema { -// nullable(isNullable?: true): MixedSchema; -// nullable(isNullable: false): MixedSchema>; -// nullable(isNullable?: boolean): MixedSchema; -// required( -// message?: TestOptionsMessage, -// ): MixedSchema>; -// defined(): MixedSchema>; -// notRequired(): MixedSchema; -// optional(): MixedSchema; -// concat(schema: this): this; -// concat(schema: Schema): MixedSchema; -// oneOf( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): MixedSchema>; -// equals( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): MixedSchema>; -// test( -// name: string, -// message: TestOptionsMessage, -// test: AssertingTestFunction, -// ): MixedSchema; -// test(name: string, message: TestOptionsMessage, test: TestFunction): this; -// test( -// options: AssertingTestOptions>, -// ): MixedSchema; -// test(options: TestOptions>): this; -// } - -// export interface StringSchemaConstructor { -// (): StringSchema; -// new (): StringSchema; -// } - -// export interface StringSchema< -// T extends string | null | undefined = string | undefined -// > extends Schema { -// length( -// limit: number | Ref, -// message?: StringLocale['length'], -// ): StringSchema; -// min(limit: number | Ref, message?: StringLocale['min']): StringSchema; -// max(limit: number | Ref, message?: StringLocale['max']): StringSchema; -// matches( -// regex: RegExp, -// messageOrOptions?: -// | StringLocale['matches'] -// | { message?: StringLocale['matches']; excludeEmptyString?: boolean }, -// ): StringSchema; -// email(message?: StringLocale['email']): StringSchema; -// url(message?: StringLocale['url']): StringSchema; -// uuid(message?: StringLocale['uuid']): StringSchema; -// ensure(): StringSchema; -// trim(message?: StringLocale['trim']): StringSchema; -// lowercase(message?: StringLocale['lowercase']): StringSchema; -// uppercase(message?: StringLocale['uppercase']): StringSchema; -// nullable(isNullable?: true): StringSchema; -// nullable(isNullable: false): StringSchema>; -// nullable(isNullable?: boolean): StringSchema; -// required( -// message?: TestOptionsMessage, -// ): StringSchema>; -// defined(): StringSchema>; -// notRequired(): StringSchema; -// oneOf( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): StringSchema>; -// equals( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): StringSchema>; -// /* -// All TestFunction generics are intentionally T with (undefined | null) as previous .required / .defined / .nullable -// will narrow out those types, and tests run for (undefined | null) even if they're not allowed. -// */ -// test( -// name: string, -// message: TestOptionsMessage, -// test: TestFunction, -// ): this; -// test( -// name: string, -// message: TestOptionsMessage, -// test: AssertingTestFunction, -// ): StringSchema; -// test( -// options: AssertingTestOptions>, -// ): StringSchema; -// test(options: TestOptions>): this; -// optional(): StringSchema; -// } - -// export interface NumberSchemaConstructor { -// (): NumberSchema; -// new (): NumberSchema; -// } - -// export interface NumberSchema< -// T extends number | null | undefined = number | undefined -// > extends Schema { -// min(limit: number | Ref, message?: NumberLocale['min']): NumberSchema; -// max(limit: number | Ref, message?: NumberLocale['max']): NumberSchema; -// lessThan( -// limit: number | Ref, -// message?: NumberLocale['lessThan'], -// ): NumberSchema; -// moreThan( -// limit: number | Ref, -// message?: NumberLocale['moreThan'], -// ): NumberSchema; -// positive(message?: NumberLocale['positive']): NumberSchema; -// negative(message?: NumberLocale['negative']): NumberSchema; -// integer(message?: NumberLocale['integer']): NumberSchema; -// truncate(): NumberSchema; -// round(type: 'floor' | 'ceil' | 'trunc' | 'round'): NumberSchema; -// nullable(isNullable?: true): NumberSchema; -// nullable(isNullable: false): NumberSchema>; -// nullable(isNullable?: boolean): NumberSchema; -// required( -// message?: TestOptionsMessage, -// ): NumberSchema>; -// defined(): NumberSchema>; -// notRequired(): NumberSchema; -// oneOf( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): NumberSchema>; -// equals( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): NumberSchema>; -// test( -// name: string, -// message: TestOptionsMessage, -// test: TestFunction, -// ): this; -// test( -// name: string, -// message: TestOptionsMessage, -// test: AssertingTestFunction, -// ): NumberSchema; -// test( -// options: AssertingTestOptions>, -// ): NumberSchema; -// test(options: TestOptions>): this; -// optional(): NumberSchema; -// } - -// export interface BooleanSchemaConstructor { -// (): BooleanSchema; -// new (): BooleanSchema; -// } - -// export interface BooleanSchema< -// T extends boolean | null | undefined = boolean | undefined -// > extends Schema { -// nullable(isNullable?: true): BooleanSchema; -// nullable(isNullable: false): BooleanSchema>; -// nullable(isNullable?: boolean): BooleanSchema; -// required( -// message?: TestOptionsMessage, -// ): BooleanSchema>; -// defined(): BooleanSchema>; -// notRequired(): BooleanSchema; -// oneOf( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): BooleanSchema>; -// equals( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): BooleanSchema>; -// test( -// name: string, -// message: TestOptionsMessage, -// test: TestFunction, -// ): this; -// test( -// name: string, -// message: TestOptionsMessage, -// test: AssertingTestFunction, -// ): BooleanSchema; -// test( -// options: AssertingTestOptions>, -// ): BooleanSchema; -// test(options: TestOptions>): this; -// optional(): BooleanSchema; -// } - -// export interface DateSchemaConstructor { -// (): DateSchema; -// new (): DateSchema; -// } - -// export interface DateSchema< -// T extends Date | null | undefined = Date | undefined -// > extends Schema { -// min(limit: Date | string | Ref, message?: DateLocale['min']): DateSchema; -// max(limit: Date | string | Ref, message?: DateLocale['max']): DateSchema; -// nullable(isNullable?: true): DateSchema; -// nullable(isNullable: false): DateSchema>; -// nullable(isNullable?: boolean): DateSchema; -// required( -// message?: TestOptionsMessage, -// ): DateSchema>; -// defined(): DateSchema>; -// notRequired(): DateSchema; -// oneOf( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): DateSchema>; -// equals( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): DateSchema>; -// test( -// name: string, -// message: TestOptionsMessage, -// test: TestFunction, -// ): this; -// test( -// name: string, -// message: TestOptionsMessage, -// test: AssertingTestFunction, -// ): DateSchema; -// test( -// options: AssertingTestOptions>, -// ): DateSchema; -// test(options: TestOptions>): this; -// optional(): DateSchema; -// } - -// export interface ArraySchemaConstructor { -// (schema?: Schema): NotRequiredArraySchema; -// new (): NotRequiredArraySchema<{}>; -// } - -// export interface BasicArraySchema -// extends Schema { -// min(limit: number | Ref, message?: ArrayLocale['min']): this; -// max(limit: number | Ref, message?: ArrayLocale['max']): this; -// ensure(): this; -// compact( -// rejector?: ( -// value: InferredArrayType, -// index: number, -// array: Array>, -// ) => boolean, -// ): this; - -// // This doesn't narrow the type of the schema like the more primitive oneOf calls -// // it would be very complex to do that accurately with the subtypes, and it's not -// // really worth it because the oneOf check is a shallow (==) comparison so it rarely -// // applies to arrays anyway. -// oneOf( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): this; -// equals( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): this; -// test( -// name: string, -// message: TestOptionsMessage, -// test: TestFunction, -// ): this; -// test(options: TestOptions>): this; -// innerType: Schema; -// } - -// export interface NotRequiredNullableArraySchema -// extends BasicArraySchema { -// of(type: Schema): NotRequiredNullableArraySchema; -// nullable(isNullable?: true): NotRequiredNullableArraySchema; -// nullable(isNullable: false): NotRequiredArraySchema; -// nullable(isNullable?: boolean): ArraySchema; -// required(message?: TestOptionsMessage): ArraySchema; -// defined(): NullableArraySchema; -// notRequired(): NotRequiredNullableArraySchema; -// optional(): NotRequiredNullableArraySchema; -// } - -// export interface NullableArraySchema -// extends BasicArraySchema { -// of(type: Schema): NullableArraySchema; -// nullable(isNullable?: true): NullableArraySchema; -// nullable(isNullable: false): ArraySchema; -// nullable(isNullable?: boolean): ArraySchema; -// required(message?: TestOptionsMessage): ArraySchema; -// defined(): NullableArraySchema; -// notRequired(): NotRequiredNullableArraySchema; -// optional(): NotRequiredNullableArraySchema; -// } - -// export interface NotRequiredArraySchema -// extends BasicArraySchema { -// of(type: Schema): NotRequiredArraySchema; -// nullable(isNullable?: true): NotRequiredNullableArraySchema; -// nullable(isNullable: false): NotRequiredArraySchema; -// nullable(isNullable: boolean): ArraySchema; -// required(message?: TestOptionsMessage): ArraySchema; -// defined(): ArraySchema; -// notRequired(): NotRequiredArraySchema; -// optional(): NotRequiredArraySchema; -// } - -// export interface ArraySchema extends BasicArraySchema { -// of(type: Schema): ArraySchema; -// nullable(isNullable?: true): NullableArraySchema; -// nullable(isNullable: false | boolean): ArraySchema; -// required(message?: TestOptionsMessage): ArraySchema; -// defined(): ArraySchema; -// notRequired(): NotRequiredArraySchema; -// optional(): NotRequiredArraySchema; -// } - -// export type ObjectSchemaDefinition = { -// // This shouldn't be necessary because MixedSchema extends Schema, but type -// // inference only works with it this way - otherwise when you use a mixed -// // field in object schema, it will type as `unknown`. Not sure why that is - -// // maybe some sort of inference depth limit? -// [field in keyof T]: Schema | MixedSchema | Ref; -// }; - -// /** -// * Merges two interfaces. For properties in common, property types from `U` trump those of `T`. -// * This is conducive to the functionality of -// * [yup's `object.shape()` method](https://www.npmjs.com/package/yup#objectshapefields-object-nosortedges-arraystring-string-schema). -// */ -// export type Shape = -// | ({ [P in keyof T]: P extends keyof U ? U[P] : T[P] } & U) -// | PreserveOptionals; - -// export interface ObjectSchemaConstructor { -// (fields?: ObjectSchemaDefinition): ObjectSchema< -// T | undefined -// >; -// new (): ObjectSchema<{}>; -// } - -// export interface ObjectSchema< -// T extends object | null | undefined = object | undefined -// > extends Schema { -// fields: { -// [k in keyof T]: Schema; -// }; -// shape( -// fields: ObjectSchemaDefinition, -// noSortEdges?: Array<[string, string]>, -// ): ObjectSchema>; -// from(fromKey: string, toKey: string, alias?: boolean): ObjectSchema; -// noUnknown( -// onlyKnownKeys?: boolean, -// message?: ObjectLocale['noUnknown'], -// ): ObjectSchema; -// unknown( -// allow?: boolean, -// message?: ObjectLocale['noUnknown'], -// ): ObjectSchema; -// transformKeys(callback: (key: any) => any): void; -// camelCase(): ObjectSchema; -// snakeCase(): ObjectSchema; -// constantCase(): ObjectSchema; -// nullable(isNullable?: true): ObjectSchema; -// nullable(isNullable: false): ObjectSchema>; -// nullable(isNullable?: boolean): ObjectSchema; -// required( -// message?: TestOptionsMessage, -// ): ObjectSchema>; -// defined(): ObjectSchema>; -// notRequired(): ObjectSchema; -// optional(): ObjectSchema; -// concat(schema: this): this; -// concat(schema: ObjectSchema): ObjectSchema; -// oneOf( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): ObjectSchema; -// equals( -// arrayOfValues: ReadonlyArray, -// message?: MixedLocale['oneOf'], -// ): ObjectSchema; -// test( -// name: string, -// message: TestOptionsMessage, -// test: TestFunction, -// ): this; -// test( -// name: string, -// message: TestOptionsMessage, -// test: AssertingTestFunction, -// ): ObjectSchema; -// test( -// options: AssertingTestOptions>, -// ): ObjectSchema; -// test(options: TestOptions>): this; -// } - -// export type TestFunction = ( -// this: TestContext, -// value: T, -// ) => boolean | ValidationError | Promise; -// export type AssertingTestFunction = ( -// this: TestContext, -// value: any, -// ) => value is T; - -// export type TransformFunction = ( -// this: T, -// value: any, -// originalValue: any, -// ) => any; - -// export interface WhenOptionsBuilderFunction { -// (value: any, schema: T): T; -// (v1: any, v2: any, schema: T): T; -// (v1: any, v2: any, v3: any, schema: T): T; -// (v1: any, v2: any, v3: any, v4: any, schema: T): T; -// } - -// export type WhenOptionsBuilderObjectIs = -// | ((...values: any[]) => boolean) -// | boolean -// | number -// | null -// | object -// | string; - -// export type WhenOptionsBuilderObject = -// | { -// is: WhenOptionsBuilderObjectIs; -// then: any; -// otherwise: any; -// } -// | object; - -// export type WhenOptions = -// | WhenOptionsBuilderFunction -// | WhenOptionsBuilderObject; - -// export interface TestContext { -// path: string; -// options: ValidateOptions; -// parent: any; -// schema: Schema; -// resolve: (value: any) => any; -// createError: (params?: { -// path?: string; -// message?: string; -// params?: object; -// }) => ValidationError; -// } - -// export interface ValidateOptions { -// /** -// * Only validate the input, and skip and coercion or transformation. Default - false -// */ -// strict?: boolean; -// /** -// * Return from validation methods on the first error rather than after all validations run. Default - true -// */ -// abortEarly?: boolean; -// /** -// * Remove unspecified keys from objects. Default - false -// */ -// stripUnknown?: boolean; -// /** -// * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true -// */ -// recursive?: boolean; -// /** -// * Any context needed for validating schema conditions (see: when()) -// */ -// context?: object; -// } - -// export interface TestMessageParams { -// path: string; -// value: any; -// originalValue: any; -// label: string; -// } - -// interface BaseTestOptions

> { -// /** -// * Unique name identifying the test. Required for exclusive tests. -// */ -// name?: string; - -// /** -// * Test function, determines schema validity -// */ -// test: TestFunction; - -// /** -// * The validation error message -// */ -// message?: TestOptionsMessage

; - -// /** -// * Values passed to message for interpolation -// */ -// params?: P; - -// /** -// * Mark the test as exclusive, meaning only one of the same can be active at once -// */ -// exclusive?: boolean; -// } - -// interface NonExclusiveTestOptions

> -// extends BaseTestOptions

{ -// exclusive?: false; -// } - -// interface ExclusiveTestOptions

> -// extends BaseTestOptions

{ -// exclusive: true; -// name: string; -// } - -// interface NonExclusiveAssertingTestOptions> -// extends NonExclusiveTestOptions

{ -// test: AssertingTestFunction; -// } - -// interface ExclusiveAssertingTestOptions> -// extends ExclusiveTestOptions

{ -// test: AssertingTestFunction; -// } - -// export type TestOptions

= {}> = -// | NonExclusiveTestOptions

-// | ExclusiveTestOptions

; - -// export type AssertingTestOptions = {}> = -// | NonExclusiveAssertingTestOptions -// | ExclusiveAssertingTestOptions; - -// export interface SchemaFieldRefDescription { -// type: 'ref'; -// key: string; -// } - -// export interface SchemaFieldInnerTypeDescription -// extends Pick> { -// innerType?: SchemaFieldDescription; -// } - -// export type SchemaFieldDescription = -// | SchemaDescription -// | SchemaFieldRefDescription -// | SchemaFieldInnerTypeDescription; - -// export interface SchemaDescription { -// type: string; -// label: string; -// meta: object; -// tests: Array<{ name: string; params: { [k: string]: any } }>; -// fields: Record; -// } - -// // ValidationError works a lot more like a class vs. a constructor -// // function that returns an interface. It's also got a couple of -// // static methods and it inherits from the generic Error class in -// // the [yup codebase][1]. -// // [1]: (https://github.com/jquense/yup/blob/master/src/ValidationError.js) -// export class ValidationError extends Error { -// name: string; -// message: string; -// value: any; -// /** -// * A string, indicating where there error was thrown. path is empty at the root level. -// */ -// path: string; -// type: any; -// /** -// * array of error messages -// */ -// errors: string[]; - -// /** -// * In the case of aggregate errors, inner is an array of ValidationErrors throw earlier in the validation chain. -// */ -// inner: ValidationError[]; -// params?: object; - -// static isError(err: any): err is ValidationError; -// static formatError( -// message: string | ((params?: any) => string), -// params?: any, -// ): string | ((params?: any) => string); - -// constructor(errors: string | string[], value: any, path: string, type?: any); -// } - -// // It is tempting to declare `Ref` very simply, but there are problems with these approaches: -// // -// // * `type Ref = Record;` - This is essentially how it was originally declared, but -// // just about any object satisfies this contract, which makes the type declaration too loose to -// // be useful. -// // -// // * `type Ref = object;` - This is a variation on the previous bullet in that it is too loose. -// // -// // * `class Ref {}` - This is yet another variation that is too loose. -// // -// // * `type Ref = void;` - This works and the emitted JavaScript is just fine, but it results in some -// // confusing IntelliSense, e.g it looks like the `ref()` returns `void`, which is not the case. -// // -// // The solution is twofold. 1.) Declare it as a class with a private constructor to prevent it from -// // being instantiated by anything but the `ref()` factory function, and; 2.) declare a private -// // readonly property (that yup actually places on the prototype) to force it to be structurally -// // incompatible with any other object type. - -// /** -// * `Ref` is an opaque type that is internal to yup. Creating a `Ref` instance is accomplished via the `ref()` factory -// * function. -// */ -// export class Ref { -// private constructor(); -// private readonly __isYupRef: true; -// } - -// // tslint:disable-next-line:no-empty-interface -// export interface Lazy extends Schema {} - -// export interface FormatErrorParams { -// path: string; -// type: string; -// value?: any; -// originalValue?: any; -// } - -// export type LocaleValue = string | ((params: FormatErrorParams) => string); - -// export interface MixedLocale { -// default?: TestOptionsMessage; -// required?: TestOptionsMessage; -// oneOf?: TestOptionsMessage<{ values: any }>; -// notOneOf?: TestOptionsMessage<{ values: any }>; -// notType?: LocaleValue; -// } - -// export interface StringLocale { -// length?: TestOptionsMessage<{ length: number }>; -// min?: TestOptionsMessage<{ min: number }>; -// max?: TestOptionsMessage<{ max: number }>; -// matches?: TestOptionsMessage<{ regex: RegExp }>; -// email?: TestOptionsMessage<{ regex: RegExp }>; -// url?: TestOptionsMessage<{ regex: RegExp }>; -// uuid?: TestOptionsMessage<{ regex: RegExp }>; -// trim?: TestOptionsMessage; -// lowercase?: TestOptionsMessage; -// uppercase?: TestOptionsMessage; -// } - -// export interface NumberLocale { -// min?: TestOptionsMessage<{ min: number }>; -// max?: TestOptionsMessage<{ max: number }>; -// lessThan?: TestOptionsMessage<{ less: number }>; -// moreThan?: TestOptionsMessage<{ more: number }>; -// positive?: TestOptionsMessage<{ more: number }>; -// negative?: TestOptionsMessage<{ less: number }>; -// integer?: TestOptionsMessage; -// } - -// export interface DateLocale { -// min?: TestOptionsMessage<{ min: Date | string }>; -// max?: TestOptionsMessage<{ max: Date | string }>; -// } - -// export interface ObjectLocale { -// noUnknown?: TestOptionsMessage; -// } - -// export interface ArrayLocale { -// min?: TestOptionsMessage<{ min: number }>; -// max?: TestOptionsMessage<{ max: number }>; -// } - -// export interface LocaleObject { -// mixed?: MixedLocale; -// string?: StringLocale; -// number?: NumberLocale; -// date?: DateLocale; -// boolean?: {}; -// object?: ObjectLocale; -// array?: ArrayLocale; -// } - -// export type InferType = T extends Schema -// ? InnerInferType

-// : never; - -// // Shut off automatic exporting after this statement -// export {}; - -// type KeyOfUndefined = { -// [P in keyof T]-?: undefined extends T[P] ? P : never; -// }[keyof T]; - -// type PreserveNull = T extends null ? null : never; -// type PreserveUndefined = T extends undefined ? undefined : never; -// type PreserveOptionals = PreserveNull | PreserveUndefined; -// type Id = { -// [K in keyof T]: T[K] extends object ? InnerInferType : T[K]; -// }; -// type RequiredProps = Pick>>; -// type NotRequiredProps = Partial>>; -// type InnerInferType = -// | (T extends Array -// ? InnerInferTypeArray -// : Id & RequiredProps>) -// | PreserveOptionals; -// interface InnerInferTypeArray extends Array> {} -// type InferredArrayType = T extends Array ? U : T; -// /** If `T` is optional, returns optional `U`. */ -// type MaintainOptionality = T extends undefined ? U | undefined : U; From 5a26a5b59e04e7177da7a20b57d8dba56df91236 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 24 Nov 2020 10:09:11 -0500 Subject: [PATCH 16/31] WIP --- src/Lazy.ts | 10 +-- src/array.ts | 161 +++++++++++++++++++++++++++++++++------------- src/mixed.ts | 77 +++++++++------------- src/number.ts | 40 ++++++------ src/object.ts | 155 +++++++++++++++++++++++++++++++------------- src/string.ts | 40 ++++++------ src/util/types.ts | 23 +++---- test/types.ts | 4 +- 8 files changed, 314 insertions(+), 196 deletions(-) diff --git a/src/Lazy.ts b/src/Lazy.ts index 5870cb143..57a7439aa 100644 --- a/src/Lazy.ts +++ b/src/Lazy.ts @@ -2,14 +2,14 @@ import isSchema from './util/isSchema'; import Schema, { CastOptions } from './Schema'; import type { Callback, ValidateOptions } from './types'; import type { ResolveOptions } from './Condition'; -import type MixedSchema from './mixed'; +import MixedSchema, { AnyMixed } from './mixed'; -export type LazyBuilder = ( +export type LazyBuilder = ( value: any, options: ResolveOptions, ) => T; -export function create(builder: LazyBuilder) { +export function create(builder: LazyBuilder) { return new Lazy(builder); } @@ -21,7 +21,7 @@ export type LazyType = LazyReturnValue extends MixedSchema ? TType : never; -class Lazy implements Schema { +class Lazy implements Schema { type = 'lazy' as const; __isYupSchema__ = true; @@ -43,7 +43,7 @@ class Lazy implements Schema { resolve(options: ResolveOptions) { return this._resolve(options.value, options); } - cast(value: any, options?: CastOptions) { + cast(value: any, options?: CastOptions): T['__inputType'] { return this._resolve(value, options).cast(value, options); } diff --git a/src/array.ts b/src/array.ts index 15df31669..79f012895 100644 --- a/src/array.ts +++ b/src/array.ts @@ -10,6 +10,7 @@ import ValidationError from './ValidationError'; import type Reference from './Reference'; import { Asserts, + Default, Nullability, Presence, ResolveInput, @@ -23,26 +24,28 @@ type RefectorFn = (value: any, index: number, array: any[]) => boolean; type MaybeArray = Maybe[]>; export function create(type?: TInner) { - return new ArraySchema(type); + return new ArraySchema(type) as OptionalArraySchema; } +// type MaintainNull = T extends null ? U | null : U; +// type MaintainUndefined = T extends undefined ? U | null : U; +// type WithOptionality = MaintainUndefined>; + type Type = T extends MixedSchema ? TType : never; +abstract class BasicArraySchema< + TElement, + TType extends Maybe, + TOut extends Maybe, + TPresence extends Presence +> extends MixedSchema {} + export default class ArraySchema< T extends AnyMixed = AnyMixed, - TDefault extends MaybeArray> = undefined, - TNullablity extends Nullability = Unset, TPresence extends Presence = Unset -> extends MixedSchema< - Type[], - TDefault, - TNullablity, - TPresence, - ResolveInput[], TNullablity, TDefault>, - ResolveOutput[], TNullablity, TPresence, TDefault> -> { +> extends BasicArraySchema, Type[], Asserts[], TPresence> { innerType: T | undefined; constructor(type?: T) { @@ -171,16 +174,16 @@ export default class ArraySchema< return next; } - concat>( - schema: TOther, - ): TOther extends ArraySchema - ? ArraySchema< - T, - D, - N extends Unset ? TNullablity : N, - P extends Unset ? TPresence : P - > - : never; + // concat>( + // schema: TOther, + // ): TOther extends ArraySchema + // ? ArraySchema< + // T, + // D, + // N extends Unset ? TNullablity : N, + // P extends Unset ? TPresence : P + // > + // : never; concat(schema: any): any; concat(schema: any): any { let next = super.concat(schema) as this; @@ -197,7 +200,7 @@ export default class ArraySchema< of( schema: TInner, - ): ArraySchema { + ): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -282,36 +285,104 @@ export default class ArraySchema< } } -export default interface ArraySchema< - T extends AnyMixed, - TDefault extends MaybeArray>, - TNullablity extends Nullability, - TPresence extends Presence -> extends MixedSchema< - Type[], - TDefault, - TNullablity, - TPresence, - ResolveInput[], TNullablity, TDefault>, - ResolveOutput[], TNullablity, TPresence, TDefault> +interface NullableArraySchema< + T extends AnyMixed = AnyMixed, + TPresence extends Presence = Unset +> extends BasicArraySchema< + Type, + Type[] | null, + Asserts[] | null, + TPresence > { - // concat(schema: any): any; + of( + schema: TInner, + ): NullableArraySchema; + nullable(isNullable?: true): NullableArraySchema; + nullable(isNullable: false): ArraySchema; + nullable(isNullable?: boolean): ArraySchema; + + defined(msg?: MixedLocale['defined']): NullableArraySchema; + required(msg?: MixedLocale['required']): NullableArraySchema; + notRequired(): ArraySchema; - default( + default[] | null | undefined>( def: TNextDefault | (() => TNextDefault), - ): ArraySchema; + ): TNextDefault extends undefined + ? NullableOptionalArraySchema + : this; +} + +interface OptionalArraySchema< + T extends AnyMixed = AnyMixed, + TPresence extends Presence = Unset +> extends BasicArraySchema< + Type, + Type[] | undefined, + Asserts[] | undefined, + TPresence + > { + of( + schema: TInner, + ): NullableArraySchema; + nullable(isNullable?: true): NullableArraySchema; + nullable(isNullable: false): ArraySchema; + nullable(isNullable?: boolean): ArraySchema; + + defined(msg?: MixedLocale['defined']): ArraySchema; + required(msg?: MixedLocale['required']): ArraySchema; + notRequired(): ArraySchema; + + default[] | null | undefined>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined + ? OptionalArraySchema + : ArraySchema; +} + +interface NullableOptionalArraySchema< + T extends AnyMixed = AnyMixed, + TPresence extends Presence = Unset +> extends BasicArraySchema< + Type, + Type[] | null | undefined, + Asserts[] | null | undefined, + TPresence + > { + of( + schema: TInner, + ): NullableArraySchema; + nullable(isNullable?: true): NullableOptionalArraySchema; + nullable(isNullable: false): OptionalArraySchema; + nullable(isNullable?: boolean): OptionalArraySchema; defined( msg?: MixedLocale['defined'], - ): ArraySchema; - + ): NullableOptionalArraySchema; required( msg?: MixedLocale['required'], - ): ArraySchema; - notRequired(): ArraySchema; + ): NullableOptionalArraySchema; + notRequired(): NullableOptionalArraySchema; - nullable(isNullable?: true): ArraySchema; - nullable( - isNullable: false, - ): ArraySchema; + default[] | null | undefined>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined ? this : NullableArraySchema; +} + +export default interface ArraySchema< + T extends AnyMixed = AnyMixed, + TPresence extends Presence = Unset +> extends BasicArraySchema, Type[], Asserts[], TPresence> { + defined(msg?: MixedLocale['defined']): ArraySchema; + required(msg?: MixedLocale['required']): ArraySchema; + notRequired(): ArraySchema; + + nullable(isNullable?: true): NullableArraySchema; + nullable(isNullable: false): ArraySchema; + nullable(isNullable?: boolean): ArraySchema; + + default[] | null | undefined>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined + ? OptionalArraySchema + : ArraySchema; } diff --git a/src/mixed.ts b/src/mixed.ts index a09729dae..e769ebb64 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -29,10 +29,12 @@ import Schema, { } from './Schema'; import { ValidationError } from '.'; import type { + Default, Nullability, Presence, ResolveInput, ResolveOutput, + StrictNonNullable, Unset, } from './util/types'; @@ -112,24 +114,21 @@ export type SchemaOptions = { spec?: SchemaSpec; }; -export type AnyMixed = MixedSchema; +export type AnyMixed = MixedSchema; export function create() { return new MixedSchema(); } export default class MixedSchema< - TType = any, - TDefault = undefined, - TNullablity extends Nullability = Unset, + TCast = any, TPresence extends Presence = Unset, - TInput = ResolveInput, - TOutput = ResolveOutput + TOutput = TCast > implements Schema { readonly type: string; - readonly __inputType!: TInput; - readonly __outputType!: TOutput; + readonly __inputType!: TCast; + readonly __outputType!: ResolveOutput; readonly __isYupSchema__!: boolean; @@ -150,7 +149,7 @@ export default class MixedSchema< protected exclusiveTests: Record = Object.create(null); - optional!: () => MixedSchema; + // optional!: () => MixedSchema; spec: SchemaSpec; @@ -182,7 +181,7 @@ export default class MixedSchema< return this.type; } - protected _typeCheck(_value: any): _value is TType { + protected _typeCheck(_value: any): _value is TCast { return true; } @@ -244,13 +243,8 @@ export default class MixedSchema< concat( schema: TOther, - ): TOther extends MixedSchema - ? MixedSchema< - T, - D, - N extends Unset ? TNullablity : N, - P extends Unset ? TPresence : P - > + ): TOther extends MixedSchema + ? MixedSchema : never; concat(schema: MixedSchema): MixedSchema; concat(schema: MixedSchema): MixedSchema { @@ -335,7 +329,7 @@ export default class MixedSchema< * @param {*=} options.parent * @param {*=} options.context */ - cast(value: any, options: CastOptions = {}): TInput { + cast(value: any, options: CastOptions = {}): TCast { let resolvedSchema = this.resolve({ value, ...options, @@ -505,14 +499,16 @@ export default class MixedSchema< : cloneDeep(defaultValue); } - getDefault(options?: ResolveOptions): TDefault { + getDefault(options?: ResolveOptions): TCast { let schema = this.resolve(options || {}); return schema._getDefault(); } - default>( + default>( def: TNextDefault | (() => TNextDefault), - ): MixedSchema { + ): TNextDefault extends undefined + ? MixedSchema + : MixedSchema, TPresence> { if (arguments.length === 0) { return this._getDefault(); } @@ -532,9 +528,7 @@ export default class MixedSchema< return value != null; } - required( - message = locale.required, - ): MixedSchema { + required(message = locale.required): MixedSchema { return this.clone({ presence: 'required' }).withMutation((s) => s.test({ message, @@ -547,19 +541,15 @@ export default class MixedSchema< ) as any; } - notRequired(): MixedSchema { + notRequired(): MixedSchema { var next = this.clone({ presence: 'optional' }); next.tests = next.tests.filter((test) => test.OPTIONS.name !== 'required'); return next as any; } - nullable( - isNullable?: true, - ): MixedSchema; - nullable( - isNullable: false, - ): MixedSchema; - nullable(isNullable = true): MixedSchema { + nullable(isNullable?: true): MixedSchema; + nullable(isNullable: false): MixedSchema, TPresence>; + nullable(isNullable = true): MixedSchema { var next = this.clone({ nullability: isNullable ? 'nullable' : 'nonnullable', }); @@ -586,10 +576,10 @@ export default class MixedSchema< * If an exclusive test is added to a schema with non-exclusive tests of the same name * the previous tests are removed and further tests of the same name will replace each other. */ - test(options: TestConfig): this; - test(test: TestFunction): this; - test(name: string, test: TestFunction): this; - test(name: string, message: Message, test: TestFunction): this; + test(options: TestConfig): this; + test(test: TestFunction): this; + test(name: string, test: TestFunction): this; + test(name: string, message: Message, test: TestFunction): this; test(...args: any[]) { let opts: TestConfig; @@ -681,7 +671,7 @@ export default class MixedSchema< return next; } - oneOf(enums: Maybe[], message = locale.oneOf): this { + oneOf(enums: Maybe[], message = locale.oneOf): this { var next = this.clone(); enums.forEach((val) => { @@ -709,7 +699,7 @@ export default class MixedSchema< return next as any; } - notOneOf( + notOneOf( enums: Maybe[], message = locale.notOneOf, ): this { @@ -762,9 +752,7 @@ export default class MixedSchema< return description; } - defined( - message = locale.defined, - ): MixedSchema { + defined(message = locale.defined): MixedSchema { return this.test({ message, name: 'defined', @@ -777,11 +765,8 @@ export default class MixedSchema< } export default interface MixedSchema< - TType, - TDefault, - TNullablity extends Nullability, + TCast, TPresence extends Presence, - TInput, TOutput > { validateAt( @@ -822,4 +807,4 @@ for (const alias of ['equals', 'is'] as const) for (const alias of ['not', 'nope'] as const) MixedSchema.prototype[alias] = MixedSchema.prototype.notOneOf; -MixedSchema.prototype.optional = MixedSchema.prototype.notRequired; +// MixedSchema.prototype.optional = MixedSchema.prototype.notRequired; diff --git a/src/number.ts b/src/number.ts index 457e35c10..426c8c4a2 100644 --- a/src/number.ts +++ b/src/number.ts @@ -3,7 +3,13 @@ import { MixedLocale, number as locale } from './locale'; import isAbsent from './util/isAbsent'; import type { Maybe } from './types'; import type Reference from './Reference'; -import type { Nullability, Presence, Unset } from './util/types'; +import type { + Defined, + Nullability, + Presence, + StrictNonNullable, + Unset, +} from './util/types'; let isNaN = (value: Maybe) => value != +value!; @@ -12,11 +18,9 @@ export function create() { } export default class NumberSchema< - TType extends number, - TDefault extends Maybe = undefined, - TNullablity extends Nullability = Unset, + TType extends Maybe = number | undefined, TPresence extends Presence = Unset -> extends MixedSchema { +> extends MixedSchema { constructor() { super({ type: 'number' }); @@ -131,28 +135,22 @@ export default class NumberSchema< } export default interface NumberSchema< - TType extends number, - TDefault extends Maybe, - TNullablity extends Nullability, + TType extends Maybe, TPresence extends Presence -> extends MixedSchema { +> extends MixedSchema { default>( def: TNextDefault | (() => TNextDefault), - ): NumberSchema; + ): TNextDefault extends undefined + ? NumberSchema + : NumberSchema, TPresence>; - defined( - msg?: MixedLocale['defined'], - ): NumberSchema; + defined(msg?: MixedLocale['defined']): NumberSchema; - required( - msg?: MixedLocale['required'], - ): NumberSchema; - notRequired(): NumberSchema; + required(msg?: MixedLocale['required']): NumberSchema; + notRequired(): NumberSchema; - nullable( - isNullable?: true, - ): NumberSchema; + nullable(isNullable?: true): NumberSchema; nullable( isNullable: false, - ): NumberSchema; + ): NumberSchema, TPresence>; } diff --git a/src/object.ts b/src/object.ts index 4768a1bb8..e8d1e44b1 100644 --- a/src/object.ts +++ b/src/object.ts @@ -20,6 +20,8 @@ import type { Presence, Unset, TypedSchema, + StrictNonNullable, + Defined, } from './util/types'; import type Reference from './Reference'; import Lazy, { LazyType } from './Lazy'; @@ -32,6 +34,7 @@ function unknown(ctx: ObjectSchema, value: any) { return Object.keys(value).filter((key) => known.indexOf(key) === -1); } +type AnyObject = Record; type ObjectShape = Record>; type AssignShape = { @@ -84,18 +87,19 @@ type ObjectSchemaSpec = SchemaSpec & { const defaultSort = sortByKeyOrder([]); +abstract class BaseObjectSchema< + TType extends Maybe, + TOut extends Maybe, + TPresence extends Presence +> extends MixedSchema {} + export default class ObjectSchema< TShape extends ObjectShape = ObjectShape, - TDefault extends Maybe> = DefaultFromShape, - TNullablity extends Nullability = Unset, TPresence extends Presence = Unset -> extends MixedSchema< +> extends BaseObjectSchema< TypeFromShape, - TDefault, - TNullablity, - TPresence, - ResolveInput, TNullablity, TDefault>, - ResolveOutput, TNullablity, TPresence, TDefault> + AssertsShape, + TPresence > { fields: TShape = Object.create(null); @@ -131,7 +135,7 @@ export default class ObjectSchema< }); } - protected _typeCheck(value: any): value is TypeFromShape { + protected _typeCheck(value: any): value is TType { return isObject(value) || typeof value === 'function'; } @@ -160,7 +164,7 @@ export default class ObjectSchema< let isChanged = false; for (const prop of props) { let field = fields[prop]; - let exists = has(value, prop); + let exists = has(value!, prop); if (field) { let fieldValue; @@ -298,16 +302,16 @@ export default class ObjectSchema< return next; } - concat>( - schema: TOther, - ): TOther extends ObjectSchema - ? ObjectSchema< - TShape & T, - D & TDefault, - N extends Unset ? TNullablity : N, - P extends Unset ? TPresence : P - > - : never; + // concat>( + // schema: TOther, + // ): TOther extends ObjectSchema + // ? ObjectSchema< + // TShape & T, + // D & TDefault, + // N extends Unset ? TNullablity : N, + // P extends Unset ? TPresence : P + // > + // : never; concat(schema: AnyMixed): AnyMixed; concat(schema: any): any { let next = super.concat(schema) as any; @@ -466,35 +470,100 @@ export default class ObjectSchema< } } -export default interface ObjectSchema< +interface NullableObjectSchema< TShape extends ObjectShape, - TDefault extends Maybe>, - TNullablity extends Nullability, TPresence extends Presence -> extends MixedSchema< - TypeFromShape, - TDefault, - TNullablity, - TPresence, - ResolveInput, TNullablity, TDefault>, - ResolveOutput, TNullablity, TPresence, TDefault> +> extends BaseObjectSchema< + TypeFromShape | null, + AssertsShape | null, + TPresence + > { + // default>>( + // def: TNextDefault | (() => TNextDefault), + // ): TNextDefault extends undefined + // ? NullableOptionalObjectSchema + // : this; + + defined( + msg?: MixedLocale['defined'], + ): NullableObjectSchema; + required( + msg?: MixedLocale['required'], + ): NullableObjectSchema; + notRequired(): NullableObjectSchema; + + nullable(isNullable?: true): NullableObjectSchema; + nullable(isNullable: false): ObjectSchema; +} + +interface OptionalObjectSchema< + TShape extends ObjectShape, + TPresence extends Presence +> extends BaseObjectSchema< + TypeFromShape | undefined, + AssertsShape | undefined, + TPresence > { - default>>( - def: TNextDefault | (() => TNextDefault), - ): ObjectSchema; + // default>>( + // def: TNextDefault | (() => TNextDefault), + // ): TNextDefault extends undefined ? this : ObjectSchema; defined( msg?: MixedLocale['defined'], - ): ObjectSchema; + ): OptionalObjectSchema; required( msg?: MixedLocale['required'], - ): ObjectSchema; - notRequired(): ObjectSchema; - - nullable( - isNullable?: true, - ): ObjectSchema; - nullable( - isNullable: false, - ): ObjectSchema; + ): OptionalObjectSchema; + notRequired(): OptionalObjectSchema; + + nullable(isNullable?: true): NullableOptionalObjectSchema; + nullable(isNullable: false): OptionalObjectSchema; +} + +interface NullableOptionalObjectSchema< + TShape extends ObjectShape, + TPresence extends Presence +> extends BaseObjectSchema< + TypeFromShape | null | undefined, + AssertsShape | null | undefined, + TPresence + > { + // default>>( + // def: TNextDefault | (() => TNextDefault), + // ): TNextDefault extends undefined + // ? this + // : NullableObjectSchema; + + defined( + msg?: MixedLocale['defined'], + ): NullableOptionalObjectSchema; + required( + msg?: MixedLocale['required'], + ): NullableOptionalObjectSchema; + notRequired(): NullableOptionalObjectSchema; + + nullable(isNullable?: true): NullableOptionalObjectSchema; + nullable(isNullable: false): OptionalObjectSchema; +} + +export default interface ObjectSchema< + TShape extends ObjectShape, + TPresence extends Presence +> extends BaseObjectSchema< + TypeFromShape, + AssertsShape, + TPresence + > { + // default>>( + // def: TNextDefault | (() => TNextDefault), + // ): TNextDefault extends undefined + // ? OptionalObjectSchema + // : ObjectSchema; + + defined(msg?: MixedLocale['defined']): ObjectSchema; + required(msg?: MixedLocale['required']): ObjectSchema; + notRequired(): ObjectSchema; + + nullable(isNullable?: true): NullableObjectSchema; + nullable(isNullable: false): this; } diff --git a/src/string.ts b/src/string.ts index 7ccc39b89..c9d57c674 100644 --- a/src/string.ts +++ b/src/string.ts @@ -3,7 +3,13 @@ import { MixedLocale, string as locale } from './locale'; import isAbsent from './util/isAbsent'; import type Reference from './Reference'; import type { Message, Maybe } from './types'; -import type { Nullability, Presence, Unset } from './util/types'; +import type { + Default, + Defined, + Presence, + StrictNonNullable, + Unset, +} from './util/types'; // eslint-disable-next-line let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; @@ -28,11 +34,9 @@ export function create() { } export default class StringSchema< - TType extends string = string, - TDefault extends Maybe = undefined, - TNullablity extends Nullability = Unset, + TType extends Maybe = string | undefined, TPresence extends Presence = Unset -> extends MixedSchema { +> extends MixedSchema { _tsType!: string | undefined; _tsValidate!: string | undefined; @@ -196,28 +200,22 @@ export default class StringSchema< } export default interface StringSchema< - TType extends string, - TDefault extends Maybe, - TNullablity extends Nullability, + TType extends Maybe, TPresence extends Presence -> extends MixedSchema { +> extends MixedSchema { default>( def: TNextDefault | (() => TNextDefault), - ): StringSchema; + ): TNextDefault extends undefined + ? StringSchema + : StringSchema, TPresence>; - defined( - msg?: MixedLocale['defined'], - ): StringSchema; + defined(msg?: MixedLocale['defined']): StringSchema; - required( - msg?: MixedLocale['required'], - ): StringSchema; - notRequired(): StringSchema; + required(msg?: MixedLocale['required']): StringSchema; + notRequired(): StringSchema; - nullable( - isNullable?: true, - ): StringSchema; + nullable(isNullable?: true): StringSchema; nullable( isNullable: false, - ): StringSchema; + ): StringSchema, TPresence>; } diff --git a/src/util/types.ts b/src/util/types.ts index 584f6f37b..d2f3d8817 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -4,12 +4,14 @@ export type Unset = 'unset'; export type Presence = 'required' | 'defined' | 'optional' | Unset; export type Nullability = 'nullable' | 'nonnullable' | Unset; -type StrictNonNullable = T extends null ? never : T; +export type StrictNonNullable = T extends null ? never : T; -type Defined = T extends undefined ? never : T; +export type Defined = T extends undefined ? never : T; export type TypeDef = Nullability | Presence | ''; +export type Default = D extends undefined ? T | undefined : T; + export type ResolveDefault = undefined> = | TType | TDefault; @@ -19,19 +21,14 @@ export type ResolveInput< TNull = Unset, TDefault = undefined > = TNull extends 'nullable' - ? TType | TDefault | null - : StrictNonNullable; + ? Default | null + : StrictNonNullable; -export type ResolveOutput< - TType, - TNull = Unset, - TPresent = Unset, - TDefault = undefined -> = TPresent extends 'required' - ? NonNullable> +export type ResolveOutput = TPresent extends 'required' + ? NonNullable : TPresent extends 'defined' - ? Defined> - : ResolveInput; // + ? Defined + : TType; // export type TypedSchema = { __inputType: any; diff --git a/test/types.ts b/test/types.ts index 4445c8c28..d02ba57a1 100644 --- a/test/types.ts +++ b/test/types.ts @@ -229,13 +229,13 @@ string().required().nullable(); .default([] as number[]) .getDefault(); - // $ExpectType string[] | (string | null)[] | null + // $ExpectType (string | null)[] | null array(string().nullable().default('')) .nullable() .default(() => [] as string[]) .validateSync(null); - // $ExpectType number[] + // $ExpectType (number | undefined)[] array(number()) .default(() => []) .getDefault(); From ebe75f71e703c04bc6a5a260cf191ec082893bd8 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 24 Nov 2020 11:13:24 -0500 Subject: [PATCH 17/31] WIP --- src/Base.ts | 810 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/array.ts | 270 +++++++++-------- src/mixed.ts | 699 +------------------------------------------ src/number.ts | 5 +- src/object.ts | 229 +++++++------- src/string.ts | 17 +- src/types.ts | 8 +- test/types.ts | 12 +- 8 files changed, 1108 insertions(+), 942 deletions(-) create mode 100644 src/Base.ts diff --git a/src/Base.ts b/src/Base.ts new file mode 100644 index 000000000..a91b4f300 --- /dev/null +++ b/src/Base.ts @@ -0,0 +1,810 @@ +// @ts-ignore +import cloneDeep from 'nanoclone'; + +import { mixed as locale } from './locale'; +import Condition, { ConditionOptions, ResolveOptions } from './Condition'; +import runTests from './util/runTests'; +import createValidation, { + TestFunction, + Test, + TestConfig, +} from './util/createValidation'; +import printValue from './util/printValue'; +import Ref from './Reference'; +import { getIn } from './util/reach'; +import Reference from './Reference'; +import toArray from './util/toArray'; +import { + ValidateOptions, + TransformFunction, + Message, + Callback, + InternalOptions, + Maybe, +} from './types'; +import Schema, { + CastOptions, + SchemaRefDescription, + SchemaDescription, +} from './Schema'; +import { ValidationError } from '.'; +import type { + Default, + Defined, + Nullability, + Presence, + ResolveInput, + ResolveOutput, + StrictNonNullable, + Unset, +} from './util/types'; + +const UNSET = 'unset' as const; + +class RefSet { + list: Set; + refs: Map; + + constructor() { + this.list = new Set(); + this.refs = new Map(); + } + get size() { + return this.list.size + this.refs.size; + } + describe() { + const description = [] as Array; + + for (const item of this.list) description.push(item); + for (const [, ref] of this.refs) description.push(ref.describe()); + + return description; + } + toArray() { + return Array.from(this.list).concat(Array.from(this.refs.values())); + } + add(value: unknown) { + Ref.isRef(value) ? this.refs.set(value.key, value) : this.list.add(value); + } + delete(value: unknown) { + Ref.isRef(value) ? this.refs.delete(value.key) : this.list.delete(value); + } + has(value: unknown, resolve: (v: unknown) => unknown) { + if (this.list.has(value)) return true; + + let item, + values = this.refs.values(); + while (((item = values.next()), !item.done)) + if (resolve(item.value) === value) return true; + + return false; + } + + clone() { + const next = new RefSet(); + next.list = new Set(this.list); + next.refs = new Map(this.refs); + return next; + } + + merge(newItems: RefSet, removeItems: RefSet) { + const next = this.clone(); + newItems.list.forEach((value) => next.add(value)); + newItems.refs.forEach((value) => next.add(value)); + removeItems.list.forEach((value) => next.delete(value)); + removeItems.refs.forEach((value) => next.delete(value)); + return next; + } +} + +export type SchemaSpec = { + nullability: Nullability; + presence: Presence; + default?: TDefault | (() => TDefault); + hasDefault?: boolean; + abortEarly?: boolean; + strip?: boolean; + strict?: boolean; + recursive?: boolean; + label?: string | undefined; + meta?: any; +}; + +export type SchemaOptions = { + type?: string; + spec?: SchemaSpec; +}; + +export type AnyBase = BaseSchema; + +export default abstract class BaseSchema< + TCast = any, + TOutput = any, + TPresence extends Presence = Unset +> implements Schema { + readonly type: string; + + readonly __inputType!: TCast; + readonly __outputType!: ResolveOutput; + + readonly __isYupSchema__!: boolean; + + readonly deps: readonly string[] = []; + + tests: Test[]; + transforms: TransformFunction[]; + + private conditions: Condition[] = []; + + private _mutate?: boolean; + private _typeError?: Test; + private _whitelistError?: Test; + private _blacklistError?: Test; + + protected _whitelist: RefSet = new RefSet(); + protected _blacklist: RefSet = new RefSet(); + + protected exclusiveTests: Record = Object.create(null); + + // optional!: () => MixedSchema; + + spec: SchemaSpec; + + constructor(options?: SchemaOptions) { + this.tests = []; + this.transforms = []; + + this.withMutation(() => { + this.typeError(locale.notType); + }); + + this.type = options?.type || ('mixed' as const); + + this.spec = { + strip: false, + strict: false, + abortEarly: true, + recursive: true, + label: undefined, + meta: undefined, + nullability: UNSET, + presence: UNSET, + ...options?.spec, + }; + } + + // TODO: remove + get _type() { + return this.type; + } + + protected _typeCheck(_value: any): _value is NonNullable { + return true; + } + + clone(spec?: Partial>): this { + if (this._mutate) { + if (spec) Object.assign(this.spec, spec); + return this; + } + + // if the nested value is a schema we can skip cloning, since + // they are already immutable + const next: AnyBase = Object.create(Object.getPrototypeOf(this)); + + // @ts-expect-error this is readonly + next.type = this.type; + + next._typeError = this._typeError; + next._whitelistError = this._whitelistError; + next._blacklistError = this._blacklistError; + next._whitelist = this._whitelist.clone(); + next._blacklist = this._blacklist.clone(); + next.exclusiveTests = { ...this.exclusiveTests }; + + // @ts-expect-error this is readonly + next.deps = [...this.deps]; + next.conditions = [...this.conditions]; + next.tests = [...this.tests]; + next.transforms = [...this.transforms]; + next.spec = cloneDeep({ ...this.spec, ...spec }); + + return next as this; + } + + label(label: string) { + var next = this.clone(); + next.spec.label = label; + return next; + } + + meta(): Record | undefined; + meta(obj: Record): void; + meta(...args: [Record?]) { + if (args.length === 0) return this.spec.meta; + + let next = this.clone(); + next.spec.meta = Object.assign(next.spec.meta || {}, args[0]); + return next; + } + + withMutation(fn: (schema: this) => T): T { + let before = this._mutate; + this._mutate = true; + let result = fn(this); + this._mutate = before; + return result; + } + + concat( + schema: TOther, + ): TOther extends BaseSchema + ? BaseSchema + : never; + concat(schema: AnyBase): AnyBase; + concat(schema: AnyBase): AnyBase { + if (!schema || schema === this) return this; + + if (schema.type !== this.type && this.type !== 'mixed') + throw new TypeError( + `You cannot \`concat()\` schema's of different types: ${this.type} and ${schema.type}`, + ); + + let base = this; + let combined = schema.clone(); + + const mergedSpec = { ...base.spec, ...combined.spec }; + + if (combined.spec.nullability === UNSET) + mergedSpec.nullability = base.spec.nullability; + + if (combined.spec.presence === UNSET) + mergedSpec.presence = base.spec.presence; + + combined.spec = mergedSpec; + + combined._typeError ||= base._typeError; + combined._whitelistError ||= base._whitelistError; + combined._blacklistError ||= base._blacklistError; + + // manually merge the blacklist/whitelist (the other `schema` takes + // precedence in case of conflicts) + combined._whitelist = base._whitelist.merge( + schema._whitelist, + schema._blacklist, + ); + combined._blacklist = base._blacklist.merge( + schema._blacklist, + schema._whitelist, + ); + + // start with the current tests + combined.tests = base.tests; + combined.exclusiveTests = base.exclusiveTests; + + // manually add the new tests to ensure + // the deduping logic is consistent + combined.withMutation((next) => { + schema.tests.forEach((fn) => { + next.test(fn.OPTIONS); + }); + }); + + return combined as any; + } + + isType(v: any) { + if (this.spec.nullability === 'nullable' && v === null) return true; + return this._typeCheck(v); + } + + resolve(options: ResolveOptions) { + let schema = this; + + if (schema.conditions.length) { + let conditions = schema.conditions; + + schema = schema.clone(); + schema.conditions = []; + schema = conditions.reduce( + (schema, condition) => condition.resolve(schema, options), + schema, + ); + + schema = schema.resolve(options); + } + + return schema; + } + + /** + * + * @param {*} value + * @param {Object} options + * @param {*=} options.parent + * @param {*=} options.context + */ + cast(value: any, options: CastOptions = {}): TCast { + let resolvedSchema = this.resolve({ + value, + ...options, + // parent: options.parent, + // context: options.context, + }); + + let result = resolvedSchema._cast(value, options); + + if ( + value !== undefined && + options.assert !== false && + resolvedSchema.isType(result) !== true + ) { + let formattedValue = printValue(value); + let formattedResult = printValue(result); + throw new TypeError( + `The value of ${ + options.path || 'field' + } could not be cast to a value ` + + `that satisfies the schema type: "${resolvedSchema._type}". \n\n` + + `attempted value: ${formattedValue} \n` + + (formattedResult !== formattedValue + ? `result of cast: ${formattedResult}` + : ''), + ); + } + + return result; + } + + protected _cast(rawValue: any, _options: CastOptions) { + let value = + rawValue === undefined + ? rawValue + : this.transforms.reduce( + (value, fn) => fn.call(this, value, rawValue), + rawValue, + ); + + if (value === undefined) { + value = this.getDefault(); + } + + return value; + } + + protected _validate( + _value: any, + options: InternalOptions = {}, + cb: Callback, + ): void { + let { + sync, + path, + from = [], + originalValue = _value, + strict = this.spec.strict, + abortEarly = this.spec.abortEarly, + } = options; + + let value = _value; + if (!strict) { + // this._validating = true; + value = this._cast(value, { assert: false, ...options }); + // this._validating = false; + } + // value is cast, we can check if it meets type requirements + let args = { + value, + path, + options, + originalValue, + schema: this, + label: this.spec.label, + sync, + from, + }; + + let initialTests = []; + + if (this._typeError) initialTests.push(this._typeError); + if (this._whitelistError) initialTests.push(this._whitelistError); + if (this._blacklistError) initialTests.push(this._blacklistError); + + runTests( + { + args, + value, + path, + sync, + tests: initialTests, + endEarly: abortEarly, + }, + (err) => { + if (err) return void cb(err, value); + + runTests( + { + tests: this.tests, + args, + path, + sync, + value, + endEarly: abortEarly, + }, + cb, + ); + }, + ); + } + + validate( + value: any, + options?: ValidateOptions, + ): Promise; + validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { + let schema = this.resolve({ ...options, value }); + + // callback case is for nested validations + return typeof maybeCb === 'function' + ? schema._validate(value, options, maybeCb) + : new Promise((resolve, reject) => + schema._validate(value, options, (err, value) => { + if (err) reject(err); + else resolve(value); + }), + ); + } + + validateSync( + value: any, + options: ValidateOptions = {}, + ): this['__outputType'] { + let schema = this.resolve({ ...options, value }); + let result: any; + + schema._validate(value, { ...options, sync: true }, (err, value) => { + if (err) throw err; + result = value; + }); + + return result; + } + + isValid(value: any, options: ValidateOptions): Promise { + return this.validate(value, options) + .then(() => true) + .catch((err) => { + if (ValidationError.isError(err)) return false; + throw err; + }); + } + + isValidSync(value: any, options: ValidateOptions): boolean { + try { + this.validateSync(value, options); + return true; + } catch (err) { + if (ValidationError.isError(err)) return false; + throw err; + } + } + + protected _getDefault() { + let defaultValue = this.spec.default; + + if (defaultValue == null) { + return defaultValue; + } + return typeof defaultValue === 'function' + ? defaultValue.call(this) + : cloneDeep(defaultValue); + } + + getDefault(options?: ResolveOptions): TCast { + let schema = this.resolve(options || {}); + return schema._getDefault(); + } + + default>( + def: TNextDefault | (() => TNextDefault), + ): any { + if (arguments.length === 0) { + return this._getDefault(); + } + + let next = this.clone({ default: def }); + + return next as any; + } + + strict(isStrict = true) { + var next = this.clone(); + next.spec.strict = isStrict; + return next; + } + + protected _isPresent(value: unknown) { + return value != null; + } + + defined(message = locale.defined): any { + return this.test({ + message, + name: 'defined', + exclusive: true, + test(value) { + return value !== undefined; + }, + }); + } + + required(message = locale.required): any { + return this.clone({ presence: 'required' }).withMutation((s) => + s.test({ + message, + name: 'required', + exclusive: true, + test(value) { + return this.schema._isPresent(value); + }, + }), + ) as any; + } + + notRequired(): any { + var next = this.clone({ presence: 'optional' }); + next.tests = next.tests.filter((test) => test.OPTIONS.name !== 'required'); + return next as any; + } + + nullable(isNullable?: true): any; + nullable(isNullable: false): any; + nullable(isNullable = true): any { + var next = this.clone({ + nullability: isNullable ? 'nullable' : 'nonnullable', + }); + + return next as any; + } + + transform(fn: TransformFunction) { + var next = this.clone(); + next.transforms.push(fn); + return next; + } + + /** + * Adds a test function to the schema's queue of tests. + * tests can be exclusive or non-exclusive. + * + * - exclusive tests, will replace any existing tests of the same name. + * - non-exclusive: can be stacked + * + * If a non-exclusive test is added to a schema with an exclusive test of the same name + * the exclusive test is removed and further tests of the same name will be stacked. + * + * If an exclusive test is added to a schema with non-exclusive tests of the same name + * the previous tests are removed and further tests of the same name will replace each other. + */ + test(options: TestConfig): this; + test(test: TestFunction): this; + test(name: string, test: TestFunction): this; + test(name: string, message: Message, test: TestFunction): this; + test(...args: any[]) { + let opts: TestConfig; + + if (args.length === 1) { + if (typeof args[0] === 'function') { + opts = { test: args[0] }; + } else { + opts = args[0]; + } + } else if (args.length === 2) { + opts = { name: args[0], test: args[1] }; + } else { + opts = { name: args[0], message: args[1], test: args[2] }; + } + + if (opts.message === undefined) opts.message = locale.default; + + if (typeof opts.test !== 'function') + throw new TypeError('`test` is a required parameters'); + + let next = this.clone(); + let validate = createValidation(opts); + + let isExclusive = + opts.exclusive || (opts.name && next.exclusiveTests[opts.name] === true); + + if (opts.exclusive) { + if (!opts.name) + throw new TypeError( + 'Exclusive tests must provide a unique `name` identifying the test', + ); + } + + if (opts.name) next.exclusiveTests[opts.name] = !!opts.exclusive; + + next.tests = next.tests.filter((fn) => { + if (fn.OPTIONS.name === opts.name) { + if (isExclusive) return false; + if (fn.OPTIONS.test === validate.OPTIONS.test) return false; + } + return true; + }); + + next.tests.push(validate); + + return next; + } + + when(options: ConditionOptions): this; + when(keys: string | string[], options: ConditionOptions): this; + when( + keys: string | string[] | ConditionOptions, + options?: ConditionOptions, + ) { + if (!Array.isArray(keys) && typeof keys !== 'string') { + options = keys; + keys = '.'; + } + + let next = this.clone(); + let deps = toArray(keys).map((key) => new Ref(key)); + + deps.forEach((dep) => { + // @ts-ignore + if (dep.isSibling) next.deps.push(dep.key); + }); + + next.conditions.push(new Condition(deps, options!)); + + return next; + } + + typeError(message: Message) { + var next = this.clone(); + + next._typeError = createValidation({ + message, + name: 'typeError', + test(value) { + if (value !== undefined && !this.schema.isType(value)) + return this.createError({ + params: { + type: this.schema._type, + }, + }); + return true; + }, + }); + return next; + } + + oneOf(enums: Maybe[], message = locale.oneOf): this { + var next = this.clone(); + + enums.forEach((val) => { + next._whitelist.add(val); + next._blacklist.delete(val); + }); + + next._whitelistError = createValidation({ + message, + name: 'oneOf', + test(value) { + if (value === undefined) return true; + let valids = this.schema._whitelist; + + return valids.has(value, this.resolve) + ? true + : this.createError({ + params: { + values: valids.toArray().join(', '), + }, + }); + }, + }); + + return next; + } + + notOneOf( + enums: Maybe[], + message = locale.notOneOf, + ): this { + var next = this.clone(); + enums.forEach((val) => { + next._blacklist.add(val); + next._whitelist.delete(val); + }); + + next._blacklistError = createValidation({ + message, + name: 'notOneOf', + test(value) { + let invalids = this.schema._blacklist; + if (invalids.has(value, this.resolve)) + return this.createError({ + params: { + values: invalids.toArray().join(', '), + }, + }); + return true; + }, + }); + + return next; + } + + strip(strip = true) { + let next = this.clone(); + next.spec.strip = strip; + return next; + } + + describe() { + const next = this.clone(); + const { label, meta } = next.spec; + const description: SchemaDescription = { + meta, + label, + type: next.type, + oneOf: next._whitelist.describe(), + notOneOf: next._blacklist.describe(), + tests: next.tests + .map((fn) => ({ name: fn.OPTIONS.name, params: fn.OPTIONS.params })) + .filter( + (n, idx, list) => list.findIndex((c) => c.name === n.name) === idx, + ), + }; + + return description; + } +} + +export default interface BaseSchema< + TCast, + TOutput, + TPresence extends Presence +> { + validateAt( + path: string, + value: any, + options?: ValidateOptions, + ): Promise; + validateSyncAt(path: string, value: any, options?: ValidateOptions): TOutput; + equals: BaseSchema['oneOf']; + is: BaseSchema['oneOf']; + not: BaseSchema['notOneOf']; + nope: BaseSchema['notOneOf']; + optional: BaseSchema['notRequired']; +} + +// @ts-expect-error +BaseSchema.prototype.__isYupSchema__ = true; + +for (const method of ['validate', 'validateSync']) + BaseSchema.prototype[ + `${method}At` as 'validateAt' | 'validateSyncAt' + ] = function (path: string, value: any, options: ValidateOptions = {}) { + const { parent, parentPath, schema } = getIn( + this, + path, + value, + options.context, + ); + return schema[method](parent && parent[parentPath], { + ...options, + parent, + path, + }); + }; + +for (const alias of ['equals', 'is'] as const) + BaseSchema.prototype[alias] = BaseSchema.prototype.oneOf; + +for (const alias of ['not', 'nope'] as const) + BaseSchema.prototype[alias] = BaseSchema.prototype.notOneOf; + +BaseSchema.prototype.optional = BaseSchema.prototype.notRequired; diff --git a/src/array.ts b/src/array.ts index 79f012895..b59985046 100644 --- a/src/array.ts +++ b/src/array.ts @@ -1,51 +1,55 @@ import isAbsent from './util/isAbsent'; import isSchema from './util/isSchema'; import printValue from './util/printValue'; -import MixedSchema, { AnyMixed } from './mixed'; import { array as locale, MixedLocale } from './locale'; import runTests, { RunTest } from './util/runTests'; import type { SchemaInnerTypeDescription, SchemaSpec } from './Schema'; -import type { InternalOptions, Callback, Message, Maybe } from './types'; +import type { + InternalOptions, + Callback, + Message, + Maybe, + PreserveOptionals, +} from './types'; import ValidationError from './ValidationError'; import type Reference from './Reference'; import { Asserts, - Default, - Nullability, + Defined, Presence, - ResolveInput, - ResolveOutput, + StrictNonNullable, TypeOf, Unset, } from './util/types'; +import BaseSchema, { AnyBase } from './Base'; type RefectorFn = (value: any, index: number, array: any[]) => boolean; type MaybeArray = Maybe[]>; -export function create(type?: TInner) { - return new ArraySchema(type) as OptionalArraySchema; +export function create(type?: TInner) { + return new ArraySchema(type); } // type MaintainNull = T extends null ? U | null : U; // type MaintainUndefined = T extends undefined ? U | null : U; // type WithOptionality = MaintainUndefined>; -type Type = T extends MixedSchema - ? TType - : never; +// export type TypeOfElement = T['__inputType']; abstract class BasicArraySchema< TElement, TType extends Maybe, TOut extends Maybe, TPresence extends Presence -> extends MixedSchema {} +> extends BaseSchema {} export default class ArraySchema< - T extends AnyMixed = AnyMixed, + T extends AnyBase = AnyBase, + TType extends Maybe[]> = TypeOf[] | undefined, + TOut extends Maybe[]> = Asserts[] | undefined, TPresence extends Presence = Unset -> extends BasicArraySchema, Type[], Asserts[], TPresence> { +> extends BaseSchema { innerType: T | undefined; constructor(type?: T) { @@ -69,12 +73,12 @@ export default class ArraySchema< }); } - private get _subType() { - return this.innerType; + protected _typeCheck(v: any): v is NonNullable { + return Array.isArray(v); } - protected _typeCheck(v: any): v is any[] { - return Array.isArray(v); + private get _subType() { + return this.innerType; } protected _cast(_value: any, _opts: InternalOptions) { @@ -174,16 +178,16 @@ export default class ArraySchema< return next; } - // concat>( - // schema: TOther, - // ): TOther extends ArraySchema - // ? ArraySchema< - // T, - // D, - // N extends Unset ? TNullablity : N, - // P extends Unset ? TPresence : P - // > - // : never; + concat>( + schema: TOther, + ): TOther extends ArraySchema + ? ArraySchema< + T, + C | PreserveOptionals, + O | PreserveOptionals, + P extends Unset ? TPresence : P + > + : never; concat(schema: any): any; concat(schema: any): any { let next = super.concat(schema) as this; @@ -198,9 +202,14 @@ export default class ArraySchema< return next; } - of( + of( schema: TInner, - ): ArraySchema { + ): ArraySchema< + TInner, + TypeOf[] | PreserveOptionals, + TypeOf[] | PreserveOptionals, + TPresence + > { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -261,7 +270,7 @@ export default class ArraySchema< } ensure() { - return this.default(() => [] as Type[]).transform((val, original) => { + return this.default(() => [] as TypeOf[]).transform((val, original) => { // We don't want to return `null` for nullable schema if (this._typeCheck(val)) return val; return original == null ? [] : [].concat(original); @@ -285,104 +294,117 @@ export default class ArraySchema< } } -interface NullableArraySchema< - T extends AnyMixed = AnyMixed, - TPresence extends Presence = Unset -> extends BasicArraySchema< - Type, - Type[] | null, - Asserts[] | null, - TPresence - > { - of( - schema: TInner, - ): NullableArraySchema; - nullable(isNullable?: true): NullableArraySchema; - nullable(isNullable: false): ArraySchema; - nullable(isNullable?: boolean): ArraySchema; - - defined(msg?: MixedLocale['defined']): NullableArraySchema; - required(msg?: MixedLocale['required']): NullableArraySchema; - notRequired(): ArraySchema; - - default[] | null | undefined>( - def: TNextDefault | (() => TNextDefault), - ): TNextDefault extends undefined - ? NullableOptionalArraySchema - : this; -} +// interface NullableArraySchema< +// T extends AnyBase = AnyBase, +// TPresence extends Presence = Unset +// > extends BasicArraySchema< +// Type, +// Type[] | null, +// Asserts[] | null, +// TPresence +// > { +// of( +// schema: TInner, +// ): NullableArraySchema; +// nullable(isNullable?: true): NullableArraySchema; +// nullable(isNullable: false): ArraySchema; +// nullable(isNullable?: boolean): ArraySchema; + +// defined(msg?: MixedLocale['defined']): NullableArraySchema; +// required(msg?: MixedLocale['required']): NullableArraySchema; +// notRequired(): ArraySchema; + +// default[] | null | undefined>( +// def: TNextDefault | (() => TNextDefault), +// ): TNextDefault extends undefined +// ? NullableOptionalArraySchema +// : this; +// } + +// interface OptionalArraySchema< +// T extends AnyBase = AnyBase, +// TPresence extends Presence = Unset +// > extends BasicArraySchema< +// Type, +// Type[] | undefined, +// Asserts[] | undefined, +// TPresence +// > { +// of( +// schema: TInner, +// ): NullableArraySchema; +// nullable(isNullable?: true): NullableArraySchema; +// nullable(isNullable: false): ArraySchema; +// nullable(isNullable?: boolean): ArraySchema; + +// defined(msg?: MixedLocale['defined']): ArraySchema; +// required(msg?: MixedLocale['required']): ArraySchema; +// notRequired(): ArraySchema; + +// default[] | null | undefined>( +// def: TNextDefault | (() => TNextDefault), +// ): TNextDefault extends undefined +// ? OptionalArraySchema +// : ArraySchema; +// } + +// interface NullableOptionalArraySchema< +// T extends AnyBase = AnyBase, +// TPresence extends Presence = Unset +// > extends BasicArraySchema< +// Type, +// Type[] | null | undefined, +// Asserts[] | null | undefined, +// TPresence +// > { +// of( +// schema: TInner, +// ): NullableArraySchema; +// nullable(isNullable?: true): NullableOptionalArraySchema; +// nullable(isNullable: false): OptionalArraySchema; +// nullable(isNullable?: boolean): OptionalArraySchema; + +// defined( +// msg?: MixedLocale['defined'], +// ): NullableOptionalArraySchema; +// required( +// msg?: MixedLocale['required'], +// ): NullableOptionalArraySchema; +// notRequired(): NullableOptionalArraySchema; + +// default[] | null | undefined>( +// def: TNextDefault | (() => TNextDefault), +// ): TNextDefault extends undefined ? this : NullableArraySchema; +// } -interface OptionalArraySchema< - T extends AnyMixed = AnyMixed, - TPresence extends Presence = Unset -> extends BasicArraySchema< - Type, - Type[] | undefined, - Asserts[] | undefined, - TPresence - > { - of( - schema: TInner, - ): NullableArraySchema; - nullable(isNullable?: true): NullableArraySchema; - nullable(isNullable: false): ArraySchema; - nullable(isNullable?: boolean): ArraySchema; - - defined(msg?: MixedLocale['defined']): ArraySchema; - required(msg?: MixedLocale['required']): ArraySchema; - notRequired(): ArraySchema; - - default[] | null | undefined>( - def: TNextDefault | (() => TNextDefault), - ): TNextDefault extends undefined - ? OptionalArraySchema - : ArraySchema; -} - -interface NullableOptionalArraySchema< - T extends AnyMixed = AnyMixed, +export default interface ArraySchema< + T extends AnyBase, + TType extends Maybe[]>, + TOut extends Maybe[]>, TPresence extends Presence = Unset -> extends BasicArraySchema< - Type, - Type[] | null | undefined, - Asserts[] | null | undefined, - TPresence - > { - of( - schema: TInner, - ): NullableArraySchema; - nullable(isNullable?: true): NullableOptionalArraySchema; - nullable(isNullable: false): OptionalArraySchema; - nullable(isNullable?: boolean): OptionalArraySchema; - - defined( - msg?: MixedLocale['defined'], - ): NullableOptionalArraySchema; +> extends BaseSchema { + defined(msg?: MixedLocale['defined']): ArraySchema; required( msg?: MixedLocale['required'], - ): NullableOptionalArraySchema; - notRequired(): NullableOptionalArraySchema; - - default[] | null | undefined>( - def: TNextDefault | (() => TNextDefault), - ): TNextDefault extends undefined ? this : NullableArraySchema; -} - -export default interface ArraySchema< - T extends AnyMixed = AnyMixed, - TPresence extends Presence = Unset -> extends BasicArraySchema, Type[], Asserts[], TPresence> { - defined(msg?: MixedLocale['defined']): ArraySchema; - required(msg?: MixedLocale['required']): ArraySchema; - notRequired(): ArraySchema; - - nullable(isNullable?: true): NullableArraySchema; - nullable(isNullable: false): ArraySchema; - nullable(isNullable?: boolean): ArraySchema; + ): ArraySchema; + notRequired(): ArraySchema; + + nullable( + isNullable?: true, + ): ArraySchema; + nullable( + isNullable: false, + ): ArraySchema< + T, + StrictNonNullable, + StrictNonNullable, + TPresence + >; + nullable(isNullable?: boolean): ArraySchema; - default[] | null | undefined>( + default[] | null | undefined>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? OptionalArraySchema - : ArraySchema; + ? ArraySchema + : ArraySchema, Defined, TPresence>; } diff --git a/src/mixed.ts b/src/mixed.ts index e769ebb64..a08da68da 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -1,7 +1,7 @@ // @ts-ignore import cloneDeep from 'nanoclone'; -import { mixed as locale } from './locale'; +import { mixed as locale, MixedLocale } from './locale'; import Condition, { ConditionOptions, ResolveOptions } from './Condition'; import runTests from './util/runTests'; import createValidation, { @@ -30,6 +30,7 @@ import Schema, { import { ValidationError } from '.'; import type { Default, + Defined, Nullability, Presence, ResolveInput, @@ -37,6 +38,7 @@ import type { StrictNonNullable, Unset, } from './util/types'; +import BaseSchema from './Base'; const UNSET = 'unset' as const; @@ -114,697 +116,28 @@ export type SchemaOptions = { spec?: SchemaSpec; }; -export type AnyMixed = MixedSchema; +export type AnyMixed = MixedSchema; export function create() { return new MixedSchema(); } export default class MixedSchema< - TCast = any, - TPresence extends Presence = Unset, - TOutput = TCast -> implements Schema { - readonly type: string; + TType = any, + TPresence extends Presence = Unset +> extends BaseSchema {} - readonly __inputType!: TCast; - readonly __outputType!: ResolveOutput; - - readonly __isYupSchema__!: boolean; - - readonly deps: readonly string[] = []; - - tests: Test[]; - transforms: TransformFunction[]; - - private conditions: Condition[] = []; - - private _mutate?: boolean; - private _typeError?: Test; - private _whitelistError?: Test; - private _blacklistError?: Test; - - protected _whitelist: RefSet = new RefSet(); - protected _blacklist: RefSet = new RefSet(); - - protected exclusiveTests: Record = Object.create(null); - - // optional!: () => MixedSchema; - - spec: SchemaSpec; - - constructor(options?: SchemaOptions) { - this.tests = []; - this.transforms = []; - - this.withMutation(() => { - this.typeError(locale.notType); - }); - - this.type = options?.type || ('mixed' as const); - - this.spec = { - strip: false, - strict: false, - abortEarly: true, - recursive: true, - label: undefined, - meta: undefined, - nullability: UNSET, - presence: UNSET, - ...options?.spec, - }; - } - - // TODO: remove - get _type() { - return this.type; - } - - protected _typeCheck(_value: any): _value is TCast { - return true; - } - - clone(spec?: Partial>): this { - if (this._mutate) { - if (spec) Object.assign(this.spec, spec); - return this; - } - - // if the nested value is a schema we can skip cloning, since - // they are already immutable - const next: MixedSchema = Object.create( - Object.getPrototypeOf(this), - ); - - // @ts-expect-error this is readonly - next.type = this.type; - - next._typeError = this._typeError; - next._whitelistError = this._whitelistError; - next._blacklistError = this._blacklistError; - next._whitelist = this._whitelist.clone(); - next._blacklist = this._blacklist.clone(); - next.exclusiveTests = { ...this.exclusiveTests }; - - // @ts-expect-error this is readonly - next.deps = [...this.deps]; - next.conditions = [...this.conditions]; - next.tests = [...this.tests]; - next.transforms = [...this.transforms]; - next.spec = cloneDeep({ ...this.spec, ...spec }); - - return next as this; - } - - label(label: string) { - var next = this.clone(); - next.spec.label = label; - return next; - } - - meta(): Record | undefined; - meta(obj: Record): void; - meta(...args: [Record?]) { - if (args.length === 0) return this.spec.meta; - - let next = this.clone(); - next.spec.meta = Object.assign(next.spec.meta || {}, args[0]); - return next; - } - - withMutation(fn: (schema: this) => T): T { - let before = this._mutate; - this._mutate = true; - let result = fn(this); - this._mutate = before; - return result; - } - - concat( - schema: TOther, - ): TOther extends MixedSchema - ? MixedSchema - : never; - concat(schema: MixedSchema): MixedSchema; - concat(schema: MixedSchema): MixedSchema { - if (!schema || schema === this) return this; - - if (schema.type !== this.type && this.type !== 'mixed') - throw new TypeError( - `You cannot \`concat()\` schema's of different types: ${this.type} and ${schema.type}`, - ); - - let base = this; - let combined = schema.clone(); - - const mergedSpec = { ...base.spec, ...combined.spec }; - - if (combined.spec.nullability === UNSET) - mergedSpec.nullability = base.spec.nullability; - - if (combined.spec.presence === UNSET) - mergedSpec.presence = base.spec.presence; - - combined.spec = mergedSpec; - - combined._typeError ||= base._typeError; - combined._whitelistError ||= base._whitelistError; - combined._blacklistError ||= base._blacklistError; - - // manually merge the blacklist/whitelist (the other `schema` takes - // precedence in case of conflicts) - combined._whitelist = base._whitelist.merge( - schema._whitelist, - schema._blacklist, - ); - combined._blacklist = base._blacklist.merge( - schema._blacklist, - schema._whitelist, - ); - - // start with the current tests - combined.tests = base.tests; - combined.exclusiveTests = base.exclusiveTests; - - // manually add the new tests to ensure - // the deduping logic is consistent - combined.withMutation((next) => { - schema.tests.forEach((fn) => { - next.test(fn.OPTIONS); - }); - }); - - return combined as any; - } - - isType(v: any) { - if (this.spec.nullability === 'nullable' && v === null) return true; - return this._typeCheck(v); - } - - resolve(options: ResolveOptions) { - let schema = this; - - if (schema.conditions.length) { - let conditions = schema.conditions; - - schema = schema.clone(); - schema.conditions = []; - schema = conditions.reduce( - (schema, condition) => condition.resolve(schema, options), - schema, - ); - - schema = schema.resolve(options); - } - - return schema; - } - - /** - * - * @param {*} value - * @param {Object} options - * @param {*=} options.parent - * @param {*=} options.context - */ - cast(value: any, options: CastOptions = {}): TCast { - let resolvedSchema = this.resolve({ - value, - ...options, - // parent: options.parent, - // context: options.context, - }); - - let result = resolvedSchema._cast(value, options); - - if ( - value !== undefined && - options.assert !== false && - resolvedSchema.isType(result) !== true - ) { - let formattedValue = printValue(value); - let formattedResult = printValue(result); - throw new TypeError( - `The value of ${ - options.path || 'field' - } could not be cast to a value ` + - `that satisfies the schema type: "${resolvedSchema._type}". \n\n` + - `attempted value: ${formattedValue} \n` + - (formattedResult !== formattedValue - ? `result of cast: ${formattedResult}` - : ''), - ); - } - - return result; - } - - protected _cast(rawValue: any, _options: CastOptions) { - let value = - rawValue === undefined - ? rawValue - : this.transforms.reduce( - (value, fn) => fn.call(this, value, rawValue), - rawValue, - ); - - if (value === undefined) { - value = this.getDefault(); - } - - return value; - } - - protected _validate( - _value: any, - options: InternalOptions = {}, - cb: Callback, - ): void { - let { - sync, - path, - from = [], - originalValue = _value, - strict = this.spec.strict, - abortEarly = this.spec.abortEarly, - } = options; - - let value = _value; - if (!strict) { - // this._validating = true; - value = this._cast(value, { assert: false, ...options }); - // this._validating = false; - } - // value is cast, we can check if it meets type requirements - let args = { - value, - path, - options, - originalValue, - schema: this, - label: this.spec.label, - sync, - from, - }; - - let initialTests = []; - - if (this._typeError) initialTests.push(this._typeError); - if (this._whitelistError) initialTests.push(this._whitelistError); - if (this._blacklistError) initialTests.push(this._blacklistError); - - runTests( - { - args, - value, - path, - sync, - tests: initialTests, - endEarly: abortEarly, - }, - (err) => { - if (err) return void cb(err, value); - - runTests( - { - tests: this.tests, - args, - path, - sync, - value, - endEarly: abortEarly, - }, - cb, - ); - }, - ); - } - - validate(value: any, options?: ValidateOptions): Promise; - validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { - let schema = this.resolve({ ...options, value }); - - // callback case is for nested validations - return typeof maybeCb === 'function' - ? schema._validate(value, options, maybeCb) - : new Promise((resolve, reject) => - schema._validate(value, options, (err, value) => { - if (err) reject(err); - else resolve(value); - }), - ); - } - - validateSync(value: any, options: ValidateOptions = {}): TOutput { - let schema = this.resolve({ ...options, value }); - let result: any; - - schema._validate(value, { ...options, sync: true }, (err, value) => { - if (err) throw err; - result = value; - }); - - return result; - } - - isValid(value: any, options: ValidateOptions): Promise { - return this.validate(value, options) - .then(() => true) - .catch((err) => { - if (ValidationError.isError(err)) return false; - throw err; - }); - } - - isValidSync(value: any, options: ValidateOptions): boolean { - try { - this.validateSync(value, options); - return true; - } catch (err) { - if (ValidationError.isError(err)) return false; - throw err; - } - } - - protected _getDefault() { - let defaultValue = this.spec.default; - - if (defaultValue == null) { - return defaultValue; - } - return typeof defaultValue === 'function' - ? defaultValue.call(this) - : cloneDeep(defaultValue); - } - - getDefault(options?: ResolveOptions): TCast { - let schema = this.resolve(options || {}); - return schema._getDefault(); - } - - default>( +export default interface MixedSchema { + default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? MixedSchema - : MixedSchema, TPresence> { - if (arguments.length === 0) { - return this._getDefault(); - } - - let next = this.clone({ default: def }); - - return next as any; - } - - strict(isStrict = true) { - var next = this.clone(); - next.spec.strict = isStrict; - return next; - } - - protected _isPresent(value: unknown) { - return value != null; - } - - required(message = locale.required): MixedSchema { - return this.clone({ presence: 'required' }).withMutation((s) => - s.test({ - message, - name: 'required', - exclusive: true, - test(value) { - return this.schema._isPresent(value); - }, - }), - ) as any; - } - - notRequired(): MixedSchema { - var next = this.clone({ presence: 'optional' }); - next.tests = next.tests.filter((test) => test.OPTIONS.name !== 'required'); - return next as any; - } - - nullable(isNullable?: true): MixedSchema; - nullable(isNullable: false): MixedSchema, TPresence>; - nullable(isNullable = true): MixedSchema { - var next = this.clone({ - nullability: isNullable ? 'nullable' : 'nonnullable', - }); - - return next as any; - } - - transform(fn: TransformFunction) { - var next = this.clone(); - next.transforms.push(fn); - return next; - } - - /** - * Adds a test function to the schema's queue of tests. - * tests can be exclusive or non-exclusive. - * - * - exclusive tests, will replace any existing tests of the same name. - * - non-exclusive: can be stacked - * - * If a non-exclusive test is added to a schema with an exclusive test of the same name - * the exclusive test is removed and further tests of the same name will be stacked. - * - * If an exclusive test is added to a schema with non-exclusive tests of the same name - * the previous tests are removed and further tests of the same name will replace each other. - */ - test(options: TestConfig): this; - test(test: TestFunction): this; - test(name: string, test: TestFunction): this; - test(name: string, message: Message, test: TestFunction): this; - test(...args: any[]) { - let opts: TestConfig; - - if (args.length === 1) { - if (typeof args[0] === 'function') { - opts = { test: args[0] }; - } else { - opts = args[0]; - } - } else if (args.length === 2) { - opts = { name: args[0], test: args[1] }; - } else { - opts = { name: args[0], message: args[1], test: args[2] }; - } - - if (opts.message === undefined) opts.message = locale.default; - - if (typeof opts.test !== 'function') - throw new TypeError('`test` is a required parameters'); - - let next = this.clone(); - let validate = createValidation(opts); - - let isExclusive = - opts.exclusive || (opts.name && next.exclusiveTests[opts.name] === true); - - if (opts.exclusive) { - if (!opts.name) - throw new TypeError( - 'Exclusive tests must provide a unique `name` identifying the test', - ); - } - - if (opts.name) next.exclusiveTests[opts.name] = !!opts.exclusive; - - next.tests = next.tests.filter((fn) => { - if (fn.OPTIONS.name === opts.name) { - if (isExclusive) return false; - if (fn.OPTIONS.test === validate.OPTIONS.test) return false; - } - return true; - }); - - next.tests.push(validate); - - return next; - } - - when(options: ConditionOptions): this; - when(keys: string | string[], options: ConditionOptions): this; - when( - keys: string | string[] | ConditionOptions, - options?: ConditionOptions, - ) { - if (!Array.isArray(keys) && typeof keys !== 'string') { - options = keys; - keys = '.'; - } - - let next = this.clone(); - let deps = toArray(keys).map((key) => new Ref(key)); - - deps.forEach((dep) => { - // @ts-ignore - if (dep.isSibling) next.deps.push(dep.key); - }); - - next.conditions.push(new Condition(deps, options!)); - - return next; - } - - typeError(message: Message) { - var next = this.clone(); - - next._typeError = createValidation({ - message, - name: 'typeError', - test(value) { - if (value !== undefined && !this.schema.isType(value)) - return this.createError({ - params: { - type: this.schema._type, - }, - }); - return true; - }, - }); - return next; - } - - oneOf(enums: Maybe[], message = locale.oneOf): this { - var next = this.clone(); - - enums.forEach((val) => { - next._whitelist.add(val); - next._blacklist.delete(val); - }); - - next._whitelistError = createValidation({ - message, - name: 'oneOf', - test(value) { - if (value === undefined) return true; - let valids = this.schema._whitelist; + ? MixedSchema + : MixedSchema, TPresence>; - return valids.has(value, this.resolve) - ? true - : this.createError({ - params: { - values: valids.toArray().join(', '), - }, - }); - }, - }); + defined(msg?: MixedLocale['defined']): MixedSchema; + required(msg?: MixedLocale['required']): MixedSchema; + notRequired(): MixedSchema; - return next as any; - } - - notOneOf( - enums: Maybe[], - message = locale.notOneOf, - ): this { - var next = this.clone(); - enums.forEach((val) => { - next._blacklist.add(val); - next._whitelist.delete(val); - }); - - next._blacklistError = createValidation({ - message, - name: 'notOneOf', - test(value) { - let invalids = this.schema._blacklist; - if (invalids.has(value, this.resolve)) - return this.createError({ - params: { - values: invalids.toArray().join(', '), - }, - }); - return true; - }, - }); - - return next; - } - - strip(strip = true) { - let next = this.clone(); - next.spec.strip = strip; - return next; - } - - describe() { - const next = this.clone(); - const { label, meta } = next.spec; - const description: SchemaDescription = { - meta, - label, - type: next.type, - oneOf: next._whitelist.describe(), - notOneOf: next._blacklist.describe(), - tests: next.tests - .map((fn) => ({ name: fn.OPTIONS.name, params: fn.OPTIONS.params })) - .filter( - (n, idx, list) => list.findIndex((c) => c.name === n.name) === idx, - ), - }; - - return description; - } - - defined(message = locale.defined): MixedSchema { - return this.test({ - message, - name: 'defined', - exclusive: true, - test(value) { - return value !== undefined; - }, - }) as any; - } + nullable(isNullable?: true): MixedSchema; + nullable(isNullable: false): MixedSchema, TPresence>; } - -export default interface MixedSchema< - TCast, - TPresence extends Presence, - TOutput -> { - validateAt( - path: string, - value: any, - options?: ValidateOptions, - ): Promise; - validateSyncAt(path: string, value: any, options?: ValidateOptions): TOutput; - equals: MixedSchema['oneOf']; - is: MixedSchema['oneOf']; - not: MixedSchema['notOneOf']; - nope: MixedSchema['notOneOf']; -} - -// @ts-expect-error -MixedSchema.prototype.__isYupSchema__ = true; - -for (const method of ['validate', 'validateSync']) - MixedSchema.prototype[ - `${method}At` as 'validateAt' | 'validateSyncAt' - ] = function (path: string, value: any, options: ValidateOptions = {}) { - const { parent, parentPath, schema } = getIn( - this, - path, - value, - options.context, - ); - return schema[method](parent && parent[parentPath], { - ...options, - parent, - path, - }); - }; - -for (const alias of ['equals', 'is'] as const) - MixedSchema.prototype[alias] = MixedSchema.prototype.oneOf; - -for (const alias of ['not', 'nope'] as const) - MixedSchema.prototype[alias] = MixedSchema.prototype.notOneOf; - -// MixedSchema.prototype.optional = MixedSchema.prototype.notRequired; diff --git a/src/number.ts b/src/number.ts index 426c8c4a2..4680c3ac9 100644 --- a/src/number.ts +++ b/src/number.ts @@ -10,6 +10,7 @@ import type { StrictNonNullable, Unset, } from './util/types'; +import BaseSchema from './Base'; let isNaN = (value: Maybe) => value != +value!; @@ -20,7 +21,7 @@ export function create() { export default class NumberSchema< TType extends Maybe = number | undefined, TPresence extends Presence = Unset -> extends MixedSchema { +> extends BaseSchema { constructor() { super({ type: 'number' }); @@ -137,7 +138,7 @@ export default class NumberSchema< export default interface NumberSchema< TType extends Maybe, TPresence extends Presence -> extends MixedSchema { +> extends BaseSchema { default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined diff --git a/src/object.ts b/src/object.ts index e8d1e44b1..9ea2bd9c8 100644 --- a/src/object.ts +++ b/src/object.ts @@ -11,20 +11,18 @@ import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; import { SchemaObjectDescription } from './Schema'; -import { InternalOptions, Callback, Maybe } from './types'; +import { InternalOptions, Callback, Maybe, PreserveOptionals } from './types'; import ValidationError from './ValidationError'; import type { - ResolveInput, - ResolveOutput, - Nullability, Presence, Unset, TypedSchema, - StrictNonNullable, Defined, + StrictNonNullable, } from './util/types'; import type Reference from './Reference'; import Lazy, { LazyType } from './Lazy'; +import BaseSchema from './Base'; let isObject = (obj: any): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; @@ -87,20 +85,12 @@ type ObjectSchemaSpec = SchemaSpec & { const defaultSort = sortByKeyOrder([]); -abstract class BaseObjectSchema< - TType extends Maybe, - TOut extends Maybe, - TPresence extends Presence -> extends MixedSchema {} - export default class ObjectSchema< TShape extends ObjectShape = ObjectShape, + TType extends Maybe = TypeOfShape, + TOut extends Maybe = AssertsShape, TPresence extends Presence = Unset -> extends BaseObjectSchema< - TypeFromShape, - AssertsShape, - TPresence -> { +> extends BaseSchema { fields: TShape = Object.create(null); spec!: ObjectSchemaSpec; @@ -135,7 +125,7 @@ export default class ObjectSchema< }); } - protected _typeCheck(value: any): value is TType { + protected _typeCheck(value: any): value is NonNullable { return isObject(value) || typeof value === 'function'; } @@ -302,16 +292,16 @@ export default class ObjectSchema< return next; } - // concat>( - // schema: TOther, - // ): TOther extends ObjectSchema - // ? ObjectSchema< - // TShape & T, - // D & TDefault, - // N extends Unset ? TNullablity : N, - // P extends Unset ? TPresence : P - // > - // : never; + concat>( + schema: TOther, + ): TOther extends ObjectSchema + ? ObjectSchema< + TShape & S, + TypeOfShape | PreserveOptionals, + AssertsShape | PreserveOptionals, + P extends Unset ? TPresence : P + > + : never; concat(schema: AnyMixed): AnyMixed; concat(schema: any): any { let next = super.concat(schema) as any; @@ -470,100 +460,111 @@ export default class ObjectSchema< } } -interface NullableObjectSchema< - TShape extends ObjectShape, - TPresence extends Presence -> extends BaseObjectSchema< - TypeFromShape | null, - AssertsShape | null, - TPresence - > { - // default>>( - // def: TNextDefault | (() => TNextDefault), - // ): TNextDefault extends undefined - // ? NullableOptionalObjectSchema - // : this; - - defined( - msg?: MixedLocale['defined'], - ): NullableObjectSchema; - required( - msg?: MixedLocale['required'], - ): NullableObjectSchema; - notRequired(): NullableObjectSchema; - - nullable(isNullable?: true): NullableObjectSchema; - nullable(isNullable: false): ObjectSchema; -} - -interface OptionalObjectSchema< - TShape extends ObjectShape, - TPresence extends Presence -> extends BaseObjectSchema< - TypeFromShape | undefined, - AssertsShape | undefined, - TPresence - > { - // default>>( - // def: TNextDefault | (() => TNextDefault), - // ): TNextDefault extends undefined ? this : ObjectSchema; - - defined( - msg?: MixedLocale['defined'], - ): OptionalObjectSchema; - required( - msg?: MixedLocale['required'], - ): OptionalObjectSchema; - notRequired(): OptionalObjectSchema; - - nullable(isNullable?: true): NullableOptionalObjectSchema; - nullable(isNullable: false): OptionalObjectSchema; -} +// interface NullableObjectSchema< +// TShape extends ObjectShape, +// TPresence extends Presence +// > extends BaseObjectSchema< +// TypeFromShape | null, +// AssertsShape | null, +// TPresence +// > { +// // default>>( +// // def: TNextDefault | (() => TNextDefault), +// // ): TNextDefault extends undefined +// // ? NullableOptionalObjectSchema +// // : this; + +// defined( +// msg?: MixedLocale['defined'], +// ): NullableObjectSchema; +// required( +// msg?: MixedLocale['required'], +// ): NullableObjectSchema; +// notRequired(): NullableObjectSchema; + +// nullable(isNullable?: true): NullableObjectSchema; +// nullable(isNullable: false): ObjectSchema; +// } + +// interface OptionalObjectSchema< +// TShape extends ObjectShape, +// TPresence extends Presence +// > extends BaseObjectSchema< +// TypeFromShape | undefined, +// AssertsShape | undefined, +// TPresence +// > { +// default>>( +// def: TNextDefault | (() => TNextDefault), +// ): TNextDefault extends undefined ? this : ObjectSchema; + +// defined( +// msg?: MixedLocale['defined'], +// ): OptionalObjectSchema; +// required( +// msg?: MixedLocale['required'], +// ): OptionalObjectSchema; +// notRequired(): OptionalObjectSchema; + +// nullable(isNullable?: true): NullableOptionalObjectSchema; +// nullable(isNullable: false): OptionalObjectSchema; +// } + +// interface NullableOptionalObjectSchema< +// TShape extends ObjectShape, +// TPresence extends Presence +// > extends BaseObjectSchema< +// TypeFromShape | null | undefined, +// AssertsShape | null | undefined, +// TPresence +// > { +// default>>( +// def: TNextDefault | (() => TNextDefault), +// ): TNextDefault extends undefined +// ? this +// : NullableObjectSchema; + +// defined( +// msg?: MixedLocale['defined'], +// ): NullableOptionalObjectSchema; +// required( +// msg?: MixedLocale['required'], +// ): NullableOptionalObjectSchema; +// notRequired(): NullableOptionalObjectSchema; + +// nullable(isNullable?: true): NullableOptionalObjectSchema; +// nullable(isNullable: false): OptionalObjectSchema; +// } -interface NullableOptionalObjectSchema< +export default interface ObjectSchema< TShape extends ObjectShape, + TType extends Maybe, + TOut extends Maybe, TPresence extends Presence -> extends BaseObjectSchema< - TypeFromShape | null | undefined, - AssertsShape | null | undefined, - TPresence - > { - // default>>( - // def: TNextDefault | (() => TNextDefault), - // ): TNextDefault extends undefined - // ? this - // : NullableObjectSchema; +> extends BaseSchema { + default>>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined + ? ObjectSchema + : ObjectSchema, Defined, TPresence>; defined( msg?: MixedLocale['defined'], - ): NullableOptionalObjectSchema; + ): ObjectSchema; required( msg?: MixedLocale['required'], - ): NullableOptionalObjectSchema; - notRequired(): NullableOptionalObjectSchema; - - nullable(isNullable?: true): NullableOptionalObjectSchema; - nullable(isNullable: false): OptionalObjectSchema; -} - -export default interface ObjectSchema< - TShape extends ObjectShape, - TPresence extends Presence -> extends BaseObjectSchema< - TypeFromShape, - AssertsShape, + ): ObjectSchema; + notRequired(): ObjectSchema; + + nullable( + isNullable?: true, + ): ObjectSchema; + nullable( + isNullable: false, + ): ObjectSchema< + TShape, + StrictNonNullable, + StrictNonNullable, TPresence - > { - // default>>( - // def: TNextDefault | (() => TNextDefault), - // ): TNextDefault extends undefined - // ? OptionalObjectSchema - // : ObjectSchema; - - defined(msg?: MixedLocale['defined']): ObjectSchema; - required(msg?: MixedLocale['required']): ObjectSchema; - notRequired(): ObjectSchema; - - nullable(isNullable?: true): NullableObjectSchema; - nullable(isNullable: false): this; + >; } diff --git a/src/string.ts b/src/string.ts index c9d57c674..14a036c59 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,15 +1,9 @@ -import MixedSchema from './mixed'; import { MixedLocale, string as locale } from './locale'; import isAbsent from './util/isAbsent'; import type Reference from './Reference'; import type { Message, Maybe } from './types'; -import type { - Default, - Defined, - Presence, - StrictNonNullable, - Unset, -} from './util/types'; +import type { Defined, Presence, StrictNonNullable, Unset } from './util/types'; +import BaseSchema from './Base'; // eslint-disable-next-line let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; @@ -36,10 +30,7 @@ export function create() { export default class StringSchema< TType extends Maybe = string | undefined, TPresence extends Presence = Unset -> extends MixedSchema { - _tsType!: string | undefined; - _tsValidate!: string | undefined; - +> extends BaseSchema { constructor() { super({ type: 'string' }); @@ -202,7 +193,7 @@ export default class StringSchema< export default interface StringSchema< TType extends Maybe, TPresence extends Presence -> extends MixedSchema { +> extends BaseSchema { default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined diff --git a/src/types.ts b/src/types.ts index 0cea84140..4d36447a8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,9 +1,9 @@ -import type { AnyMixed } from './mixed'; +import type { AnyBase } from './Base'; import type Schema from './Schema'; export type Callback = (err: Error | null, value?: T) => void; -export type TransformFunction = ( +export type TransformFunction = ( this: T, value: any, originalValue: any, @@ -59,3 +59,7 @@ export type ExtraParams = Record; export type AnyMessageParams = MessageParams & ExtraParams; export type Maybe = T | null | undefined; + +type Preserve = T extends U ? U : never; + +export type PreserveOptionals = Preserve | Preserve; diff --git a/test/types.ts b/test/types.ts index d02ba57a1..b3204f853 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,6 +1,7 @@ /* eslint-disable no-unused-expressions */ // import { Asserts } from '../src/mixed'; import { array, string, object, mixed, number, ref, lazy } from '../src'; +import { Type } from '../src/array'; import { AssertsShape, DefaultFromShape, TypeOfShape } from '../src/object'; import { ResolveInput, ResolveOutput, Unset } from '../src/util/types'; @@ -209,6 +210,9 @@ string().required().nullable(); } { + // const str = string(); + // type f = Type; + // $ExpectType (string | undefined)[] | undefined array(string()).cast(null); @@ -224,7 +228,7 @@ string().required().nullable(); // $ExpectType (string | null)[] | undefined array(string().nullable().default('')).validateSync(null); - // $ExpectType number[] + // $ExpectType any[] array() .default([] as number[]) .getDefault(); @@ -235,10 +239,10 @@ string().required().nullable(); .default(() => [] as string[]) .validateSync(null); + const numList = [1, 2]; + // $ExpectType (number | undefined)[] - array(number()) - .default(() => []) - .getDefault(); + array(number()).default(numList).getDefault(); const a1 = object({ list: array(number().required()).required(), From dac734bc3ad67855346e17febd08911895fb6fa9 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 24 Nov 2020 13:22:12 -0500 Subject: [PATCH 18/31] WIP --- src/Base.ts | 156 ++++++++++++------------------ src/Condition.ts | 10 +- src/Lazy.ts | 18 ++-- src/Reference.ts | 12 +-- src/Schema.ts | 73 -------------- src/array.ts | 137 +++++---------------------- src/boolean.ts | 45 ++++----- src/date.ts | 82 ++++++++-------- src/index.ts | 5 +- src/mixed.ts | 120 +---------------------- src/number.ts | 10 +- src/object.ts | 178 +++++++++++------------------------ src/string.ts | 20 ++-- src/types.ts | 6 +- src/util/ReferenceSet.ts | 65 +++++++++++++ src/util/createValidation.ts | 7 +- src/util/isSchema.ts | 4 +- src/util/types.ts | 6 ++ test/types.ts | 103 ++++++++++---------- 19 files changed, 374 insertions(+), 683 deletions(-) delete mode 100644 src/Schema.ts create mode 100644 src/util/ReferenceSet.ts diff --git a/src/Base.ts b/src/Base.ts index a91b4f300..0a1f29cd5 100644 --- a/src/Base.ts +++ b/src/Base.ts @@ -12,7 +12,6 @@ import createValidation, { import printValue from './util/printValue'; import Ref from './Reference'; import { getIn } from './util/reach'; -import Reference from './Reference'; import toArray from './util/toArray'; import { ValidateOptions, @@ -21,87 +20,19 @@ import { Callback, InternalOptions, Maybe, + ExtraParams, } from './types'; -import Schema, { - CastOptions, - SchemaRefDescription, - SchemaDescription, -} from './Schema'; + import { ValidationError } from '.'; -import type { - Default, - Defined, - Nullability, - Presence, - ResolveInput, - ResolveOutput, - StrictNonNullable, - Unset, -} from './util/types'; +import type { Asserts, Presence, ResolveOutput, Unset } from './util/types'; +import ReferenceSet from './util/ReferenceSet'; const UNSET = 'unset' as const; -class RefSet { - list: Set; - refs: Map; - - constructor() { - this.list = new Set(); - this.refs = new Map(); - } - get size() { - return this.list.size + this.refs.size; - } - describe() { - const description = [] as Array; - - for (const item of this.list) description.push(item); - for (const [, ref] of this.refs) description.push(ref.describe()); - - return description; - } - toArray() { - return Array.from(this.list).concat(Array.from(this.refs.values())); - } - add(value: unknown) { - Ref.isRef(value) ? this.refs.set(value.key, value) : this.list.add(value); - } - delete(value: unknown) { - Ref.isRef(value) ? this.refs.delete(value.key) : this.list.delete(value); - } - has(value: unknown, resolve: (v: unknown) => unknown) { - if (this.list.has(value)) return true; - - let item, - values = this.refs.values(); - while (((item = values.next()), !item.done)) - if (resolve(item.value) === value) return true; - - return false; - } - - clone() { - const next = new RefSet(); - next.list = new Set(this.list); - next.refs = new Map(this.refs); - return next; - } - - merge(newItems: RefSet, removeItems: RefSet) { - const next = this.clone(); - newItems.list.forEach((value) => next.add(value)); - newItems.refs.forEach((value) => next.add(value)); - removeItems.list.forEach((value) => next.delete(value)); - removeItems.refs.forEach((value) => next.delete(value)); - return next; - } -} - export type SchemaSpec = { - nullability: Nullability; + nullable: boolean; presence: Presence; default?: TDefault | (() => TDefault); - hasDefault?: boolean; abortEarly?: boolean; strip?: boolean; strict?: boolean; @@ -117,11 +48,47 @@ export type SchemaOptions = { export type AnyBase = BaseSchema; +export interface CastOptions { + parent?: any; + context?: {}; + assert?: boolean; + // XXX: should be private? + path?: string; +} + +export interface SchemaRefDescription { + type: 'ref'; + key: string; +} + +export interface SchemaInnerTypeDescription extends SchemaDescription { + innerType?: SchemaFieldDescription; +} + +export interface SchemaObjectDescription extends SchemaDescription { + fields: Record; +} + +export type SchemaFieldDescription = + | SchemaDescription + | SchemaRefDescription + | SchemaObjectDescription + | SchemaInnerTypeDescription; + +export interface SchemaDescription { + type: string; + label?: string; + meta: object; + oneOf: unknown[]; + notOneOf: unknown[]; + tests: Array<{ name?: string; params: ExtraParams | undefined }>; +} + export default abstract class BaseSchema< TCast = any, TOutput = any, TPresence extends Presence = Unset -> implements Schema { +> { readonly type: string; readonly __inputType!: TCast; @@ -141,13 +108,11 @@ export default abstract class BaseSchema< private _whitelistError?: Test; private _blacklistError?: Test; - protected _whitelist: RefSet = new RefSet(); - protected _blacklist: RefSet = new RefSet(); + protected _whitelist = new ReferenceSet(); + protected _blacklist = new ReferenceSet(); protected exclusiveTests: Record = Object.create(null); - // optional!: () => MixedSchema; - spec: SchemaSpec; constructor(options?: SchemaOptions) { @@ -167,7 +132,7 @@ export default abstract class BaseSchema< recursive: true, label: undefined, meta: undefined, - nullability: UNSET, + nullable: false, presence: UNSET, ...options?.spec, }; @@ -238,8 +203,8 @@ export default abstract class BaseSchema< concat( schema: TOther, - ): TOther extends BaseSchema - ? BaseSchema + ): TOther extends BaseSchema + ? BaseSchema : never; concat(schema: AnyBase): AnyBase; concat(schema: AnyBase): AnyBase { @@ -255,8 +220,8 @@ export default abstract class BaseSchema< const mergedSpec = { ...base.spec, ...combined.spec }; - if (combined.spec.nullability === UNSET) - mergedSpec.nullability = base.spec.nullability; + // if (combined.spec.nullable === UNSET) + // mergedSpec.nullable = base.spec.nullable; if (combined.spec.presence === UNSET) mergedSpec.presence = base.spec.presence; @@ -294,7 +259,7 @@ export default abstract class BaseSchema< } isType(v: any) { - if (this.spec.nullability === 'nullable' && v === null) return true; + if (this.spec.nullable && v === null) return true; return this._typeCheck(v); } @@ -309,7 +274,7 @@ export default abstract class BaseSchema< schema = conditions.reduce( (schema, condition) => condition.resolve(schema, options), schema, - ); + ) as this; schema = schema.resolve(options); } @@ -470,16 +435,17 @@ export default abstract class BaseSchema< return result; } - isValid(value: any, options: ValidateOptions): Promise { - return this.validate(value, options) - .then(() => true) - .catch((err) => { - if (ValidationError.isError(err)) return false; - throw err; - }); + async isValid(value: any, options: ValidateOptions): Promise { + try { + await this.validate(value, options); + return true; + } catch (err) { + if (ValidationError.isError(err)) return false; + throw err; + } } - isValidSync(value: any, options: ValidateOptions): boolean { + isValidSync(value: any, options: ValidateOptions): value is Asserts { try { this.validateSync(value, options); return true; @@ -561,7 +527,7 @@ export default abstract class BaseSchema< nullable(isNullable: false): any; nullable(isNullable = true): any { var next = this.clone({ - nullability: isNullable ? 'nullable' : 'nonnullable', + nullable: isNullable !== false, }); return next as any; diff --git a/src/Condition.ts b/src/Condition.ts index 951bbf678..e6e05fafb 100644 --- a/src/Condition.ts +++ b/src/Condition.ts @@ -1,22 +1,22 @@ import has from 'lodash/has'; import isSchema from './util/isSchema'; import Reference from './Reference'; -import Schema from './Schema'; +import { AnySchema } from './types'; -export interface ConditionBuilder { +export interface ConditionBuilder { (this: T, value: any, schema: T): T; (v1: any, v2: any, schema: T): T; (v1: any, v2: any, v3: any, schema: T): T; (v1: any, v2: any, v3: any, v4: any, schema: T): T; } -export type ConditionConfig = { +export type ConditionConfig = { is: any | ((...values: any[]) => boolean); then?: T | ((schema: T) => T); otherwise?: T | ((schema: T) => T); }; -export type ConditionOptions = +export type ConditionOptions = | ConditionBuilder | ConditionConfig; @@ -26,7 +26,7 @@ export type ResolveOptions = { context?: any; }; -class Condition { +class Condition { fn: ConditionBuilder; constructor(public refs: Reference[], options: ConditionOptions) { diff --git a/src/Lazy.ts b/src/Lazy.ts index 57a7439aa..b83a76cad 100644 --- a/src/Lazy.ts +++ b/src/Lazy.ts @@ -2,14 +2,16 @@ import isSchema from './util/isSchema'; import Schema, { CastOptions } from './Schema'; import type { Callback, ValidateOptions } from './types'; import type { ResolveOptions } from './Condition'; -import MixedSchema, { AnyMixed } from './mixed'; -export type LazyBuilder = ( +import { AnyBase } from './Base'; +import { TypedSchema, TypeOf } from './util/types'; + +export type LazyBuilder = ( value: any, options: ResolveOptions, ) => T; -export function create(builder: LazyBuilder) { +export function create(builder: LazyBuilder) { return new Lazy(builder); } @@ -17,11 +19,11 @@ export type LazyReturnValue = T extends Lazy ? TSchema : never; -export type LazyType = LazyReturnValue extends MixedSchema - ? TType +export type LazyType = LazyReturnValue extends TypedSchema + ? TypeOf> : never; -class Lazy implements Schema { +class Lazy implements Schema { type = 'lazy' as const; __isYupSchema__ = true; @@ -31,7 +33,7 @@ class Lazy implements Schema { constructor(private builder: LazyBuilder) {} - private _resolve = (value: any, options: ResolveOptions = {}) => { + private _resolve = (value: any, options: ResolveOptions = {}): T => { let schema = this.builder(value, options); if (!isSchema(schema)) @@ -52,7 +54,7 @@ class Lazy implements Schema { options?: ValidateOptions, maybeCb?: Callback, ): T['__outputType'] { - // @ts-expect-error + // @ts-expect-error missing public callback on type return this._resolve(value, options).validate(value, options, maybeCb); } diff --git a/src/Reference.ts b/src/Reference.ts index 3c46e5c9a..f4b4caac2 100644 --- a/src/Reference.ts +++ b/src/Reference.ts @@ -6,15 +6,15 @@ const prefixes = { value: '.', }; -export type ReferenceOptions = { - map?: (value: unknown) => unknown; +export type ReferenceOptions = { + map?: (value: unknown) => TValue; }; export function create(key: string, options?: ReferenceOptions) { return new Reference(key, options); } -export default class Reference { +export default class Reference { readonly key: string; readonly isContext: boolean; readonly isValue: boolean; @@ -22,11 +22,11 @@ export default class Reference { readonly path: any; readonly getter: (data: unknown) => unknown; - readonly map?: (value: unknown) => unknown; + readonly map?: (value: unknown) => TValue; readonly __isYupRef!: boolean; - constructor(key: string, options: ReferenceOptions = {}) { + constructor(key: string, options: ReferenceOptions = {}) { if (typeof key !== 'string') throw new TypeError('ref must be a string, got: ' + key); @@ -49,7 +49,7 @@ export default class Reference { this.map = options.map; } - getValue(value: any, parent?: {}, context?: {}) { + getValue(value: any, parent?: {}, context?: {}): TValue { let result = this.isContext ? context : this.isValue ? value : parent; if (this.getter) result = this.getter(result || {}); diff --git a/src/Schema.ts b/src/Schema.ts deleted file mode 100644 index 3bb30a36a..000000000 --- a/src/Schema.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ResolveOptions } from './Condition'; -import { - ValidateOptions, - Callback, - ExtraParams -} from './types'; - -export interface CastOptions { - parent?: any; - context?: {}; - assert?: boolean; - // XXX: should be private? - path?: string; -} - -export type SchemaSpec = { - nullable?: boolean; - default: TDefault | (() => TDefault); - hasDefault?: boolean; - abortEarly?: boolean; - strip?: boolean; - strict?: boolean; - recursive?: boolean; - label?: string | undefined; - meta?: any; -}; - -export interface SchemaRefDescription { - type: 'ref'; - key: string; -} - -export interface SchemaInnerTypeDescription extends SchemaDescription { - innerType?: SchemaFieldDescription; -} - -export interface SchemaObjectDescription extends SchemaDescription { - fields: Record; -} - -export type SchemaFieldDescription = - | SchemaDescription - | SchemaRefDescription - | SchemaObjectDescription - | SchemaInnerTypeDescription; - -export interface SchemaDescription { - type: string; - label?: string; - meta: object; - oneOf: unknown[]; - notOneOf: unknown[]; - tests: Array<{ name?: string; params: ExtraParams | undefined }>; -} - -export default interface Schema { - __isYupSchema__: boolean; - type: string; - // cast(value: any): any; - // validate(value: any): any; - validate(value: any, options: ValidateOptions): Promise; - describe(): any; - validate( - value: any, - options: ValidateOptions | undefined, - callback: Callback, - ): void; - - resolve(options: ResolveOptions): any; - cast(value: any, options?: CastOptions): any; - - describe(): SchemaDescription; -} diff --git a/src/array.ts b/src/array.ts index b59985046..22b38be7b 100644 --- a/src/array.ts +++ b/src/array.ts @@ -3,7 +3,6 @@ import isSchema from './util/isSchema'; import printValue from './util/printValue'; import { array as locale, MixedLocale } from './locale'; import runTests, { RunTest } from './util/runTests'; -import type { SchemaInnerTypeDescription, SchemaSpec } from './Schema'; import type { InternalOptions, Callback, @@ -21,29 +20,18 @@ import { TypeOf, Unset, } from './util/types'; -import BaseSchema, { AnyBase } from './Base'; +import BaseSchema, { + AnyBase, + SchemaInnerTypeDescription, + SchemaSpec, +} from './Base'; -type RefectorFn = (value: any, index: number, array: any[]) => boolean; - -type MaybeArray = Maybe[]>; +type RejectorFn = (value: any, index: number, array: any[]) => boolean; export function create(type?: TInner) { return new ArraySchema(type); } -// type MaintainNull = T extends null ? U | null : U; -// type MaintainUndefined = T extends undefined ? U | null : U; -// type WithOptionality = MaintainUndefined>; - -// export type TypeOfElement = T['__inputType']; - -abstract class BasicArraySchema< - TElement, - TType extends Maybe, - TOut extends Maybe, - TPresence extends Presence -> extends BaseSchema {} - export default class ArraySchema< T extends AnyBase = AnyBase, TType extends Maybe[]> = TypeOf[] | undefined, @@ -180,11 +168,16 @@ export default class ArraySchema< concat>( schema: TOther, - ): TOther extends ArraySchema - ? ArraySchema< + ): TOther extends ArraySchema + ? // hoooo boy + ArraySchema< T, - C | PreserveOptionals, - O | PreserveOptionals, + | (TypeOf & TypeOf)[] + | PreserveOptionals + | PreserveOptionals, + | (Asserts & Asserts)[] + | PreserveOptionals + | PreserveOptionals, P extends Unset ? TPresence : P > : never; @@ -226,7 +219,7 @@ export default class ArraySchema< } length( - length: number | Reference, + length: number | Reference, message: Message<{ length: number }> = locale.length, ) { return this.test({ @@ -240,7 +233,7 @@ export default class ArraySchema< }); } - min(min: number | Reference, message?: Message<{ min: number }>) { + min(min: number | Reference, message?: Message<{ min: number }>) { message = message || locale.min; return this.test({ @@ -255,30 +248,29 @@ export default class ArraySchema< }); } - max(max: number | Reference, message?: Message<{ max: number }>) { + max(max: number | Reference, message?: Message<{ max: number }>) { message = message || locale.max; return this.test({ message, name: 'max', exclusive: true, params: { max }, - // FIXME(ts): Array test(value) { return isAbsent(value) || value.length <= this.resolve(max); }, }); } - ensure() { - return this.default(() => [] as TypeOf[]).transform((val, original) => { + ensure(): ArraySchema, NonNullable, TPresence> { + return this.default[]>(() => []).transform((val, original) => { // We don't want to return `null` for nullable schema if (this._typeCheck(val)) return val; return original == null ? [] : [].concat(original); - }); + }) as any; } - compact(rejector?: RefectorFn) { - let reject: RefectorFn = !rejector + compact(rejector?: RejectorFn) { + let reject: RejectorFn = !rejector ? (v) => !!v : (v, i, a) => !rejector(v, i, a); @@ -294,89 +286,6 @@ export default class ArraySchema< } } -// interface NullableArraySchema< -// T extends AnyBase = AnyBase, -// TPresence extends Presence = Unset -// > extends BasicArraySchema< -// Type, -// Type[] | null, -// Asserts[] | null, -// TPresence -// > { -// of( -// schema: TInner, -// ): NullableArraySchema; -// nullable(isNullable?: true): NullableArraySchema; -// nullable(isNullable: false): ArraySchema; -// nullable(isNullable?: boolean): ArraySchema; - -// defined(msg?: MixedLocale['defined']): NullableArraySchema; -// required(msg?: MixedLocale['required']): NullableArraySchema; -// notRequired(): ArraySchema; - -// default[] | null | undefined>( -// def: TNextDefault | (() => TNextDefault), -// ): TNextDefault extends undefined -// ? NullableOptionalArraySchema -// : this; -// } - -// interface OptionalArraySchema< -// T extends AnyBase = AnyBase, -// TPresence extends Presence = Unset -// > extends BasicArraySchema< -// Type, -// Type[] | undefined, -// Asserts[] | undefined, -// TPresence -// > { -// of( -// schema: TInner, -// ): NullableArraySchema; -// nullable(isNullable?: true): NullableArraySchema; -// nullable(isNullable: false): ArraySchema; -// nullable(isNullable?: boolean): ArraySchema; - -// defined(msg?: MixedLocale['defined']): ArraySchema; -// required(msg?: MixedLocale['required']): ArraySchema; -// notRequired(): ArraySchema; - -// default[] | null | undefined>( -// def: TNextDefault | (() => TNextDefault), -// ): TNextDefault extends undefined -// ? OptionalArraySchema -// : ArraySchema; -// } - -// interface NullableOptionalArraySchema< -// T extends AnyBase = AnyBase, -// TPresence extends Presence = Unset -// > extends BasicArraySchema< -// Type, -// Type[] | null | undefined, -// Asserts[] | null | undefined, -// TPresence -// > { -// of( -// schema: TInner, -// ): NullableArraySchema; -// nullable(isNullable?: true): NullableOptionalArraySchema; -// nullable(isNullable: false): OptionalArraySchema; -// nullable(isNullable?: boolean): OptionalArraySchema; - -// defined( -// msg?: MixedLocale['defined'], -// ): NullableOptionalArraySchema; -// required( -// msg?: MixedLocale['required'], -// ): NullableOptionalArraySchema; -// notRequired(): NullableOptionalArraySchema; - -// default[] | null | undefined>( -// def: TNextDefault | (() => TNextDefault), -// ): TNextDefault extends undefined ? this : NullableArraySchema; -// } - export default interface ArraySchema< T extends AnyBase, TType extends Maybe[]>, diff --git a/src/boolean.ts b/src/boolean.ts index 6a1c6c91a..2e79558d1 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -1,18 +1,17 @@ +import BaseSchema from './Base'; import type { MixedLocale } from './locale'; import MixedSchema from './mixed'; import type { Maybe } from './types'; -import type { Nullability, Presence, Unset } from './util/types'; +import type { Defined, Nullability, Presence, Unset } from './util/types'; export function create() { return new BooleanSchema(); } export default class BooleanSchema< - TType extends boolean, - TDefault extends Maybe = undefined, - TNullablity extends Nullability = Unset, + TType extends Maybe = boolean | undefined, TPresence extends Presence = Unset -> extends MixedSchema { +> extends BaseSchema { constructor() { super({ type: 'boolean' }); @@ -27,7 +26,7 @@ export default class BooleanSchema< }); } - protected _typeCheck(v: any): v is TType { + protected _typeCheck(v: any): v is NonNullable { if (v instanceof Boolean) v = v.valueOf(); return typeof v === 'boolean'; @@ -35,28 +34,20 @@ export default class BooleanSchema< } export default interface BooleanSchema< - TType extends boolean, - TDefault extends Maybe, - TNullablity extends Nullability, + TType extends Maybe, TPresence extends Presence -> extends MixedSchema { +> extends BaseSchema { default>( def: TNextDefault | (() => TNextDefault), - ): BooleanSchema; - - defined( - msg?: MixedLocale['defined'], - ): BooleanSchema; - - required( - msg?: MixedLocale['required'], - ): BooleanSchema; - notRequired(): BooleanSchema; - - nullable( - isNullable?: true, - ): BooleanSchema; - nullable( - isNullable: false, - ): BooleanSchema; + ): TNextDefault extends undefined + ? BooleanSchema + : BooleanSchema, TPresence>; + + defined(msg?: MixedLocale['defined']): BooleanSchema; + required(msg?: MixedLocale['required']): BooleanSchema; + notRequired(): BooleanSchema; + // optional(): BooleanSchema; + + nullable(isNullable?: true): BooleanSchema; + nullable(isNullable: false): BooleanSchema; } diff --git a/src/date.ts b/src/date.ts index 2333f22b7..7429420ac 100644 --- a/src/date.ts +++ b/src/date.ts @@ -5,7 +5,8 @@ import { date as locale, MixedLocale } from './locale'; import isAbsent from './util/isAbsent'; import Ref from './Reference'; import type { Maybe } from './types'; -import type { Nullability, Presence, Unset } from './util/types'; +import type { Defined, Nullability, Presence, Unset } from './util/types'; +import BaseSchema from './Base'; let invalidDate = new Date(''); @@ -17,11 +18,9 @@ export function create() { } export default class DateSchema< - TType extends Date, - TDefault extends Maybe = undefined, - TNullablity extends Nullability = Unset, + TType extends Maybe = Date | undefined, TPresence extends Presence = Unset -> extends MixedSchema { +> extends BaseSchema { constructor() { super({ type: 'date' }); @@ -37,20 +36,31 @@ export default class DateSchema< }); } - protected _typeCheck(v: any): v is TType { + protected _typeCheck(v: any): v is NonNullable { return isDate(v) && !isNaN(v.getTime()); } - min(min: unknown | Ref, message = locale.min) { - var limit = min; + private prepareParam( + ref: unknown | Ref, + name: string, + ): Date | Ref { + let param: Date | Ref; - if (!Ref.isRef(limit)) { - limit = this.cast(min); - if (!this._typeCheck(limit)) + if (!Ref.isRef(ref)) { + let cast = this.cast(ref); + if (!this._typeCheck(cast)) throw new TypeError( - '`min` must be a Date or a value that can be `cast()` to a Date', + `\`${name}\` must be a Date or a value that can be \`cast()\` to a Date`, ); + param = cast; + } else { + param = ref as Ref; } + return param; + } + + min(min: unknown | Ref, message = locale.min) { + let limit = this.prepareParam(min, 'min'); return this.test({ message, @@ -58,21 +68,13 @@ export default class DateSchema< exclusive: true, params: { min }, test(value) { - return isAbsent(value) || value >= this.resolve(limit); + return isAbsent(value) || value >= this.resolve(limit); }, }); } max(max: unknown | Ref, message = locale.max) { - var limit = max; - - if (!Ref.isRef(limit)) { - limit = this.cast(max); - if (!this._typeCheck(limit)) - throw new TypeError( - '`max` must be a Date or a value that can be `cast()` to a Date', - ); - } + var limit = this.prepareParam(max, 'max'); return this.test({ message, @@ -80,35 +82,27 @@ export default class DateSchema< exclusive: true, params: { max }, test(value) { - return isAbsent(value) || value <= this.resolve(limit); + return isAbsent(value) || value <= this.resolve(limit); }, }); } } export default interface DateSchema< - TType extends Date, - TDefault extends Maybe, - TNullablity extends Nullability, + TType extends Maybe, TPresence extends Presence -> extends MixedSchema { +> extends BaseSchema { default>( def: TNextDefault | (() => TNextDefault), - ): DateSchema; - - defined( - msg?: MixedLocale['defined'], - ): DateSchema; - - required( - msg?: MixedLocale['required'], - ): DateSchema; - notRequired(): DateSchema; - - nullable( - isNullable?: true, - ): DateSchema; - nullable( - isNullable: false, - ): DateSchema; + ): TNextDefault extends undefined + ? DateSchema + : DateSchema, TPresence>; + + defined(msg?: MixedLocale['defined']): DateSchema; + + required(msg?: MixedLocale['required']): DateSchema; + notRequired(): DateSchema; + + nullable(isNullable?: true): DateSchema; + nullable(isNullable: false): DateSchema; } diff --git a/src/index.ts b/src/index.ts index 291c69e8f..ca9153645 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ -import MixedSchema, { create as mixedCreate, SchemaSpec } from './mixed'; +import MixedSchema, { create as mixedCreate } from './mixed'; import BoolSchema, { create as boolCreate } from './boolean'; import StringSchema, { create as stringCreate } from './string'; import NumberSchema, { create as numberCreate } from './number'; import DateSchema, { create as dateCreate } from './date'; -import ObjectSchema, { create as objectCreate } from './object'; +import ObjectSchema, { create as objectCreate, ObjectSchemaOf } from './object'; import ArraySchema, { create as arrayCreate } from './array'; import { create as refCreate } from './Reference'; import { create as lazyCreate } from './Lazy'; @@ -38,6 +38,7 @@ export type { Presence, Unset, Asserts as InferType, + ObjectSchemaOf, }; export { diff --git a/src/mixed.ts b/src/mixed.ts index a08da68da..694fdb406 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -1,123 +1,9 @@ -// @ts-ignore -import cloneDeep from 'nanoclone'; +import type { MixedLocale } from './locale'; -import { mixed as locale, MixedLocale } from './locale'; -import Condition, { ConditionOptions, ResolveOptions } from './Condition'; -import runTests from './util/runTests'; -import createValidation, { - TestFunction, - Test, - TestConfig, -} from './util/createValidation'; -import printValue from './util/printValue'; -import Ref from './Reference'; -import { getIn } from './util/reach'; -import Reference from './Reference'; -import toArray from './util/toArray'; -import { - ValidateOptions, - TransformFunction, - Message, - Callback, - InternalOptions, - Maybe, -} from './types'; -import Schema, { - CastOptions, - SchemaRefDescription, - SchemaDescription, -} from './Schema'; -import { ValidationError } from '.'; -import type { - Default, - Defined, - Nullability, - Presence, - ResolveInput, - ResolveOutput, - StrictNonNullable, - Unset, -} from './util/types'; +import { Maybe } from './types'; +import type { Defined, Presence, StrictNonNullable, Unset } from './util/types'; import BaseSchema from './Base'; -const UNSET = 'unset' as const; - -class RefSet { - list: Set; - refs: Map; - - constructor() { - this.list = new Set(); - this.refs = new Map(); - } - get size() { - return this.list.size + this.refs.size; - } - describe() { - const description = [] as Array; - - for (const item of this.list) description.push(item); - for (const [, ref] of this.refs) description.push(ref.describe()); - - return description; - } - toArray() { - return Array.from(this.list).concat(Array.from(this.refs.values())); - } - add(value: unknown) { - Ref.isRef(value) ? this.refs.set(value.key, value) : this.list.add(value); - } - delete(value: unknown) { - Ref.isRef(value) ? this.refs.delete(value.key) : this.list.delete(value); - } - has(value: unknown, resolve: (v: unknown) => unknown) { - if (this.list.has(value)) return true; - - let item, - values = this.refs.values(); - while (((item = values.next()), !item.done)) - if (resolve(item.value) === value) return true; - - return false; - } - - clone() { - const next = new RefSet(); - next.list = new Set(this.list); - next.refs = new Map(this.refs); - return next; - } - - merge(newItems: RefSet, removeItems: RefSet) { - const next = this.clone(); - newItems.list.forEach((value) => next.add(value)); - newItems.refs.forEach((value) => next.add(value)); - removeItems.list.forEach((value) => next.delete(value)); - removeItems.refs.forEach((value) => next.delete(value)); - return next; - } -} - -export type SchemaSpec = { - nullability: Nullability; - presence: Presence; - default?: TDefault | (() => TDefault); - hasDefault?: boolean; - abortEarly?: boolean; - strip?: boolean; - strict?: boolean; - recursive?: boolean; - label?: string | undefined; - meta?: any; -}; - -export type SchemaOptions = { - type?: string; - spec?: SchemaSpec; -}; - -export type AnyMixed = MixedSchema; - export function create() { return new MixedSchema(); } diff --git a/src/number.ts b/src/number.ts index 4680c3ac9..3cbd92029 100644 --- a/src/number.ts +++ b/src/number.ts @@ -43,13 +43,13 @@ export default class NumberSchema< }); } - protected _typeCheck(value: any): value is TType { + protected _typeCheck(value: any): value is NonNullable { if (value instanceof Number) value = value.valueOf(); return typeof value === 'number' && !isNaN(value); } - min(min: number | Reference, message = locale.min) { + min(min: number | Reference, message = locale.min) { return this.test({ message, name: 'min', @@ -61,7 +61,7 @@ export default class NumberSchema< }); } - max(max: number | Reference, message = locale.max) { + max(max: number | Reference, message = locale.max) { return this.test({ message, name: 'max', @@ -73,7 +73,7 @@ export default class NumberSchema< }); } - lessThan(less: number | Reference, message = locale.lessThan) { + lessThan(less: number | Reference, message = locale.lessThan) { return this.test({ message, name: 'max', @@ -85,7 +85,7 @@ export default class NumberSchema< }); } - moreThan(more: number | Reference, message = locale.moreThan) { + moreThan(more: number | Reference, message = locale.moreThan) { return this.test({ message, name: 'min', diff --git a/src/object.ts b/src/object.ts index 9ea2bd9c8..7629a9a70 100644 --- a/src/object.ts +++ b/src/object.ts @@ -5,13 +5,17 @@ import mapKeys from 'lodash/mapKeys'; import mapValues from 'lodash/mapValues'; import { getter } from 'property-expr'; -import MixedSchema, { AnyMixed, SchemaSpec } from './mixed'; import { MixedLocale, object as locale } from './locale'; import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; -import { SchemaObjectDescription } from './Schema'; -import { InternalOptions, Callback, Maybe, PreserveOptionals } from './types'; +import { + InternalOptions, + Callback, + Maybe, + PreserveOptionals, + Preserve, +} from './types'; import ValidationError from './ValidationError'; import type { Presence, @@ -19,50 +23,47 @@ import type { TypedSchema, Defined, StrictNonNullable, + InferPresence, } from './util/types'; import type Reference from './Reference'; import Lazy, { LazyType } from './Lazy'; -import BaseSchema from './Base'; +import BaseSchema, { + AnyBase, + SchemaObjectDescription, + SchemaSpec, +} from './Base'; -let isObject = (obj: any): obj is Record => - Object.prototype.toString.call(obj) === '[object Object]'; - -function unknown(ctx: ObjectSchema, value: any) { - let known = Object.keys(ctx.fields); - return Object.keys(value).filter((key) => known.indexOf(key) === -1); -} +export type AnyObject = Record; -type AnyObject = Record; -type ObjectShape = Record>; +export type ObjectSchemaOf> = ObjectSchema< + { + [k in keyof T]: BaseSchema, Maybe, InferPresence>; + }, + Partial, + Partial +>; -type AssignShape = { - [P in keyof T]: P extends keyof U ? U[P] : T[P]; -} & - U; +export type ObjectShape = Record>; export function create(spec?: TShape) { return new ObjectSchema(spec); } -export type TypeFromShape = { - [K in keyof Shape]: Shape[K] extends Reference - ? unknown - : Shape[K] extends MixedSchema - ? TType - : // not sure why this is necessary - Shape[K] extends ObjectSchema - ? TypeFromShape - : LazyType; -}; - export type DefaultFromShape = { - [K in keyof Shape]: Shape[K] extends MixedSchema - ? TDefault - : Shape[K] extends Reference - ? undefined - : never; + [K in keyof Shape]: Shape[K] extends ObjectSchema + ? DefaultFromShape + : Shape[K] extends { getDefault: () => infer D } + ? Preserve extends never + ? Defined + : Preserve + : undefined; }; +type AssignShape = { + [P in keyof T]: P extends keyof U ? U[P] : T[P]; +} & + U; + export type TypeOfShape = { [K in keyof Shape]: Shape[K] extends TypedSchema ? Shape[K]['__inputType'] @@ -79,10 +80,18 @@ export type AssertsShape = { : never; }; -type ObjectSchemaSpec = SchemaSpec & { +export type ObjectSchemaSpec = SchemaSpec & { noUnknown?: boolean; }; +let isObject = (obj: any): obj is Record => + Object.prototype.toString.call(obj) === '[object Object]'; + +function unknown(ctx: ObjectSchema, value: any) { + let known = Object.keys(ctx.fields); + return Object.keys(value).filter((key) => known.indexOf(key) === -1); +} + const defaultSort = sortByKeyOrder([]); export default class ObjectSchema< @@ -105,8 +114,6 @@ export default class ObjectSchema< }); this.withMutation(() => { - // this.spec.default = () => {}; - this.transform(function coerce(value) { if (typeof value === 'string') { try { @@ -302,7 +309,7 @@ export default class ObjectSchema< P extends Unset ? TPresence : P > : never; - concat(schema: AnyMixed): AnyMixed; + concat(schema: AnyBase): AnyBase; concat(schema: any): any { let next = super.concat(schema) as any; @@ -312,8 +319,8 @@ export default class ObjectSchema< if (target === undefined) { nextFields[field] = schemaOrRef; } else if ( - target instanceof MixedSchema && - schemaOrRef instanceof MixedSchema + target instanceof BaseSchema && + schemaOrRef instanceof BaseSchema ) { nextFields[field] = schemaOrRef.concat(target); } @@ -322,6 +329,15 @@ export default class ObjectSchema< return next.withMutation((next: any) => next.shape(nextFields)); } + getDefaultFromShape(): DefaultFromShape { + let dft = {} as Record; + this._nodes.forEach((key) => { + const field = this.fields[key]; + dft[key] = 'default' in field ? field.getDefault() : undefined; + }); + return dft as any; + } + protected _getDefault() { if ('default' in this.spec) { return super._getDefault(); @@ -331,13 +347,7 @@ export default class ObjectSchema< if (!this._nodes.length) { return undefined; } - - let dft = {} as Record; - this._nodes.forEach((key) => { - const field = this.fields[key]; - dft[key] = 'default' in field ? field.getDefault() : undefined; - }); - return dft as any; + return this.getDefaultFromShape(); } shape( @@ -460,82 +470,6 @@ export default class ObjectSchema< } } -// interface NullableObjectSchema< -// TShape extends ObjectShape, -// TPresence extends Presence -// > extends BaseObjectSchema< -// TypeFromShape | null, -// AssertsShape | null, -// TPresence -// > { -// // default>>( -// // def: TNextDefault | (() => TNextDefault), -// // ): TNextDefault extends undefined -// // ? NullableOptionalObjectSchema -// // : this; - -// defined( -// msg?: MixedLocale['defined'], -// ): NullableObjectSchema; -// required( -// msg?: MixedLocale['required'], -// ): NullableObjectSchema; -// notRequired(): NullableObjectSchema; - -// nullable(isNullable?: true): NullableObjectSchema; -// nullable(isNullable: false): ObjectSchema; -// } - -// interface OptionalObjectSchema< -// TShape extends ObjectShape, -// TPresence extends Presence -// > extends BaseObjectSchema< -// TypeFromShape | undefined, -// AssertsShape | undefined, -// TPresence -// > { -// default>>( -// def: TNextDefault | (() => TNextDefault), -// ): TNextDefault extends undefined ? this : ObjectSchema; - -// defined( -// msg?: MixedLocale['defined'], -// ): OptionalObjectSchema; -// required( -// msg?: MixedLocale['required'], -// ): OptionalObjectSchema; -// notRequired(): OptionalObjectSchema; - -// nullable(isNullable?: true): NullableOptionalObjectSchema; -// nullable(isNullable: false): OptionalObjectSchema; -// } - -// interface NullableOptionalObjectSchema< -// TShape extends ObjectShape, -// TPresence extends Presence -// > extends BaseObjectSchema< -// TypeFromShape | null | undefined, -// AssertsShape | null | undefined, -// TPresence -// > { -// default>>( -// def: TNextDefault | (() => TNextDefault), -// ): TNextDefault extends undefined -// ? this -// : NullableObjectSchema; - -// defined( -// msg?: MixedLocale['defined'], -// ): NullableOptionalObjectSchema; -// required( -// msg?: MixedLocale['required'], -// ): NullableOptionalObjectSchema; -// notRequired(): NullableOptionalObjectSchema; - -// nullable(isNullable?: true): NullableOptionalObjectSchema; -// nullable(isNullable: false): OptionalObjectSchema; -// } - export default interface ObjectSchema< TShape extends ObjectShape, TType extends Maybe, diff --git a/src/string.ts b/src/string.ts index 14a036c59..215ccb1c1 100644 --- a/src/string.ts +++ b/src/string.ts @@ -49,7 +49,7 @@ export default class StringSchema< }); } - protected _typeCheck(value: any): value is TType { + protected _typeCheck(value: any): value is NonNullable { if (value instanceof String) value = value.valueOf(); return typeof value === 'string'; @@ -60,7 +60,7 @@ export default class StringSchema< } length( - length: number | Reference, + length: number | Reference, message: Message<{ length: number }> = locale.length, ) { return this.test({ @@ -74,7 +74,10 @@ export default class StringSchema< }); } - min(min: number | Reference, message: Message<{ min: number }> = locale.min) { + min( + min: number | Reference, + message: Message<{ min: number }> = locale.min, + ) { return this.test({ message, name: 'min', @@ -86,7 +89,10 @@ export default class StringSchema< }); } - max(max: number | Reference, message: Message<{ max: number }> = locale.max) { + max( + max: number | Reference, + message: Message<{ max: number }> = locale.max, + ) { return this.test({ name: 'max', exclusive: true, @@ -151,10 +157,10 @@ export default class StringSchema< } //-- transforms -- - ensure() { - return this.default('' as TType).transform((val) => + ensure(): StringSchema, TPresence> { + return this.default('' as Defined).transform((val) => val === null ? '' : val, - ); + ) as any; } trim(message = locale.trim) { diff --git a/src/types.ts b/src/types.ts index 4d36447a8..b849a3820 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,7 @@ import type { AnyBase } from './Base'; -import type Schema from './Schema'; +import type Lazy from './Lazy'; + +export type AnySchema = AnyBase | Lazy; export type Callback = (err: Error | null, value?: T) => void; @@ -60,6 +62,6 @@ export type AnyMessageParams = MessageParams & ExtraParams; export type Maybe = T | null | undefined; -type Preserve = T extends U ? U : never; +export type Preserve = T extends U ? U : never; export type PreserveOptionals = Preserve | Preserve; diff --git a/src/util/ReferenceSet.ts b/src/util/ReferenceSet.ts new file mode 100644 index 000000000..a18919770 --- /dev/null +++ b/src/util/ReferenceSet.ts @@ -0,0 +1,65 @@ +import type { SchemaRefDescription } from '../Base'; +import Reference from '../Reference'; + +export default class ReferenceSet { + list: Set; + refs: Map; + + constructor() { + this.list = new Set(); + this.refs = new Map(); + } + get size() { + return this.list.size + this.refs.size; + } + + describe() { + const description = [] as Array; + + for (const item of this.list) description.push(item); + for (const [, ref] of this.refs) description.push(ref.describe()); + + return description; + } + + toArray() { + return Array.from(this.list).concat(Array.from(this.refs.values())); + } + + add(value: unknown) { + Reference.isRef(value) + ? this.refs.set(value.key, value) + : this.list.add(value); + } + delete(value: unknown) { + Reference.isRef(value) + ? this.refs.delete(value.key) + : this.list.delete(value); + } + has(value: unknown, resolve: (v: unknown) => unknown) { + if (this.list.has(value)) return true; + + let item, + values = this.refs.values(); + while (((item = values.next()), !item.done)) + if (resolve(item.value) === value) return true; + + return false; + } + + clone() { + const next = new ReferenceSet(); + next.list = new Set(this.list); + next.refs = new Map(this.refs); + return next; + } + + merge(newItems: ReferenceSet, removeItems: ReferenceSet) { + const next = this.clone(); + newItems.list.forEach((value) => next.add(value)); + newItems.refs.forEach((value) => next.add(value)); + removeItems.list.forEach((value) => next.delete(value)); + removeItems.refs.forEach((value) => next.delete(value)); + return next; + } +} diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts index 19e047ee9..09c17b741 100644 --- a/src/util/createValidation.ts +++ b/src/util/createValidation.ts @@ -6,11 +6,10 @@ import { Message, InternalOptions, Callback, - MessageParams, - AnyMessageParams, ExtraParams, } from '../types'; import Schema from '../Schema'; +import Reference from '../Reference'; export type CreateErrorOptions = { path?: string; @@ -24,7 +23,7 @@ export type TestContext = { options: ValidateOptions; parent: any; schema: any; // TODO: Schema; - resolve: (value: any) => T; + resolve: (value: T | Reference) => T; createError: (params?: CreateErrorOptions) => ValidationError; }; @@ -77,7 +76,7 @@ export default function createValidation(config: { const { name, test, params, message } = config; let { parent, context } = options; - function resolve(item: any) { + function resolve(item: T | Reference) { return Ref.isRef(item) ? item.getValue(value, parent, context) : item; } diff --git a/src/util/isSchema.ts b/src/util/isSchema.ts index 9a99d1a73..908ba1e0f 100644 --- a/src/util/isSchema.ts +++ b/src/util/isSchema.ts @@ -1,3 +1,3 @@ -import Schema from '../Schema'; +import type { AnySchema } from '../types'; -export default (obj: any): obj is Schema => obj && obj.__isYupSchema__; +export default (obj: any): obj is AnySchema => obj && obj.__isYupSchema__; diff --git a/src/util/types.ts b/src/util/types.ts index d2f3d8817..b13c94f64 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -4,6 +4,12 @@ export type Unset = 'unset'; export type Presence = 'required' | 'defined' | 'optional' | Unset; export type Nullability = 'nullable' | 'nonnullable' | Unset; +export type InferPresence = T extends null + ? 'unset' + : T extends undefined + ? 'defined' + : 'required'; + export type StrictNonNullable = T extends null ? never : T; export type Defined = T extends undefined ? never : T; diff --git a/test/types.ts b/test/types.ts index b3204f853..49a006d8e 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,9 +1,23 @@ +/* eslint-disable no-unused-labels */ /* eslint-disable no-unused-expressions */ -// import { Asserts } from '../src/mixed'; -import { array, string, object, mixed, number, ref, lazy } from '../src'; -import { Type } from '../src/array'; -import { AssertsShape, DefaultFromShape, TypeOfShape } from '../src/object'; -import { ResolveInput, ResolveOutput, Unset } from '../src/util/types'; + +import { + array, + string, + object, + mixed, + number, + ref, + lazy, + StringSchema, +} from '../src'; +import { + AssertsShape, + DefaultFromShape, + ObjectSchemaOf, + TypeOfShape, +} from '../src/object'; +import { ResolveOutput, TypedSchema, Unset } from '../src/util/types'; // let schema = object({ // str: string().nullable(), @@ -19,71 +33,41 @@ string().required().nullable(); /** Type utils */ { - // $ExpectType string | undefined - type _d1 = ResolveInput; - - // $ExpectType string | null | undefined - type _d2 = ResolveInput; - - // $ExpectType string | undefined - type _d3 = ResolveInput; - - // $ExpectType string - type _d4 = ResolveOutput; - - // $ExpectType string - type _d5 = ResolveOutput; - - // $ExpectType string | null - type _i1 = ResolveInput; - - // $ExpectType string | undefined - type _i2 = ResolveInput; - // $ExpectType string - type _i3 = ResolveInput; + type _d4 = ResolveOutput; // $ExpectType string - type _i4 = ResolveInput; + type _d5 = ResolveOutput; // $ExpectType string - type _i5 = ResolveInput; - - // $ExpectType string | null | undefined - type _i6 = ResolveInput; - - // $ExpectType string - type _o1 = ResolveOutput; + type _o1 = ResolveOutput; // $ExpectType string | undefined - type _o2 = ResolveOutput; + type _o2 = ResolveOutput; // $ExpectType string - type _o22 = ResolveOutput; + type _o22 = ResolveOutput; // $ExpectType string - type _o3 = ResolveOutput; + type _o3 = ResolveOutput; // $ExpectType string - type _o4 = ResolveOutput; + type _o4 = ResolveOutput; // $ExpectType string - type _o5 = ResolveOutput; + type _o5 = ResolveOutput; // $ExpectType string | null | undefined - type _o6 = ResolveOutput; + type _o6 = ResolveOutput; // $ExpectType string | null - type _o7 = ResolveOutput; + type _o7 = ResolveOutput; // $ExpectType string | null - type _o8 = ResolveOutput; + type _o8 = ResolveOutput; // $ExpectType string - type _o9 = ResolveOutput; - - // $ExpectError number is not a MaybeString - type _e1 = ResolveOutput; + type _o9 = ResolveOutput; const strRequired = string().required(); @@ -107,7 +91,7 @@ string().required().nullable(); const strDefined = string().default(''); - // $ExpectType "" + // $ExpectType string const _strDefined = strDefined.getDefault(); const strDefault = string().nullable().default(''); @@ -140,6 +124,9 @@ string().required().nullable(); lazy: lazy(() => number().required()), }); + type F = StringSchema; + type f = F extends TypedSchema ? F['__inputType'] : false; + // const f = obj.cast({}); // f!.number; // f!.string; @@ -181,14 +168,17 @@ string().required().nullable(); // // Object Defaults // - const dflt1 = obj.getDefault(); + const dflt1 = obj.getDefaultFromShape(); - // $ExpectType 1 + // $ExpectType number dflt1.number; // $ExpectType undefined dflt1.ref; + // $ExpectType undefined + dflt1.lazy; + // $ExpectType undefined dflt1.string; @@ -209,6 +199,18 @@ string().required().nullable(); merge.cast({}).other; } +ObjectSchemaOf: { + type Person = { + firstName: string; + }; + + type PersonSchema = ObjectSchemaOf; + + const _t: PersonSchema = object({ + firstName: string().defined(), + }); +} + { // const str = string(); // type f = Type; @@ -285,6 +287,7 @@ string().required().nullable(); mixed().required().concat(mixed()).validateSync(''); let _f = mixed().notRequired(); + // $ExpectType string | undefined const _oo = mixed().required().concat(_f).validateSync(''); From 9fc73aa4248ad5e0c3b3c4707defd905e346abe9 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 24 Nov 2020 16:24:51 -0500 Subject: [PATCH 19/31] WIP --- src/Base.ts | 22 ++++++++++++---------- src/Condition.ts | 22 +++++++++++----------- src/Lazy.ts | 5 ++--- src/Reference.ts | 2 +- src/array.ts | 2 +- src/boolean.ts | 14 +++++++++++--- src/date.ts | 12 +++++++++--- src/index.ts | 12 ++++++++++++ src/object.ts | 10 +++++----- src/types.ts | 4 ++-- src/util/createValidation.ts | 6 +++--- src/util/isSchema.ts | 4 ++-- 12 files changed, 71 insertions(+), 44 deletions(-) diff --git a/src/Base.ts b/src/Base.ts index 0a1f29cd5..576b570e5 100644 --- a/src/Base.ts +++ b/src/Base.ts @@ -26,6 +26,7 @@ import { import { ValidationError } from '.'; import type { Asserts, Presence, ResolveOutput, Unset } from './util/types'; import ReferenceSet from './util/ReferenceSet'; +import Reference from './Reference'; const UNSET = 'unset' as const; @@ -46,12 +47,13 @@ export type SchemaOptions = { spec?: SchemaSpec; }; -export type AnyBase = BaseSchema; +export type AnyBase = BaseSchema; export interface CastOptions { parent?: any; context?: {}; assert?: boolean; + stripUnknown?: boolean; // XXX: should be private? path?: string; } @@ -406,7 +408,7 @@ export default abstract class BaseSchema< value: any, options?: ValidateOptions, ): Promise; - validate(value: any, options: ValidateOptions = {}, maybeCb?: Callback) { + validate(value: any, options?: ValidateOptions, maybeCb?: Callback) { let schema = this.resolve({ ...options, value }); // callback case is for nested validations @@ -420,10 +422,7 @@ export default abstract class BaseSchema< ); } - validateSync( - value: any, - options: ValidateOptions = {}, - ): this['__outputType'] { + validateSync(value: any, options?: ValidateOptions): this['__outputType'] { let schema = this.resolve({ ...options, value }); let result: any; @@ -435,7 +434,7 @@ export default abstract class BaseSchema< return result; } - async isValid(value: any, options: ValidateOptions): Promise { + async isValid(value: any, options?: ValidateOptions): Promise { try { await this.validate(value, options); return true; @@ -445,7 +444,7 @@ export default abstract class BaseSchema< } } - isValidSync(value: any, options: ValidateOptions): value is Asserts { + isValidSync(value: any, options?: ValidateOptions): value is Asserts { try { this.validateSync(value, options); return true; @@ -647,7 +646,10 @@ export default abstract class BaseSchema< return next; } - oneOf(enums: Maybe[], message = locale.oneOf): this { + oneOf( + enums: Array | Reference>, + message = locale.oneOf, + ): this { var next = this.clone(); enums.forEach((val) => { @@ -676,7 +678,7 @@ export default abstract class BaseSchema< } notOneOf( - enums: Maybe[], + enums: Array | Reference>, message = locale.notOneOf, ): this { var next = this.clone(); diff --git a/src/Condition.ts b/src/Condition.ts index e6e05fafb..0510fb70f 100644 --- a/src/Condition.ts +++ b/src/Condition.ts @@ -1,22 +1,22 @@ import has from 'lodash/has'; import isSchema from './util/isSchema'; import Reference from './Reference'; -import { AnySchema } from './types'; +import { SchemaLike } from './types'; -export interface ConditionBuilder { - (this: T, value: any, schema: T): T; - (v1: any, v2: any, schema: T): T; - (v1: any, v2: any, v3: any, schema: T): T; - (v1: any, v2: any, v3: any, v4: any, schema: T): T; +export interface ConditionBuilder { + (this: T, value: any, schema: T): SchemaLike; + (v1: any, v2: any, schema: T): SchemaLike; + (v1: any, v2: any, v3: any, schema: T): SchemaLike; + (v1: any, v2: any, v3: any, v4: any, schema: T): SchemaLike; } -export type ConditionConfig = { +export type ConditionConfig = { is: any | ((...values: any[]) => boolean); - then?: T | ((schema: T) => T); - otherwise?: T | ((schema: T) => T); + then?: SchemaLike | ((schema: T) => SchemaLike); + otherwise?: SchemaLike | ((schema: T) => SchemaLike); }; -export type ConditionOptions = +export type ConditionOptions = | ConditionBuilder | ConditionConfig; @@ -26,7 +26,7 @@ export type ResolveOptions = { context?: any; }; -class Condition { +class Condition { fn: ConditionBuilder; constructor(public refs: Reference[], options: ConditionOptions) { diff --git a/src/Lazy.ts b/src/Lazy.ts index b83a76cad..cfea80b2a 100644 --- a/src/Lazy.ts +++ b/src/Lazy.ts @@ -1,9 +1,8 @@ import isSchema from './util/isSchema'; -import Schema, { CastOptions } from './Schema'; import type { Callback, ValidateOptions } from './types'; import type { ResolveOptions } from './Condition'; -import { AnyBase } from './Base'; +import type { AnyBase, CastOptions } from './Base'; import { TypedSchema, TypeOf } from './util/types'; export type LazyBuilder = ( @@ -23,7 +22,7 @@ export type LazyType = LazyReturnValue extends TypedSchema ? TypeOf> : never; -class Lazy implements Schema { +class Lazy { type = 'lazy' as const; __isYupSchema__ = true; diff --git a/src/Reference.ts b/src/Reference.ts index f4b4caac2..3ef8db7b4 100644 --- a/src/Reference.ts +++ b/src/Reference.ts @@ -1,5 +1,5 @@ import { getter } from 'property-expr'; -import { SchemaRefDescription } from './Schema'; +import type { SchemaRefDescription } from './Base'; const prefixes = { context: '$', diff --git a/src/array.ts b/src/array.ts index 22b38be7b..3662eafbc 100644 --- a/src/array.ts +++ b/src/array.ts @@ -200,7 +200,7 @@ export default class ArraySchema< ): ArraySchema< TInner, TypeOf[] | PreserveOptionals, - TypeOf[] | PreserveOptionals, + Asserts[] | PreserveOptionals, TPresence > { // FIXME: this should return a new instance of array without the default to be diff --git a/src/boolean.ts b/src/boolean.ts index 2e79558d1..78053254c 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -2,7 +2,13 @@ import BaseSchema from './Base'; import type { MixedLocale } from './locale'; import MixedSchema from './mixed'; import type { Maybe } from './types'; -import type { Defined, Nullability, Presence, Unset } from './util/types'; +import type { + Defined, + Nullability, + Presence, + StrictNonNullable, + Unset, +} from './util/types'; export function create() { return new BooleanSchema(); @@ -48,6 +54,8 @@ export default interface BooleanSchema< notRequired(): BooleanSchema; // optional(): BooleanSchema; - nullable(isNullable?: true): BooleanSchema; - nullable(isNullable: false): BooleanSchema; + nullable(isNullable?: true): BooleanSchema; + nullable( + isNullable: false, + ): BooleanSchema, TPresence>; } diff --git a/src/date.ts b/src/date.ts index 7429420ac..6ef951a4b 100644 --- a/src/date.ts +++ b/src/date.ts @@ -5,7 +5,13 @@ import { date as locale, MixedLocale } from './locale'; import isAbsent from './util/isAbsent'; import Ref from './Reference'; import type { Maybe } from './types'; -import type { Defined, Nullability, Presence, Unset } from './util/types'; +import type { + Defined, + Nullability, + Presence, + StrictNonNullable, + Unset, +} from './util/types'; import BaseSchema from './Base'; let invalidDate = new Date(''); @@ -103,6 +109,6 @@ export default interface DateSchema< required(msg?: MixedLocale['required']): DateSchema; notRequired(): DateSchema; - nullable(isNullable?: true): DateSchema; - nullable(isNullable: false): DateSchema; + nullable(isNullable?: true): DateSchema; + nullable(isNullable: false): DateSchema, TPresence>; } diff --git a/src/index.ts b/src/index.ts index ca9153645..551c5de75 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import ValidationError from './ValidationError'; import reach from './util/reach'; import isSchema from './util/isSchema'; import setLocale from './setLocale'; +import type { AnyBase as Schema } from './Base'; import type { TypeOf, Asserts, @@ -19,6 +20,16 @@ import type { Unset, } from './util/types'; +function addMethod( + schemaType: (...aarg: any[]) => T, + name: string, + fn: (this: T, ...args: any[]) => T, +): void; +function addMethod Schema>( + schemaType: T, + name: string, + fn: (this: InstanceType, ...args: any[]) => InstanceType, +): void; function addMethod(schemaType: any, name: string, fn: any) { if (!schemaType || !isSchema(schemaType.prototype)) throw new TypeError('You must provide a yup schema constructor function'); @@ -39,6 +50,7 @@ export type { Unset, Asserts as InferType, ObjectSchemaOf, + Schema, }; export { diff --git a/src/object.ts b/src/object.ts index 7629a9a70..1f72067d8 100644 --- a/src/object.ts +++ b/src/object.ts @@ -35,12 +35,12 @@ import BaseSchema, { export type AnyObject = Record; +type ShapeOf = { + [k in keyof T]: BaseSchema, Maybe, InferPresence>; +}; + export type ObjectSchemaOf> = ObjectSchema< - { - [k in keyof T]: BaseSchema, Maybe, InferPresence>; - }, - Partial, - Partial + ShapeOf >; export type ObjectShape = Record>; diff --git a/src/types.ts b/src/types.ts index b849a3820..7fd4e0ab8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,7 @@ import type { AnyBase } from './Base'; import type Lazy from './Lazy'; -export type AnySchema = AnyBase | Lazy; +export type SchemaLike = AnyBase | Lazy; export type Callback = (err: Error | null, value?: T) => void; @@ -40,7 +40,7 @@ export interface InternalOptions extends ValidateOptions { parent?: any; path?: string; sync?: boolean; - from?: { schema: Schema; value: any }[]; + from?: { schema: AnyBase; value: any }[]; } export interface MessageParams { diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts index 09c17b741..5cceb7ea0 100644 --- a/src/util/createValidation.ts +++ b/src/util/createValidation.ts @@ -8,8 +8,8 @@ import { Callback, ExtraParams, } from '../types'; -import Schema from '../Schema'; import Reference from '../Reference'; +import type { AnyBase } from '../Base'; export type CreateErrorOptions = { path?: string; @@ -33,7 +33,7 @@ export type TestFunction = ( context: TestContext, ) => boolean | ValidationError | Promise; -export type TestOptions = { +export type TestOptions = { value: any; path?: string; label?: string; @@ -61,7 +61,7 @@ export default function createValidation(config: { params?: ExtraParams; message?: Message; }) { - function validate( + function validate( { value, path = '', diff --git a/src/util/isSchema.ts b/src/util/isSchema.ts index 908ba1e0f..ea1f05023 100644 --- a/src/util/isSchema.ts +++ b/src/util/isSchema.ts @@ -1,3 +1,3 @@ -import type { AnySchema } from '../types'; +import type { SchemaLike } from '../types'; -export default (obj: any): obj is AnySchema => obj && obj.__isYupSchema__; +export default (obj: any): obj is SchemaLike => obj && obj.__isYupSchema__; From 52d448b00520d48076dafd82c0d758c69a3209fe Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 24 Nov 2020 17:07:20 -0500 Subject: [PATCH 20/31] WIP --- src/Base.ts | 16 ++--- src/array.ts | 179 ++++++++++++++++++++++++++++++------------------ src/mixed.ts | 26 +++---- src/string.d.ts | 57 +++++++++++++++ src/string.ts | 78 ++++++++++++++------- 5 files changed, 244 insertions(+), 112 deletions(-) create mode 100644 src/string.d.ts diff --git a/src/Base.ts b/src/Base.ts index 576b570e5..7c85866fc 100644 --- a/src/Base.ts +++ b/src/Base.ts @@ -47,7 +47,7 @@ export type SchemaOptions = { spec?: SchemaSpec; }; -export type AnyBase = BaseSchema; +export type AnyBase = BaseSchema; export interface CastOptions { parent?: any; @@ -86,15 +86,11 @@ export interface SchemaDescription { tests: Array<{ name?: string; params: ExtraParams | undefined }>; } -export default abstract class BaseSchema< - TCast = any, - TOutput = any, - TPresence extends Presence = Unset -> { +export default abstract class BaseSchema { readonly type: string; readonly __inputType!: TCast; - readonly __outputType!: ResolveOutput; + readonly __outputType!: TOutput; readonly __isYupSchema__!: boolean; @@ -731,11 +727,7 @@ export default abstract class BaseSchema< } } -export default interface BaseSchema< - TCast, - TOutput, - TPresence extends Presence -> { +export default interface BaseSchema { validateAt( path: string, value: any, diff --git a/src/array.ts b/src/array.ts index 3662eafbc..5a058fbf7 100644 --- a/src/array.ts +++ b/src/array.ts @@ -28,19 +28,22 @@ import BaseSchema, { type RejectorFn = (value: any, index: number, array: any[]) => boolean; -export function create(type?: TInner) { +export function create(type?: BaseSchema) { return new ArraySchema(type); } -export default class ArraySchema< - T extends AnyBase = AnyBase, - TType extends Maybe[]> = TypeOf[] | undefined, - TOut extends Maybe[]> = Asserts[] | undefined, - TPresence extends Presence = Unset -> extends BaseSchema { - innerType: T | undefined; +abstract class BaseArraySchema> extends BaseSchema< + TType, + TType +> {} - constructor(type?: T) { +export default class ArraySchema extends BaseArraySchema< + TType, + TType[] | undefined +> { + innerType?: AnyBase; + + constructor(type?: AnyBase) { super({ type: 'array' }); // `undefined` specifically means uninitialized, as opposed to @@ -61,7 +64,7 @@ export default class ArraySchema< }); } - protected _typeCheck(v: any): v is NonNullable { + protected _typeCheck(v: any): v is TType[] { return Array.isArray(v); } @@ -166,21 +169,21 @@ export default class ArraySchema< return next; } - concat>( - schema: TOther, - ): TOther extends ArraySchema - ? // hoooo boy - ArraySchema< - T, - | (TypeOf & TypeOf)[] - | PreserveOptionals - | PreserveOptionals, - | (Asserts & Asserts)[] - | PreserveOptionals - | PreserveOptionals, - P extends Unset ? TPresence : P - > - : never; + // concat>( + // schema: TOther, + // ): TOther extends ArraySchema + // ? // hoooo boy + // ArraySchema< + // T, + // | (TypeOf & TypeOf)[] + // | PreserveOptionals + // | PreserveOptionals, + // | (Asserts & Asserts)[] + // | PreserveOptionals + // | PreserveOptionals, + // P extends Unset ? TPresence : P + // > + // : never; concat(schema: any): any; concat(schema: any): any { let next = super.concat(schema) as this; @@ -195,14 +198,7 @@ export default class ArraySchema< return next; } - of( - schema: TInner, - ): ArraySchema< - TInner, - TypeOf[] | PreserveOptionals, - Asserts[] | PreserveOptionals, - TPresence - > { + of(schema: BaseSchema): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -261,8 +257,8 @@ export default class ArraySchema< }); } - ensure(): ArraySchema, NonNullable, TPresence> { - return this.default[]>(() => []).transform((val, original) => { + ensure(): RequiredArraySchema { + return this.default(() => []).transform((val, original) => { // We don't want to return `null` for nullable schema if (this._typeCheck(val)) return val; return original == null ? [] : [].concat(original); @@ -286,34 +282,87 @@ export default class ArraySchema< } } -export default interface ArraySchema< - T extends AnyBase, - TType extends Maybe[]>, - TOut extends Maybe[]>, - TPresence extends Presence = Unset -> extends BaseSchema { - defined(msg?: MixedLocale['defined']): ArraySchema; - required( - msg?: MixedLocale['required'], - ): ArraySchema; - notRequired(): ArraySchema; - - nullable( - isNullable?: true, - ): ArraySchema; - nullable( - isNullable: false, - ): ArraySchema< - T, - StrictNonNullable, - StrictNonNullable, - TPresence - >; - nullable(isNullable?: boolean): ArraySchema; - - default[] | null | undefined>( - def: TNextDefault | (() => TNextDefault), - ): TNextDefault extends undefined - ? ArraySchema - : ArraySchema, Defined, TPresence>; +// export default interface ArraySchema< +// T extends AnyBase, +// TType extends Maybe[]>, +// TOut extends Maybe[]> +// > extends BaseSchema { +// defined(msg?: MixedLocale['defined']): ArraySchema; +// required( +// msg?: MixedLocale['required'], +// ): ArraySchema; +// notRequired(): ArraySchema; + +// nullable( +// isNullable?: true, +// ): ArraySchema; +// nullable( +// isNullable: false, +// ): ArraySchema< +// T, +// StrictNonNullable, +// StrictNonNullable, +// TPresence +// >; +// nullable(isNullable?: boolean): ArraySchema; + +// default[] | null | undefined>( +// def: TNextDefault | (() => TNextDefault), +// ): TNextDefault extends undefined +// ? ArraySchema +// : ArraySchema, Defined, TPresence>; +// } + +// +// Interfaces +// +interface DefinedArraySchema extends BaseArraySchema { + // default>( + // def: Thunk, + // ): IIF< + // D, + // DefinedArraySchema, + // DefinedArraySchema> + // >; + + defined(msg?: MixedLocale['defined']): this; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): ArraySchema; + nullable(isNullable?: true): RequiredArraySchema; + nullable(isNullable: false): RequiredArraySchema>; +} + +interface RequiredArraySchema + extends BaseArraySchema> { + // default>( + // def: Thunk, + // ): IIF< + // D, + // RequiredArraySchema, + // RequiredArraySchema> + // >; + + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): ArraySchema; + nullable(isNullable?: true): RequiredArraySchema; + nullable(isNullable: false): RequiredArraySchema>; +} + +type Thunk = T | (() => T); + +type IIF = T extends undefined ? Y : N; + +export default interface ArraySchema + extends BaseArraySchema { + // default>( + // def: Thunk, + // ): IIF, ArraySchema>>; + + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): ArraySchema; + + nullable(isNullable?: true): ArraySchema; + nullable(isNullable: false): ArraySchema>; } diff --git a/src/mixed.ts b/src/mixed.ts index 694fdb406..f30cfebab 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -8,22 +8,24 @@ export function create() { return new MixedSchema(); } -export default class MixedSchema< - TType = any, - TPresence extends Presence = Unset -> extends BaseSchema {} +export default class MixedSchema extends BaseSchema< + TType, + TType +> {} -export default interface MixedSchema { +export default interface MixedSchema { default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? MixedSchema - : MixedSchema, TPresence>; + ? MixedSchema + : MixedSchema, Defined>; - defined(msg?: MixedLocale['defined']): MixedSchema; - required(msg?: MixedLocale['required']): MixedSchema; - notRequired(): MixedSchema; + defined(msg?: MixedLocale['defined']): MixedSchema>; + required( + msg?: MixedLocale['required'], + ): MixedSchema>; + notRequired(): MixedSchema; - nullable(isNullable?: true): MixedSchema; - nullable(isNullable: false): MixedSchema, TPresence>; + nullable(isNullable?: true): MixedSchema; + nullable(isNullable: false): MixedSchema>; } diff --git a/src/string.d.ts b/src/string.d.ts new file mode 100644 index 000000000..f03cf922c --- /dev/null +++ b/src/string.d.ts @@ -0,0 +1,57 @@ +import type { MixedLocale } from './locale'; +import type { Maybe } from './types'; +import type { Defined, StrictNonNullable } from './util/types'; +import type BaseSchema from './Base'; + +interface DefinedStringSchema> + extends BaseSchema> { + default>( + def: Thunk, + ): IIF< + D, + DefinedStringSchema, + DefinedStringSchema> + >; + + defined(msg?: MixedLocale['defined']): this; + required(msg?: MixedLocale['required']): RequiredStringSchema; + notRequired(): StringSchema; + nullable(isNullable?: true): RequiredStringSchema; + nullable(isNullable: false): RequiredStringSchema>; +} + +interface RequiredStringSchema> + extends BaseSchema> { + default>( + def: Thunk, + ): IIF< + D, + RequiredStringSchema, + RequiredStringSchema> + >; + + defined(msg?: MixedLocale['defined']): DefinedStringSchema; + required(msg?: MixedLocale['required']): RequiredStringSchema; + notRequired(): StringSchema; + nullable(isNullable?: true): RequiredStringSchema; + nullable(isNullable: false): RequiredStringSchema>; +} + +type Thunk = T | (() => T); + +type IIF = T extends undefined ? Y : N; +export default interface StringSchema> + extends BaseSchema { + default>( + def: Thunk, + ): IIF, StringSchema>>; + + defined(msg?: MixedLocale['defined']): DefinedStringSchema; + required(msg?: MixedLocale['required']): RequiredStringSchema; + notRequired(): StringSchema; + + nullable(isNullable?: true): StringSchema; + nullable(isNullable: false): StringSchema>; +} + +create().required().nullable().defined().validateSync(''); diff --git a/src/string.ts b/src/string.ts index 215ccb1c1..9b3af1662 100644 --- a/src/string.ts +++ b/src/string.ts @@ -28,9 +28,8 @@ export function create() { } export default class StringSchema< - TType extends Maybe = string | undefined, - TPresence extends Presence = Unset -> extends BaseSchema { + TType extends Maybe = string | undefined +> extends BaseSchema { constructor() { super({ type: 'string' }); @@ -157,7 +156,7 @@ export default class StringSchema< } //-- transforms -- - ensure(): StringSchema, TPresence> { + ensure(): StringSchema> { return this.default('' as Defined).transform((val) => val === null ? '' : val, ) as any; @@ -196,23 +195,56 @@ export default class StringSchema< } } -export default interface StringSchema< - TType extends Maybe, - TPresence extends Presence -> extends BaseSchema { - default>( - def: TNextDefault | (() => TNextDefault), - ): TNextDefault extends undefined - ? StringSchema - : StringSchema, TPresence>; - - defined(msg?: MixedLocale['defined']): StringSchema; - - required(msg?: MixedLocale['required']): StringSchema; - notRequired(): StringSchema; - - nullable(isNullable?: true): StringSchema; - nullable( - isNullable: false, - ): StringSchema, TPresence>; +// +// String Interfaces +// +interface DefinedStringSchema> + extends BaseSchema> { + default>( + def: Thunk, + ): IIF< + D, + DefinedStringSchema, + DefinedStringSchema> + >; + + defined(msg?: MixedLocale['defined']): this; + required(msg?: MixedLocale['required']): RequiredStringSchema; + notRequired(): StringSchema; + nullable(isNullable?: true): RequiredStringSchema; + nullable(isNullable: false): RequiredStringSchema>; +} + +interface RequiredStringSchema> + extends BaseSchema> { + default>( + def: Thunk, + ): IIF< + D, + RequiredStringSchema, + RequiredStringSchema> + >; + + defined(msg?: MixedLocale['defined']): DefinedStringSchema; + required(msg?: MixedLocale['required']): RequiredStringSchema; + notRequired(): StringSchema; + nullable(isNullable?: true): RequiredStringSchema; + nullable(isNullable: false): RequiredStringSchema>; +} + +type Thunk = T | (() => T); + +type IIF = T extends undefined ? Y : N; +export default interface StringSchema> + extends BaseSchema { + default>( + def: Thunk, + ): IIF, StringSchema>>; + + defined(msg?: MixedLocale['defined']): DefinedStringSchema; + required(msg?: MixedLocale['required']): RequiredStringSchema; + notRequired(): StringSchema; + + nullable(isNullable?: true): StringSchema; + nullable(isNullable: false): StringSchema>; } From 0cac9e643a2ad7a11a94a0e0d55bdddb26148a40 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Sun, 29 Nov 2020 10:03:01 -0500 Subject: [PATCH 21/31] WIP --- src/Base.ts | 2 +- src/array.ts | 208 +++++++++++++++++++++------------------------- src/string.ts | 35 ++++---- src/util/types.ts | 4 + 4 files changed, 122 insertions(+), 127 deletions(-) diff --git a/src/Base.ts b/src/Base.ts index 7c85866fc..d316f0c6c 100644 --- a/src/Base.ts +++ b/src/Base.ts @@ -47,7 +47,7 @@ export type SchemaOptions = { spec?: SchemaSpec; }; -export type AnyBase = BaseSchema; +export type AnyBase = BaseSchema; export interface CastOptions { parent?: any; diff --git a/src/array.ts b/src/array.ts index 5a058fbf7..89ae16a50 100644 --- a/src/array.ts +++ b/src/array.ts @@ -1,7 +1,7 @@ import isAbsent from './util/isAbsent'; import isSchema from './util/isSchema'; import printValue from './util/printValue'; -import { array as locale, MixedLocale } from './locale'; +import { array, array as locale, MixedLocale } from './locale'; import runTests, { RunTest } from './util/runTests'; import type { InternalOptions, @@ -15,8 +15,10 @@ import type Reference from './Reference'; import { Asserts, Defined, + If, Presence, StrictNonNullable, + Thunk, TypeOf, Unset, } from './util/types'; @@ -25,25 +27,25 @@ import BaseSchema, { SchemaInnerTypeDescription, SchemaSpec, } from './Base'; +import { string } from '.'; type RejectorFn = (value: any, index: number, array: any[]) => boolean; -export function create(type?: BaseSchema) { - return new ArraySchema(type); -} +type PreserveOptionality = T extends Array ? unknown[] : T; + +type f = PreserveOptionality; -abstract class BaseArraySchema> extends BaseSchema< - TType, - TType -> {} +export function create(type?: BaseSchema) { + return new ArraySchema(type); +} -export default class ArraySchema extends BaseArraySchema< - TType, - TType[] | undefined -> { - innerType?: AnyBase; +class BaseArraySchema< + TType extends Maybe, + TOut extends Maybe = TType +> extends BaseSchema { + innerType?: AnyBase; - constructor(type?: AnyBase) { + constructor(type?: AnyBase) { super({ type: 'array' }); // `undefined` specifically means uninitialized, as opposed to @@ -64,7 +66,7 @@ export default class ArraySchema extends BaseArraySchema< }); } - protected _typeCheck(v: any): v is TType[] { + protected _typeCheck(v: any): v is NonNullable { return Array.isArray(v); } @@ -169,21 +171,11 @@ export default class ArraySchema extends BaseArraySchema< return next; } - // concat>( - // schema: TOther, - // ): TOther extends ArraySchema - // ? // hoooo boy - // ArraySchema< - // T, - // | (TypeOf & TypeOf)[] - // | PreserveOptionals - // | PreserveOptionals, - // | (Asserts & Asserts)[] - // | PreserveOptionals - // | PreserveOptionals, - // P extends Unset ? TPresence : P - // > - // : never; + concat>( + schema: TOther, + ): TOther extends ArraySchema + ? ArraySchema + : never; concat(schema: any): any; concat(schema: any): any { let next = super.concat(schema) as this; @@ -198,7 +190,9 @@ export default class ArraySchema extends BaseArraySchema< return next; } - of(schema: BaseSchema): ArraySchema { + of( + schema: BaseSchema, + ): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -257,12 +251,14 @@ export default class ArraySchema extends BaseArraySchema< }); } - ensure(): RequiredArraySchema { - return this.default(() => []).transform((val, original) => { - // We don't want to return `null` for nullable schema - if (this._typeCheck(val)) return val; - return original == null ? [] : [].concat(original); - }) as any; + ensure(): RequiredArraySchema { + return this.default(() => ([] as any) as TType).transform( + (val: TType, original: any) => { + // We don't want to return `null` for nullable schema + if (this._typeCheck(val)) return val; + return original == null ? [] : [].concat(original); + }, + ) as any; } compact(rejector?: RejectorFn) { @@ -282,87 +278,75 @@ export default class ArraySchema extends BaseArraySchema< } } -// export default interface ArraySchema< -// T extends AnyBase, -// TType extends Maybe[]>, -// TOut extends Maybe[]> -// > extends BaseSchema { -// defined(msg?: MixedLocale['defined']): ArraySchema; -// required( -// msg?: MixedLocale['required'], -// ): ArraySchema; -// notRequired(): ArraySchema; - -// nullable( -// isNullable?: true, -// ): ArraySchema; -// nullable( -// isNullable: false, -// ): ArraySchema< -// T, -// StrictNonNullable, -// StrictNonNullable, -// TPresence -// >; -// nullable(isNullable?: boolean): ArraySchema; - -// default[] | null | undefined>( -// def: TNextDefault | (() => TNextDefault), -// ): TNextDefault extends undefined -// ? ArraySchema -// : ArraySchema, Defined, TPresence>; -// } +export default class ArraySchema< + TType extends Maybe = any[] | undefined, + TOut extends Maybe = any[] | undefined +> extends BaseArraySchema {} // // Interfaces // -interface DefinedArraySchema extends BaseArraySchema { - // default>( - // def: Thunk, - // ): IIF< - // D, - // DefinedArraySchema, - // DefinedArraySchema> - // >; - - defined(msg?: MixedLocale['defined']): this; - required(msg?: MixedLocale['required']): RequiredArraySchema; - notRequired(): ArraySchema; - nullable(isNullable?: true): RequiredArraySchema; - nullable(isNullable: false): RequiredArraySchema>; -} -interface RequiredArraySchema - extends BaseArraySchema> { - // default>( - // def: Thunk, - // ): IIF< - // D, - // RequiredArraySchema, - // RequiredArraySchema> - // >; - - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; - notRequired(): ArraySchema; - nullable(isNullable?: true): RequiredArraySchema; - nullable(isNullable: false): RequiredArraySchema>; +type ElementOf = T extends Array ? E : never; + +interface DefinedArraySchema< + TType extends Maybe>, + TOut extends Maybe> +> extends BaseArraySchema> { + default>( + def: Thunk, + ): If< + D, + DefinedArraySchema, + DefinedArraySchema, Defined> + >; + + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): ArraySchema; + nullable(isNullable?: true): DefinedArraySchema; + nullable( + isNullable: false, + ): RequiredArraySchema, StrictNonNullable>; } -type Thunk = T | (() => T); - -type IIF = T extends undefined ? Y : N; - -export default interface ArraySchema - extends BaseArraySchema { - // default>( - // def: Thunk, - // ): IIF, ArraySchema>>; - - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; - notRequired(): ArraySchema; +interface RequiredArraySchema< + TType extends Maybe, + TOut extends Maybe +> extends BaseArraySchema> { + default>( + def: Thunk, + ): If< + D, + RequiredArraySchema, + RequiredArraySchema, TOut> + >; + + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): ArraySchema; + nullable(isNullable?: true): RequiredArraySchema; + nullable( + isNullable: false, + ): RequiredArraySchema, TOut>; +} - nullable(isNullable?: true): ArraySchema; - nullable(isNullable: false): ArraySchema>; +export default interface ArraySchema< + TType extends Maybe = any[] | undefined, + TOut extends Maybe = any[] | undefined +> extends BaseArraySchema { + default>( + def: Thunk, + ): If, ArraySchema>>; + + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): this; + + nullable(isNullable?: true): ArraySchema; + nullable( + isNullable: false, + ): ArraySchema, StrictNonNullable>; } + +let f = create(string().required()).nullable().validateSync(''); diff --git a/src/string.ts b/src/string.ts index 9b3af1662..127dc2736 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,8 +1,15 @@ -import { MixedLocale, string as locale } from './locale'; +import { MixedLocale, string as locale, string } from './locale'; import isAbsent from './util/isAbsent'; import type Reference from './Reference'; import type { Message, Maybe } from './types'; -import type { Defined, Presence, StrictNonNullable, Unset } from './util/types'; +import type { + Defined, + If, + Presence, + StrictNonNullable, + Thunk, + Unset, +} from './util/types'; import BaseSchema from './Base'; // eslint-disable-next-line @@ -28,8 +35,9 @@ export function create() { } export default class StringSchema< - TType extends Maybe = string | undefined -> extends BaseSchema { + TType extends Maybe = string | undefined, + TOut extends TType = TType +> extends BaseSchema { constructor() { super({ type: 'string' }); @@ -199,10 +207,10 @@ export default class StringSchema< // String Interfaces // interface DefinedStringSchema> - extends BaseSchema> { + extends StringSchema> { default>( def: Thunk, - ): IIF< + ): If< D, DefinedStringSchema, DefinedStringSchema> @@ -216,10 +224,10 @@ interface DefinedStringSchema> } interface RequiredStringSchema> - extends BaseSchema> { + extends StringSchema> { default>( def: Thunk, - ): IIF< + ): If< D, RequiredStringSchema, RequiredStringSchema> @@ -232,14 +240,13 @@ interface RequiredStringSchema> nullable(isNullable: false): RequiredStringSchema>; } -type Thunk = T | (() => T); - -type IIF = T extends undefined ? Y : N; -export default interface StringSchema> - extends BaseSchema { +export default interface StringSchema< + TType extends Maybe = string | undefined, + TOut extends TType = TType +> extends BaseSchema { default>( def: Thunk, - ): IIF, StringSchema>>; + ): If, StringSchema>>; defined(msg?: MixedLocale['defined']): DefinedStringSchema; required(msg?: MixedLocale['required']): RequiredStringSchema; diff --git a/src/util/types.ts b/src/util/types.ts index b13c94f64..58d893ad4 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -44,3 +44,7 @@ export type TypedSchema = { export type TypeOf = TSchema['__inputType']; export type Asserts = TSchema['__outputType']; + +export type Thunk = T | (() => T); + +export type If = T extends undefined ? Y : N; From 2e2fe400205ad0e4bbe0046e2aabf7eef06a1886 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 30 Nov 2020 08:29:32 -0500 Subject: [PATCH 22/31] WIP --- src/number.ts | 76 +++++++++++++++++++++--------- src/object.ts | 126 +++++++++++++++++++++++++------------------------- 2 files changed, 118 insertions(+), 84 deletions(-) diff --git a/src/number.ts b/src/number.ts index 3cbd92029..085f22984 100644 --- a/src/number.ts +++ b/src/number.ts @@ -5,7 +5,8 @@ import type { Maybe } from './types'; import type Reference from './Reference'; import type { Defined, - Nullability, + If, + Thunk, Presence, StrictNonNullable, Unset, @@ -20,8 +21,8 @@ export function create() { export default class NumberSchema< TType extends Maybe = number | undefined, - TPresence extends Presence = Unset -> extends BaseSchema { + TOut extends TType = TType +> extends BaseSchema { constructor() { super({ type: 'number' }); @@ -135,23 +136,56 @@ export default class NumberSchema< } } +// +// Number Interfaces +// +interface DefinedNumberSchema> + extends NumberSchema> { + default>( + def: Thunk, + ): If< + D, + DefinedNumberSchema, + DefinedNumberSchema> + >; + + defined(msg?: MixedLocale['defined']): this; + required(msg?: MixedLocale['required']): RequiredNumberSchema; + notRequired(): NumberSchema; + nullable(isNullable?: true): RequiredNumberSchema; + nullable(isNullable: false): RequiredNumberSchema>; +} + +interface RequiredNumberSchema> + extends NumberSchema> { + default>( + def: Thunk, + ): If< + D, + RequiredNumberSchema, + RequiredNumberSchema> + >; + + defined(msg?: MixedLocale['defined']): DefinedNumberSchema; + required(msg?: MixedLocale['required']): RequiredNumberSchema; + notRequired(): NumberSchema; + nullable(isNullable?: true): RequiredNumberSchema; + nullable(isNullable: false): RequiredNumberSchema>; +} + export default interface NumberSchema< - TType extends Maybe, - TPresence extends Presence -> extends BaseSchema { - default>( - def: TNextDefault | (() => TNextDefault), - ): TNextDefault extends undefined - ? NumberSchema - : NumberSchema, TPresence>; - - defined(msg?: MixedLocale['defined']): NumberSchema; - - required(msg?: MixedLocale['required']): NumberSchema; - notRequired(): NumberSchema; - - nullable(isNullable?: true): NumberSchema; - nullable( - isNullable: false, - ): NumberSchema, TPresence>; + TType extends Maybe = number | undefined, + TOut extends TType = TType +> extends BaseSchema { + default>( + def: Thunk, + ): If, NumberSchema>>; + + defined(msg?: MixedLocale['defined']): DefinedNumberSchema; + + required(msg?: MixedLocale['required']): RequiredNumberSchema; + notRequired(): NumberSchema; + + nullable(isNullable?: true): NumberSchema; + nullable(isNullable: false): NumberSchema>; } diff --git a/src/object.ts b/src/object.ts index 1f72067d8..57dcff0e5 100644 --- a/src/object.ts +++ b/src/object.ts @@ -35,13 +35,13 @@ import BaseSchema, { export type AnyObject = Record; -type ShapeOf = { - [k in keyof T]: BaseSchema, Maybe, InferPresence>; -}; +// type ShapeOf = { +// [k in keyof T]: BaseSchema, Maybe, InferPresence>; +// }; -export type ObjectSchemaOf> = ObjectSchema< - ShapeOf ->; +// export type ObjectSchemaOf> = ObjectSchema< +// ShapeOf +// >; export type ObjectShape = Record>; @@ -49,15 +49,15 @@ export function create(spec?: TShape) { return new ObjectSchema(spec); } -export type DefaultFromShape = { - [K in keyof Shape]: Shape[K] extends ObjectSchema - ? DefaultFromShape - : Shape[K] extends { getDefault: () => infer D } - ? Preserve extends never - ? Defined - : Preserve - : undefined; -}; +// export type DefaultFromShape = { +// [K in keyof Shape]: Shape[K] extends ObjectSchema +// ? DefaultFromShape +// : Shape[K] extends { getDefault: () => infer D } +// ? Preserve extends never +// ? Defined +// : Preserve +// : undefined; +// }; type AssignShape = { [P in keyof T]: P extends keyof U ? U[P] : T[P]; @@ -94,13 +94,26 @@ function unknown(ctx: ObjectSchema, value: any) { const defaultSort = sortByKeyOrder([]); +type SameShape = { [K in keyof T]: any }; + +type ObjectShape< + T extends Maybe, + TOut extends Maybe> +> = { + // This shouldn't be necessary because MixedSchema extends Schema, but type + // inference only works with it this way - otherwise when you use a mixed + // field in object schema, it will type as `unknown`. Not sure why that is - + // maybe some sort of inference depth limit? + [field in keyof T]: BaseSchema | Reference; +}; + export default class ObjectSchema< - TShape extends ObjectShape = ObjectShape, - TType extends Maybe = TypeOfShape, - TOut extends Maybe = AssertsShape, - TPresence extends Presence = Unset -> extends BaseSchema { - fields: TShape = Object.create(null); + TType extends Maybe = AnyObject | undefined, + TOut extends Maybe> = SameShape | undefined +> extends BaseSchema { + fields: { + [k in keyof TType]: BaseSchema[k]>; + } = Object.create(null); spec!: ObjectSchemaSpec; @@ -108,7 +121,7 @@ export default class ObjectSchema< private _nodes: readonly string[] = []; private _excludedEdges: readonly string[] = []; - constructor(spec?: TShape) { + constructor(spec?: ObjectShape) { super({ type: 'object', }); @@ -299,16 +312,16 @@ export default class ObjectSchema< return next; } - concat>( - schema: TOther, - ): TOther extends ObjectSchema - ? ObjectSchema< - TShape & S, - TypeOfShape | PreserveOptionals, - AssertsShape | PreserveOptionals, - P extends Unset ? TPresence : P - > - : never; + // concat>( + // schema: TOther, + // ): TOther extends ObjectSchema + // ? ObjectSchema< + // TShape & S, + // TypeOfShape | PreserveOptionals, + // AssertsShape | PreserveOptionals, + // P extends Unset ? TPresence : P + // > + // : never; concat(schema: AnyBase): AnyBase; concat(schema: any): any { let next = super.concat(schema) as any; @@ -329,14 +342,14 @@ export default class ObjectSchema< return next.withMutation((next: any) => next.shape(nextFields)); } - getDefaultFromShape(): DefaultFromShape { - let dft = {} as Record; - this._nodes.forEach((key) => { - const field = this.fields[key]; - dft[key] = 'default' in field ? field.getDefault() : undefined; - }); - return dft as any; - } + // getDefaultFromShape(): DefaultFromShape { + // let dft = {} as Record; + // this._nodes.forEach((key) => { + // const field = this.fields[key]; + // dft[key] = 'default' in field ? field.getDefault() : undefined; + // }); + // return dft as any; + // } protected _getDefault() { if ('default' in this.spec) { @@ -471,34 +484,21 @@ export default class ObjectSchema< } export default interface ObjectSchema< - TShape extends ObjectShape, - TType extends Maybe, - TOut extends Maybe, - TPresence extends Presence -> extends BaseSchema { + TType extends Maybe = AnyObject | undefined, + TOut extends Maybe> = SameShape | undefined +> extends BaseSchema { default>>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? ObjectSchema - : ObjectSchema, Defined, TPresence>; + ? ObjectSchema + : ObjectSchema, Defined>; - defined( - msg?: MixedLocale['defined'], - ): ObjectSchema; - required( - msg?: MixedLocale['required'], - ): ObjectSchema; - notRequired(): ObjectSchema; + defined(msg?: MixedLocale['defined']): ObjectSchema; + required(msg?: MixedLocale['required']): ObjectSchema; + notRequired(): ObjectSchema; - nullable( - isNullable?: true, - ): ObjectSchema; + nullable(isNullable?: true): ObjectSchema; nullable( isNullable: false, - ): ObjectSchema< - TShape, - StrictNonNullable, - StrictNonNullable, - TPresence - >; + ): ObjectSchema, StrictNonNullable>; } From 7cd6fe64211b42c58d2a7952c8fcc241a98ac5b6 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 30 Nov 2020 08:45:52 -0500 Subject: [PATCH 23/31] WIP --- src/array.ts | 127 ++++++++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 56 deletions(-) diff --git a/src/array.ts b/src/array.ts index 89ae16a50..370a9143f 100644 --- a/src/array.ts +++ b/src/array.ts @@ -35,17 +35,18 @@ type PreserveOptionality = T extends Array ? unknown[] : T; type f = PreserveOptionality; -export function create(type?: BaseSchema) { - return new ArraySchema(type); +export function create(type?: T) { + return new ArraySchema(type); } class BaseArraySchema< - TType extends Maybe, - TOut extends Maybe = TType -> extends BaseSchema { - innerType?: AnyBase; + T extends AnyBase = AnyBase, + TIn extends Maybe[]> = TypeOf[] | undefined, + TOut extends Maybe[]> = Asserts[] | undefined +> extends BaseSchema { + innerType?: T; - constructor(type?: AnyBase) { + constructor(type?: T) { super({ type: 'array' }); // `undefined` specifically means uninitialized, as opposed to @@ -66,7 +67,7 @@ class BaseArraySchema< }); } - protected _typeCheck(v: any): v is NonNullable { + protected _typeCheck(v: any): v is NonNullable { return Array.isArray(v); } @@ -171,11 +172,21 @@ class BaseArraySchema< return next; } - concat>( - schema: TOther, - ): TOther extends ArraySchema - ? ArraySchema - : never; + // concat>( + // schema: TOther, + // ): TOther extends ArraySchema + // ? // hoooo boy + // ArraySchema< + // T, + // | (TypeOf & TypeOf)[] + // | PreserveOptionals + // | PreserveOptionals, + // | (Asserts & Asserts)[] + // | PreserveOptionals + // | PreserveOptionals, + // P extends Unset ? TPresence : P + // > + // : never; concat(schema: any): any; concat(schema: any): any { let next = super.concat(schema) as this; @@ -190,9 +201,7 @@ class BaseArraySchema< return next; } - of( - schema: BaseSchema, - ): ArraySchema { + of(schema: TInner): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -251,9 +260,9 @@ class BaseArraySchema< }); } - ensure(): RequiredArraySchema { - return this.default(() => ([] as any) as TType).transform( - (val: TType, original: any) => { + ensure(): RequiredArraySchema { + return this.default(() => ([] as any) as TIn).transform( + (val: TIn, original: any) => { // We don't want to return `null` for nullable schema if (this._typeCheck(val)) return val; return original == null ? [] : [].concat(original); @@ -279,74 +288,80 @@ class BaseArraySchema< } export default class ArraySchema< - TType extends Maybe = any[] | undefined, - TOut extends Maybe = any[] | undefined -> extends BaseArraySchema {} + T extends AnyBase, + TIn extends Maybe[]> = TypeOf[] | undefined, + TOut extends Maybe[]> = TypeOf[] | undefined +> extends BaseArraySchema {} // // Interfaces // -type ElementOf = T extends Array ? E : never; - interface DefinedArraySchema< - TType extends Maybe>, - TOut extends Maybe> -> extends BaseArraySchema> { - default>( + T extends AnyBase, + TIn extends Maybe[]>, + TOut extends Maybe[]> +> extends BaseArraySchema, TOut> { + default>( def: Thunk, ): If< D, - DefinedArraySchema, - DefinedArraySchema, Defined> + DefinedArraySchema, + DefinedArraySchema, Defined> >; - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; - notRequired(): ArraySchema; - nullable(isNullable?: true): DefinedArraySchema; + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): ArraySchema; + nullable(isNullable?: true): DefinedArraySchema; nullable( isNullable: false, - ): RequiredArraySchema, StrictNonNullable>; + ): RequiredArraySchema, StrictNonNullable>; } interface RequiredArraySchema< - TType extends Maybe, - TOut extends Maybe -> extends BaseArraySchema> { - default>( + T extends AnyBase, + TIn extends Maybe[]>, + TOut extends Maybe[]> +> extends BaseArraySchema> { + default>( def: Thunk, ): If< D, - RequiredArraySchema, - RequiredArraySchema, TOut> + RequiredArraySchema, + RequiredArraySchema, TOut> >; - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; - notRequired(): ArraySchema; - nullable(isNullable?: true): RequiredArraySchema; + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; + notRequired(): ArraySchema; + nullable(isNullable?: true): RequiredArraySchema; nullable( isNullable: false, - ): RequiredArraySchema, TOut>; + ): RequiredArraySchema, TOut>; } export default interface ArraySchema< - TType extends Maybe = any[] | undefined, - TOut extends Maybe = any[] | undefined -> extends BaseArraySchema { - default>( + T extends AnyBase, + TIn extends Maybe[]> = TypeOf[] | undefined, + TOut extends Maybe[]> = TypeOf[] | undefined +> extends BaseArraySchema { + default>( def: Thunk, - ): If, ArraySchema>>; + ): If< + D, + ArraySchema, + ArraySchema, TOut> + >; - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): RequiredArraySchema; notRequired(): this; - nullable(isNullable?: true): ArraySchema; + nullable(isNullable?: true): ArraySchema; nullable( isNullable: false, - ): ArraySchema, StrictNonNullable>; + ): ArraySchema, StrictNonNullable>; } -let f = create(string().required()).nullable().validateSync(''); +let f = create(string().required()).required().nullable().validateSync(''); From cde3a3988d3db5ab44bb3f629625ac14c670e500 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 30 Nov 2020 09:25:13 -0500 Subject: [PATCH 24/31] WIP --- src/Base.ts | 50 ++++++++++++++------ src/Reference.ts | 2 +- src/array.ts | 114 ++++++++++++++++++++++++++++------------------ src/object.ts | 2 - src/string.d.ts | 57 ----------------------- src/string.ts | 40 ++++++++++------ src/types.ts | 9 ++-- src/util/types.ts | 9 ++++ 8 files changed, 147 insertions(+), 136 deletions(-) delete mode 100644 src/string.d.ts diff --git a/src/Base.ts b/src/Base.ts index d316f0c6c..c4783bafc 100644 --- a/src/Base.ts +++ b/src/Base.ts @@ -21,6 +21,7 @@ import { InternalOptions, Maybe, ExtraParams, + AnyObject, } from './types'; import { ValidationError } from '.'; @@ -86,7 +87,11 @@ export interface SchemaDescription { tests: Array<{ name?: string; params: ExtraParams | undefined }>; } -export default abstract class BaseSchema { +export default abstract class BaseSchema< + TCast = any, + TContext = AnyObject, + TOutput = any +> { readonly type: string; readonly __inputType!: TCast; @@ -199,11 +204,11 @@ export default abstract class BaseSchema { return result; } - concat( - schema: TOther, - ): TOther extends BaseSchema - ? BaseSchema - : never; + // concat( + // schema: TOther, + // ): TOther extends BaseSchema + // ? BaseSchema + // : never; concat(schema: AnyBase): AnyBase; concat(schema: AnyBase): AnyBase { if (!schema || schema === this) return this; @@ -402,9 +407,13 @@ export default abstract class BaseSchema { validate( value: any, - options?: ValidateOptions, + options?: ValidateOptions, ): Promise; - validate(value: any, options?: ValidateOptions, maybeCb?: Callback) { + validate( + value: any, + options?: ValidateOptions, + maybeCb?: Callback, + ) { let schema = this.resolve({ ...options, value }); // callback case is for nested validations @@ -418,7 +427,10 @@ export default abstract class BaseSchema { ); } - validateSync(value: any, options?: ValidateOptions): this['__outputType'] { + validateSync( + value: any, + options?: ValidateOptions, + ): this['__outputType'] { let schema = this.resolve({ ...options, value }); let result: any; @@ -430,7 +442,10 @@ export default abstract class BaseSchema { return result; } - async isValid(value: any, options?: ValidateOptions): Promise { + async isValid( + value: any, + options?: ValidateOptions, + ): Promise { try { await this.validate(value, options); return true; @@ -440,7 +455,10 @@ export default abstract class BaseSchema { } } - isValidSync(value: any, options?: ValidateOptions): value is Asserts { + isValidSync( + value: any, + options?: ValidateOptions, + ): value is Asserts { try { this.validateSync(value, options); return true; @@ -727,13 +745,17 @@ export default abstract class BaseSchema { } } -export default interface BaseSchema { +export default interface BaseSchema { validateAt( path: string, value: any, - options?: ValidateOptions, + options?: ValidateOptions, ): Promise; - validateSyncAt(path: string, value: any, options?: ValidateOptions): TOutput; + validateSyncAt( + path: string, + value: any, + options?: ValidateOptions, + ): TOutput; equals: BaseSchema['oneOf']; is: BaseSchema['oneOf']; not: BaseSchema['notOneOf']; diff --git a/src/Reference.ts b/src/Reference.ts index 3ef8db7b4..ae81a138d 100644 --- a/src/Reference.ts +++ b/src/Reference.ts @@ -4,7 +4,7 @@ import type { SchemaRefDescription } from './Base'; const prefixes = { context: '$', value: '.', -}; +} as const; export type ReferenceOptions = { map?: (value: unknown) => TValue; diff --git a/src/array.ts b/src/array.ts index 370a9143f..da6e33575 100644 --- a/src/array.ts +++ b/src/array.ts @@ -4,11 +4,11 @@ import printValue from './util/printValue'; import { array, array as locale, MixedLocale } from './locale'; import runTests, { RunTest } from './util/runTests'; import type { + AnyObject, InternalOptions, Callback, Message, Maybe, - PreserveOptionals, } from './types'; import ValidationError from './ValidationError'; import type Reference from './Reference'; @@ -16,11 +16,10 @@ import { Asserts, Defined, If, - Presence, + PreserveOptionality, StrictNonNullable, Thunk, TypeOf, - Unset, } from './util/types'; import BaseSchema, { AnyBase, @@ -31,19 +30,19 @@ import { string } from '.'; type RejectorFn = (value: any, index: number, array: any[]) => boolean; -type PreserveOptionality = T extends Array ? unknown[] : T; - -type f = PreserveOptionality; - -export function create(type?: T) { - return new ArraySchema(type); +export function create< + C extends AnyObject = AnyObject, + T extends AnyBase = AnyBase +>(type?: T) { + return new ArraySchema(type); } class BaseArraySchema< - T extends AnyBase = AnyBase, - TIn extends Maybe[]> = TypeOf[] | undefined, - TOut extends Maybe[]> = Asserts[] | undefined -> extends BaseSchema { + T extends AnyBase, + C extends AnyObject, + TIn extends Maybe[]>, + TOut extends Maybe[]> +> extends BaseSchema { innerType?: T; constructor(type?: T) { @@ -260,7 +259,7 @@ class BaseArraySchema< }); } - ensure(): RequiredArraySchema { + ensure(): RequiredArraySchema { return this.default(() => ([] as any) as TIn).transform( (val: TIn, original: any) => { // We don't want to return `null` for nullable schema @@ -289,9 +288,10 @@ class BaseArraySchema< export default class ArraySchema< T extends AnyBase, + TContext extends AnyObject = AnyObject, TIn extends Maybe[]> = TypeOf[] | undefined, - TOut extends Maybe[]> = TypeOf[] | undefined -> extends BaseArraySchema {} + TOut extends Maybe[]> = Asserts[] | undefined +> extends BaseArraySchema {} // // Interfaces @@ -299,69 +299,93 @@ export default class ArraySchema< interface DefinedArraySchema< T extends AnyBase, - TIn extends Maybe[]>, - TOut extends Maybe[]> -> extends BaseArraySchema, TOut> { + TContext extends AnyObject, + TIn extends Maybe[]> +> extends BaseArraySchema[] | undefined> { default>( def: Thunk, ): If< D, - DefinedArraySchema, - DefinedArraySchema, Defined> + DefinedArraySchema, + DefinedArraySchema> >; - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; - notRequired(): ArraySchema; - nullable(isNullable?: true): DefinedArraySchema; + defined(msg?: MixedLocale['defined']): this; + required( + msg?: MixedLocale['required'], + ): RequiredArraySchema; + notRequired(): ArraySchema< + T, + TContext, + TIn, + PreserveOptionality[]> + >; + nullable(isNullable?: true): DefinedArraySchema; nullable( isNullable: false, - ): RequiredArraySchema, StrictNonNullable>; + ): RequiredArraySchema>; } interface RequiredArraySchema< T extends AnyBase, - TIn extends Maybe[]>, - TOut extends Maybe[]> -> extends BaseArraySchema> { + TContext extends AnyObject, + TIn extends Maybe[]> +> extends BaseArraySchema[]> { default>( def: Thunk, ): If< D, - RequiredArraySchema, - RequiredArraySchema, TOut> + RequiredArraySchema, + RequiredArraySchema> >; - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; - notRequired(): ArraySchema; - nullable(isNullable?: true): RequiredArraySchema; + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required(msg?: MixedLocale['required']): this; + notRequired(): ArraySchema< + T, + TContext, + TIn, + PreserveOptionality[]> + >; + nullable(isNullable?: true): RequiredArraySchema; nullable( isNullable: false, - ): RequiredArraySchema, TOut>; + ): RequiredArraySchema>; } export default interface ArraySchema< T extends AnyBase, + TContext extends AnyObject = AnyObject, TIn extends Maybe[]> = TypeOf[] | undefined, - TOut extends Maybe[]> = TypeOf[] | undefined -> extends BaseArraySchema { + TOut extends Maybe[]> = Asserts[] | undefined +> extends BaseArraySchema { default>( def: Thunk, ): If< D, - ArraySchema, - ArraySchema, TOut> + ArraySchema, + ArraySchema, Defined> >; - defined(msg?: MixedLocale['defined']): DefinedArraySchema; - required(msg?: MixedLocale['required']): RequiredArraySchema; + defined(msg?: MixedLocale['defined']): DefinedArraySchema; + required( + msg?: MixedLocale['required'], + ): RequiredArraySchema; notRequired(): this; - nullable(isNullable?: true): ArraySchema; + nullable( + isNullable?: true, + ): ArraySchema; nullable( isNullable: false, - ): ArraySchema, StrictNonNullable>; + ): ArraySchema, StrictNonNullable>; } -let f = create(string().required()).required().nullable().validateSync(''); +let s = create<{ foo: string }>(string().required()) + .required() + .nullable() + .notRequired(); + +let c = s.cast(''); + +let f = s.validateSync('')?.map; diff --git a/src/object.ts b/src/object.ts index 57dcff0e5..147e29326 100644 --- a/src/object.ts +++ b/src/object.ts @@ -33,8 +33,6 @@ import BaseSchema, { SchemaSpec, } from './Base'; -export type AnyObject = Record; - // type ShapeOf = { // [k in keyof T]: BaseSchema, Maybe, InferPresence>; // }; diff --git a/src/string.d.ts b/src/string.d.ts deleted file mode 100644 index f03cf922c..000000000 --- a/src/string.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { MixedLocale } from './locale'; -import type { Maybe } from './types'; -import type { Defined, StrictNonNullable } from './util/types'; -import type BaseSchema from './Base'; - -interface DefinedStringSchema> - extends BaseSchema> { - default>( - def: Thunk, - ): IIF< - D, - DefinedStringSchema, - DefinedStringSchema> - >; - - defined(msg?: MixedLocale['defined']): this; - required(msg?: MixedLocale['required']): RequiredStringSchema; - notRequired(): StringSchema; - nullable(isNullable?: true): RequiredStringSchema; - nullable(isNullable: false): RequiredStringSchema>; -} - -interface RequiredStringSchema> - extends BaseSchema> { - default>( - def: Thunk, - ): IIF< - D, - RequiredStringSchema, - RequiredStringSchema> - >; - - defined(msg?: MixedLocale['defined']): DefinedStringSchema; - required(msg?: MixedLocale['required']): RequiredStringSchema; - notRequired(): StringSchema; - nullable(isNullable?: true): RequiredStringSchema; - nullable(isNullable: false): RequiredStringSchema>; -} - -type Thunk = T | (() => T); - -type IIF = T extends undefined ? Y : N; -export default interface StringSchema> - extends BaseSchema { - default>( - def: Thunk, - ): IIF, StringSchema>>; - - defined(msg?: MixedLocale['defined']): DefinedStringSchema; - required(msg?: MixedLocale['required']): RequiredStringSchema; - notRequired(): StringSchema; - - nullable(isNullable?: true): StringSchema; - nullable(isNullable: false): StringSchema>; -} - -create().required().nullable().defined().validateSync(''); diff --git a/src/string.ts b/src/string.ts index 127dc2736..b6cf2f7e1 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,7 +1,7 @@ import { MixedLocale, string as locale, string } from './locale'; import isAbsent from './util/isAbsent'; import type Reference from './Reference'; -import type { Message, Maybe } from './types'; +import type { Message, Maybe, AnyObject } from './types'; import type { Defined, If, @@ -30,14 +30,15 @@ export type MatchOptions = { let objStringTag = {}.toString(); -export function create() { - return new StringSchema(); +export function create() { + return new StringSchema(); } export default class StringSchema< TType extends Maybe = string | undefined, + TContext extends AnyObject = AnyObject, TOut extends TType = TType -> extends BaseSchema { +> extends BaseSchema { constructor() { super({ type: 'string' }); @@ -206,8 +207,10 @@ export default class StringSchema< // // String Interfaces // -interface DefinedStringSchema> - extends StringSchema> { +interface DefinedStringSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends StringSchema> { default>( def: Thunk, ): If< @@ -223,8 +226,10 @@ interface DefinedStringSchema> nullable(isNullable: false): RequiredStringSchema>; } -interface RequiredStringSchema> - extends StringSchema> { +interface RequiredStringSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends StringSchema> { default>( def: Thunk, ): If< @@ -242,16 +247,23 @@ interface RequiredStringSchema> export default interface StringSchema< TType extends Maybe = string | undefined, + TContext extends AnyObject = AnyObject, TOut extends TType = TType -> extends BaseSchema { +> extends BaseSchema { default>( def: Thunk, - ): If, StringSchema>>; + ): If< + D, + StringSchema, + StringSchema, TContext> + >; - defined(msg?: MixedLocale['defined']): DefinedStringSchema; - required(msg?: MixedLocale['required']): RequiredStringSchema; + defined(msg?: MixedLocale['defined']): DefinedStringSchema; + required( + msg?: MixedLocale['required'], + ): RequiredStringSchema; notRequired(): StringSchema; - nullable(isNullable?: true): StringSchema; - nullable(isNullable: false): StringSchema>; + nullable(isNullable?: true): StringSchema; + nullable(isNullable: false): StringSchema, TContext>; } diff --git a/src/types.ts b/src/types.ts index 7fd4e0ab8..e4899bad8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,8 @@ import type { AnyBase } from './Base'; import type Lazy from './Lazy'; +export type AnyObject = Record; + export type SchemaLike = AnyBase | Lazy; export type Callback = (err: Error | null, value?: T) => void; @@ -11,7 +13,7 @@ export type TransformFunction = ( originalValue: any, ) => any; -export interface ValidateOptions { +export interface ValidateOptions { /** * Only validate the input, and skip and coercion or transformation. Default - false */ @@ -31,10 +33,11 @@ export interface ValidateOptions { /** * Any context needed for validating schema conditions (see: when()) */ - context?: object; + context?: TContext; } -export interface InternalOptions extends ValidateOptions { +export interface InternalOptions + extends ValidateOptions { __validating?: boolean; originalValue?: any; parent?: any; diff --git a/src/util/types.ts b/src/util/types.ts index 58d893ad4..1dcb8e00b 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -48,3 +48,12 @@ export type Asserts = TSchema['__outputType']; export type Thunk = T | (() => T); export type If = T extends undefined ? Y : N; + +type Preserve = T extends U ? U : never; + +export type PreserveOptionality = + | U + | Preserve + | Preserve; + +type f = PreserveOptionality; From 5d346c39030dbde7e52f09c0025d27bb3085d968 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 30 Nov 2020 09:25:17 -0500 Subject: [PATCH 25/31] WIP --- src/object.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/object.ts b/src/object.ts index 147e29326..e4d138b07 100644 --- a/src/object.ts +++ b/src/object.ts @@ -15,6 +15,7 @@ import { Maybe, PreserveOptionals, Preserve, + AnyObject, } from './types'; import ValidationError from './ValidationError'; import type { @@ -94,18 +95,19 @@ const defaultSort = sortByKeyOrder([]); type SameShape = { [K in keyof T]: any }; -type ObjectShape< - T extends Maybe, - TOut extends Maybe> -> = { - // This shouldn't be necessary because MixedSchema extends Schema, but type - // inference only works with it this way - otherwise when you use a mixed - // field in object schema, it will type as `unknown`. Not sure why that is - - // maybe some sort of inference depth limit? - [field in keyof T]: BaseSchema | Reference; -}; +// type ObjectShape< +// T extends Maybe, +// TOut extends Maybe> +// > = { +// // This shouldn't be necessary because MixedSchema extends Schema, but type +// // inference only works with it this way - otherwise when you use a mixed +// // field in object schema, it will type as `unknown`. Not sure why that is - +// // maybe some sort of inference depth limit? +// [field in keyof T]: BaseSchema | Reference; +// }; export default class ObjectSchema< + TShape extends ObjectShape = AnyObject, TType extends Maybe = AnyObject | undefined, TOut extends Maybe> = SameShape | undefined > extends BaseSchema { From 387c26f82c257a90ea7541597417ec00c44c9249 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 30 Nov 2020 10:08:07 -0500 Subject: [PATCH 26/31] WIP --- src/array.ts | 51 +++++------- src/object.ts | 199 +++++++++++++++++++++++++++++----------------- src/types.ts | 2 +- src/util/types.ts | 9 --- test/types.ts | 4 +- 5 files changed, 146 insertions(+), 119 deletions(-) diff --git a/src/array.ts b/src/array.ts index da6e33575..9c5f7fe5b 100644 --- a/src/array.ts +++ b/src/array.ts @@ -9,6 +9,8 @@ import type { Callback, Message, Maybe, + Preserve, + Optionals, } from './types'; import ValidationError from './ValidationError'; import type Reference from './Reference'; @@ -16,7 +18,6 @@ import { Asserts, Defined, If, - PreserveOptionality, StrictNonNullable, Thunk, TypeOf, @@ -289,9 +290,8 @@ class BaseArraySchema< export default class ArraySchema< T extends AnyBase, TContext extends AnyObject = AnyObject, - TIn extends Maybe[]> = TypeOf[] | undefined, - TOut extends Maybe[]> = Asserts[] | undefined -> extends BaseArraySchema {} + TIn extends Maybe[]> = TypeOf[] | undefined +> extends BaseArraySchema[] | Optionals> {} // // Interfaces @@ -301,7 +301,12 @@ interface DefinedArraySchema< T extends AnyBase, TContext extends AnyObject, TIn extends Maybe[]> -> extends BaseArraySchema[] | undefined> { +> extends BaseArraySchema< + T, + TContext, + TIn, + Asserts[] | Preserve + > { default>( def: Thunk, ): If< @@ -314,12 +319,7 @@ interface DefinedArraySchema< required( msg?: MixedLocale['required'], ): RequiredArraySchema; - notRequired(): ArraySchema< - T, - TContext, - TIn, - PreserveOptionality[]> - >; + notRequired(): ArraySchema; nullable(isNullable?: true): DefinedArraySchema; nullable( isNullable: false, @@ -341,12 +341,7 @@ interface RequiredArraySchema< defined(msg?: MixedLocale['defined']): DefinedArraySchema; required(msg?: MixedLocale['required']): this; - notRequired(): ArraySchema< - T, - TContext, - TIn, - PreserveOptionality[]> - >; + notRequired(): ArraySchema; nullable(isNullable?: true): RequiredArraySchema; nullable( isNullable: false, @@ -356,15 +351,14 @@ interface RequiredArraySchema< export default interface ArraySchema< T extends AnyBase, TContext extends AnyObject = AnyObject, - TIn extends Maybe[]> = TypeOf[] | undefined, - TOut extends Maybe[]> = Asserts[] | undefined -> extends BaseArraySchema { + TIn extends Maybe[]> = TypeOf[] | undefined +> extends BaseArraySchema[] | Optionals> { default>( def: Thunk, ): If< D, - ArraySchema, - ArraySchema, Defined> + ArraySchema, + ArraySchema> >; defined(msg?: MixedLocale['defined']): DefinedArraySchema; @@ -373,18 +367,11 @@ export default interface ArraySchema< ): RequiredArraySchema; notRequired(): this; - nullable( - isNullable?: true, - ): ArraySchema; - nullable( - isNullable: false, - ): ArraySchema, StrictNonNullable>; + nullable(isNullable?: true): ArraySchema; + nullable(isNullable: false): ArraySchema>; } -let s = create<{ foo: string }>(string().required()) - .required() - .nullable() - .notRequired(); +let s = create(string().required()).required().nullable().notRequired(); let c = s.cast(''); diff --git a/src/object.ts b/src/object.ts index e4d138b07..633cc93a9 100644 --- a/src/object.ts +++ b/src/object.ts @@ -9,14 +9,7 @@ import { MixedLocale, object as locale } from './locale'; import sortFields from './util/sortFields'; import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; -import { - InternalOptions, - Callback, - Maybe, - PreserveOptionals, - Preserve, - AnyObject, -} from './types'; +import { InternalOptions, Callback, Maybe, Optionals, Preserve } from './types'; import ValidationError from './ValidationError'; import type { Presence, @@ -34,13 +27,15 @@ import BaseSchema, { SchemaSpec, } from './Base'; -// type ShapeOf = { -// [k in keyof T]: BaseSchema, Maybe, InferPresence>; -// }; +export type AnyObject = Record; -// export type ObjectSchemaOf> = ObjectSchema< -// ShapeOf -// >; +type ShapeOf = { + [k in keyof T]: BaseSchema, any, Maybe>; +}; + +export type ObjectSchemaOf> = ObjectSchema< + ShapeOf +>; export type ObjectShape = Record>; @@ -48,15 +43,15 @@ export function create(spec?: TShape) { return new ObjectSchema(spec); } -// export type DefaultFromShape = { -// [K in keyof Shape]: Shape[K] extends ObjectSchema -// ? DefaultFromShape -// : Shape[K] extends { getDefault: () => infer D } -// ? Preserve extends never -// ? Defined -// : Preserve -// : undefined; -// }; +export type DefaultFromShape = { + [K in keyof Shape]: Shape[K] extends ObjectSchema + ? DefaultFromShape + : Shape[K] extends { getDefault: () => infer D } + ? Preserve extends never + ? Defined + : Preserve + : undefined; +}; type AssignShape = { [P in keyof T]: P extends keyof U ? U[P] : T[P]; @@ -86,34 +81,20 @@ export type ObjectSchemaSpec = SchemaSpec & { let isObject = (obj: any): obj is Record => Object.prototype.toString.call(obj) === '[object Object]'; -function unknown(ctx: ObjectSchema, value: any) { +function unknown(ctx: ObjectSchema, value: any) { let known = Object.keys(ctx.fields); return Object.keys(value).filter((key) => known.indexOf(key) === -1); } const defaultSort = sortByKeyOrder([]); -type SameShape = { [K in keyof T]: any }; - -// type ObjectShape< -// T extends Maybe, -// TOut extends Maybe> -// > = { -// // This shouldn't be necessary because MixedSchema extends Schema, but type -// // inference only works with it this way - otherwise when you use a mixed -// // field in object schema, it will type as `unknown`. Not sure why that is - -// // maybe some sort of inference depth limit? -// [field in keyof T]: BaseSchema | Reference; -// }; - -export default class ObjectSchema< - TShape extends ObjectShape = AnyObject, - TType extends Maybe = AnyObject | undefined, - TOut extends Maybe> = SameShape | undefined -> extends BaseSchema { - fields: { - [k in keyof TType]: BaseSchema[k]>; - } = Object.create(null); +abstract class BaseObjectSchema< + TShape extends ObjectShape, + TContext extends AnyObject, + TIn extends Maybe, + TOut extends Maybe +> extends BaseSchema { + fields: TShape = Object.create(null); spec!: ObjectSchemaSpec; @@ -121,7 +102,7 @@ export default class ObjectSchema< private _nodes: readonly string[] = []; private _excludedEdges: readonly string[] = []; - constructor(spec?: ObjectShape) { + constructor(spec?: TShape) { super({ type: 'object', }); @@ -145,7 +126,7 @@ export default class ObjectSchema< }); } - protected _typeCheck(value: any): value is NonNullable { + protected _typeCheck(value: any): value is NonNullable { return isObject(value) || typeof value === 'function'; } @@ -312,16 +293,15 @@ export default class ObjectSchema< return next; } - // concat>( - // schema: TOther, - // ): TOther extends ObjectSchema - // ? ObjectSchema< - // TShape & S, - // TypeOfShape | PreserveOptionals, - // AssertsShape | PreserveOptionals, - // P extends Unset ? TPresence : P - // > - // : never; + concat>( + schema: TOther, + ): TOther extends ObjectSchema + ? ObjectSchema< + TShape & S, + TypeOfShape | Optionals, + AssertsShape | Optionals + > + : never; concat(schema: AnyBase): AnyBase; concat(schema: any): any { let next = super.concat(schema) as any; @@ -342,14 +322,14 @@ export default class ObjectSchema< return next.withMutation((next: any) => next.shape(nextFields)); } - // getDefaultFromShape(): DefaultFromShape { - // let dft = {} as Record; - // this._nodes.forEach((key) => { - // const field = this.fields[key]; - // dft[key] = 'default' in field ? field.getDefault() : undefined; - // }); - // return dft as any; - // } + getDefaultFromShape(): DefaultFromShape { + let dft = {} as Record; + this._nodes.forEach((key) => { + const field = this.fields[key]; + dft[key] = 'default' in field ? field.getDefault() : undefined; + }); + return dft as any; + } protected _getDefault() { if ('default' in this.spec) { @@ -483,22 +463,91 @@ export default class ObjectSchema< } } +export default class ObjectSchema< + TShape extends ObjectShape, + TContext extends AnyObject = AnyObject, + TIn extends Maybe> = TypeOfShape | undefined +> extends BaseObjectSchema< + TShape, + TContext, + TIn, + AssertsShape | Optionals +> {} + export default interface ObjectSchema< - TType extends Maybe = AnyObject | undefined, - TOut extends Maybe> = SameShape | undefined -> extends BaseSchema { + TShape extends ObjectShape, + TContext extends AnyObject = AnyObject, + TIn extends Maybe> = TypeOfShape | undefined +> extends BaseObjectSchema< + TShape, + TContext, + TIn, + AssertsShape | Optionals + > { default>>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? ObjectSchema - : ObjectSchema, Defined>; + ? ObjectSchema + : ObjectSchema>; + + defined(msg?: MixedLocale['defined']): ObjectSchema; + required(msg?: MixedLocale['required']): ObjectSchema; + notRequired(): this; + nullable(isNullable?: true): ObjectSchema; + nullable( + isNullable: false, + ): ObjectSchema>; +} - defined(msg?: MixedLocale['defined']): ObjectSchema; - required(msg?: MixedLocale['required']): ObjectSchema; - notRequired(): ObjectSchema; +interface DefinedObjectSchema< + TShape extends ObjectShape, + TContext extends AnyObject, + TIn extends Maybe> +> extends BaseObjectSchema< + TShape, + TContext, + TIn, + AssertsShape | Extract + > { + default>>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined + ? DefinedObjectSchema + : DefinedObjectSchema>; + + defined(msg?: MixedLocale['defined']): this; + required( + msg?: MixedLocale['required'], + ): RequiredObjectSchema; + notRequired(): ObjectSchema; + nullable( + isNullable?: true, + ): DefinedObjectSchema; + nullable( + isNullable: false, + ): DefinedObjectSchema>; +} - nullable(isNullable?: true): ObjectSchema; +interface RequiredObjectSchema< + TShape extends ObjectShape, + TContext extends AnyObject, + TIn extends Maybe> +> extends BaseObjectSchema> { + default>>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined + ? RequiredObjectSchema + : RequiredObjectSchema>; + + defined( + msg?: MixedLocale['defined'], + ): DefinedObjectSchema; + required(msg?: MixedLocale['required']): this; + notRequired(): ObjectSchema; + nullable( + isNullable?: true, + ): RequiredObjectSchema; nullable( isNullable: false, - ): ObjectSchema, StrictNonNullable>; + ): RequiredObjectSchema>; } diff --git a/src/types.ts b/src/types.ts index e4899bad8..4ccfd69bc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -67,4 +67,4 @@ export type Maybe = T | null | undefined; export type Preserve = T extends U ? U : never; -export type PreserveOptionals = Preserve | Preserve; +export type Optionals = Extract; diff --git a/src/util/types.ts b/src/util/types.ts index 1dcb8e00b..58d893ad4 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -48,12 +48,3 @@ export type Asserts = TSchema['__outputType']; export type Thunk = T | (() => T); export type If = T extends undefined ? Y : N; - -type Preserve = T extends U ? U : never; - -export type PreserveOptionality = - | U - | Preserve - | Preserve; - -type f = PreserveOptionality; diff --git a/test/types.ts b/test/types.ts index 49a006d8e..d477262b2 100644 --- a/test/types.ts +++ b/test/types.ts @@ -124,8 +124,8 @@ string().required().nullable(); lazy: lazy(() => number().required()), }); - type F = StringSchema; - type f = F extends TypedSchema ? F['__inputType'] : false; + // type F = StringSchema; + // type f = F extends TypedSchema ? F['__inputType'] : false; // const f = obj.cast({}); // f!.number; From c2029a86e0c9123204a04a25053c351837b72889 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 30 Nov 2020 10:28:14 -0500 Subject: [PATCH 27/31] WIP --- src/boolean.ts | 73 ++++++++++++++++++++++++++++++++++++-------------- src/mixed.ts | 2 +- src/number.ts | 72 +++++++++++++++++++++++++++++++------------------ src/object.ts | 4 +-- test/types.ts | 18 +++++-------- 5 files changed, 108 insertions(+), 61 deletions(-) diff --git a/src/boolean.ts b/src/boolean.ts index 78053254c..b0dcdf625 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -1,14 +1,7 @@ import BaseSchema from './Base'; import type { MixedLocale } from './locale'; -import MixedSchema from './mixed'; -import type { Maybe } from './types'; -import type { - Defined, - Nullability, - Presence, - StrictNonNullable, - Unset, -} from './util/types'; +import type { AnyObject, Maybe } from './types'; +import type { Defined, StrictNonNullable } from './util/types'; export function create() { return new BooleanSchema(); @@ -16,8 +9,9 @@ export function create() { export default class BooleanSchema< TType extends Maybe = boolean | undefined, - TPresence extends Presence = Unset -> extends BaseSchema { + TContext extends AnyObject = AnyObject, + TOut extends TType = TType +> extends BaseSchema { constructor() { super({ type: 'boolean' }); @@ -41,21 +35,60 @@ export default class BooleanSchema< export default interface BooleanSchema< TType extends Maybe, - TPresence extends Presence -> extends BaseSchema { + TContext extends AnyObject = AnyObject, + TOut extends TType = TType +> extends BaseSchema { default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? BooleanSchema - : BooleanSchema, TPresence>; + ? BooleanSchema + : BooleanSchema>; - defined(msg?: MixedLocale['defined']): BooleanSchema; - required(msg?: MixedLocale['required']): BooleanSchema; - notRequired(): BooleanSchema; + defined(msg?: MixedLocale['defined']): BooleanSchema; + required(msg?: MixedLocale['required']): BooleanSchema; + notRequired(): BooleanSchema; + // optional(): BooleanSchema; + + nullable(isNullable?: true): BooleanSchema; + nullable(isNullable: false): BooleanSchema>; +} + +interface DefinedBooleanSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends BooleanSchema> { + default>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined + ? BooleanSchema + : BooleanSchema>; + + defined(msg?: MixedLocale['defined']): this; + required(msg?: MixedLocale['required']): RequiredBooleanSchema; + notRequired(): BooleanSchema; + // optional(): BooleanSchema; + + nullable(isNullable?: true): DefinedBooleanSchema; + nullable(isNullable: false): DefinedBooleanSchema>; +} + +interface RequiredBooleanSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends BooleanSchema> { + default>( + def: TNextDefault | (() => TNextDefault), + ): TNextDefault extends undefined + ? BooleanSchema + : BooleanSchema, TContext>; + + defined(msg?: MixedLocale['defined']): DefinedBooleanSchema; + required(msg?: MixedLocale['required']): this; + notRequired(): BooleanSchema; // optional(): BooleanSchema; - nullable(isNullable?: true): BooleanSchema; + nullable(isNullable?: true): RequiredBooleanSchema; nullable( isNullable: false, - ): BooleanSchema, TPresence>; + ): RequiredBooleanSchema, TContext>; } diff --git a/src/mixed.ts b/src/mixed.ts index f30cfebab..0d7aa1137 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -1,7 +1,7 @@ import type { MixedLocale } from './locale'; import { Maybe } from './types'; -import type { Defined, Presence, StrictNonNullable, Unset } from './util/types'; +import type { Defined, StrictNonNullable } from './util/types'; import BaseSchema from './Base'; export function create() { diff --git a/src/number.ts b/src/number.ts index 085f22984..a0140d03d 100644 --- a/src/number.ts +++ b/src/number.ts @@ -1,7 +1,7 @@ import MixedSchema from './mixed'; import { MixedLocale, number as locale } from './locale'; import isAbsent from './util/isAbsent'; -import type { Maybe } from './types'; +import type { AnyObject, Maybe } from './types'; import type Reference from './Reference'; import type { Defined, @@ -21,8 +21,9 @@ export function create() { export default class NumberSchema< TType extends Maybe = number | undefined, + TContext extends AnyObject = AnyObject, TOut extends TType = TType -> extends BaseSchema { +> extends BaseSchema { constructor() { super({ type: 'number' }); @@ -139,53 +140,72 @@ export default class NumberSchema< // // Number Interfaces // -interface DefinedNumberSchema> - extends NumberSchema> { +interface DefinedNumberSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends NumberSchema> { default>( def: Thunk, ): If< D, - DefinedNumberSchema, - DefinedNumberSchema> + DefinedNumberSchema, + DefinedNumberSchema, TContext> >; defined(msg?: MixedLocale['defined']): this; - required(msg?: MixedLocale['required']): RequiredNumberSchema; - notRequired(): NumberSchema; - nullable(isNullable?: true): RequiredNumberSchema; - nullable(isNullable: false): RequiredNumberSchema>; + required( + msg?: MixedLocale['required'], + ): RequiredNumberSchema; + notRequired(): NumberSchema; + nullable(isNullable?: true): RequiredNumberSchema; + nullable( + isNullable: false, + ): RequiredNumberSchema, TContext>; } -interface RequiredNumberSchema> - extends NumberSchema> { +interface RequiredNumberSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends NumberSchema> { default>( def: Thunk, ): If< D, - RequiredNumberSchema, - RequiredNumberSchema> + RequiredNumberSchema, + RequiredNumberSchema, TContext> >; - defined(msg?: MixedLocale['defined']): DefinedNumberSchema; - required(msg?: MixedLocale['required']): RequiredNumberSchema; - notRequired(): NumberSchema; - nullable(isNullable?: true): RequiredNumberSchema; - nullable(isNullable: false): RequiredNumberSchema>; + defined(msg?: MixedLocale['defined']): DefinedNumberSchema; + required( + msg?: MixedLocale['required'], + ): RequiredNumberSchema; + notRequired(): NumberSchema; + nullable(isNullable?: true): RequiredNumberSchema; + nullable( + isNullable: false, + ): RequiredNumberSchema, TContext>; } export default interface NumberSchema< TType extends Maybe = number | undefined, + TContext extends AnyObject = AnyObject, TOut extends TType = TType -> extends BaseSchema { +> extends BaseSchema { default>( def: Thunk, - ): If, NumberSchema>>; + ): If< + D, + NumberSchema, + NumberSchema, TContext> + >; - defined(msg?: MixedLocale['defined']): DefinedNumberSchema; + defined(msg?: MixedLocale['defined']): DefinedNumberSchema; - required(msg?: MixedLocale['required']): RequiredNumberSchema; - notRequired(): NumberSchema; + required( + msg?: MixedLocale['required'], + ): RequiredNumberSchema; + notRequired(): NumberSchema; - nullable(isNullable?: true): NumberSchema; - nullable(isNullable: false): NumberSchema>; + nullable(isNullable?: true): NumberSchema; + nullable(isNullable: false): NumberSchema, TContext>; } diff --git a/src/object.ts b/src/object.ts index 633cc93a9..f5e9d92a7 100644 --- a/src/object.ts +++ b/src/object.ts @@ -466,7 +466,7 @@ abstract class BaseObjectSchema< export default class ObjectSchema< TShape extends ObjectShape, TContext extends AnyObject = AnyObject, - TIn extends Maybe> = TypeOfShape | undefined + TIn extends Maybe> = TypeOfShape > extends BaseObjectSchema< TShape, TContext, @@ -477,7 +477,7 @@ export default class ObjectSchema< export default interface ObjectSchema< TShape extends ObjectShape, TContext extends AnyObject = AnyObject, - TIn extends Maybe> = TypeOfShape | undefined + TIn extends Maybe> = TypeOfShape > extends BaseObjectSchema< TShape, TContext, diff --git a/test/types.ts b/test/types.ts index d477262b2..241c4532f 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,23 +1,14 @@ /* eslint-disable no-unused-labels */ /* eslint-disable no-unused-expressions */ -import { - array, - string, - object, - mixed, - number, - ref, - lazy, - StringSchema, -} from '../src'; -import { +import { array, string, object, mixed, number, ref, lazy } from '../src'; +import type { AssertsShape, DefaultFromShape, ObjectSchemaOf, TypeOfShape, } from '../src/object'; -import { ResolveOutput, TypedSchema, Unset } from '../src/util/types'; +import { ResolveOutput, Unset } from '../src/util/types'; // let schema = object({ // str: string().nullable(), @@ -151,6 +142,8 @@ string().required().nullable(); // $ExpectType string type _i5 = AssertsShape['string']; + // type __ = typeof obj['fields']['lazy']['__outputType']; + // $ExpectType number type _i6 = AssertsShape['lazy']; @@ -279,6 +272,7 @@ ObjectSchemaOf: { // $ExpectType string | number | undefined lazy((v) => (typeof v === 'string' ? string() : number())).cast(3); } + // // CONCAT // From 91845dfd841e50d37070dd26d13942592c2e5db3 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 30 Nov 2020 15:34:31 -0500 Subject: [PATCH 28/31] WIP --- README.md | 104 +++++++++++++++-------------------- src/Base.ts | 62 ++++++++++++--------- src/Condition.ts | 4 +- src/Lazy.ts | 32 +++++++---- src/ValidationError.ts | 3 +- src/array.ts | 70 ++++++++--------------- src/boolean.ts | 78 +++++++++++++++++++------- src/date.ts | 91 ++++++++++++++++++++++-------- src/index.ts | 38 ++++++------- src/locale.ts | 6 +- src/mixed.ts | 47 ++++++++++------ src/number.ts | 72 ++++++++++++------------ src/object.ts | 53 ++++++++---------- src/string.ts | 66 ++++++++++++---------- src/types.ts | 8 +-- src/util/createValidation.ts | 24 ++++---- src/util/runTests.ts | 8 ++- src/util/sortFields.ts | 8 +-- src/util/types.ts | 36 ------------ test/types.ts | 89 ++++++++++++------------------ 20 files changed, 466 insertions(+), 433 deletions(-) diff --git a/README.md b/README.md index 255f23cb6..096306589 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane - - [Install](#install) - [Usage](#usage) - [Using a custom locale dictionary](#using-a-custom-locale-dictionary) @@ -1194,7 +1193,13 @@ const nameAndAge = person.omit('color']); nameAndAge.getDefault(); // => { age: 30, name: 'pat'} ``` -#### `object.from(fromKey: string, toKey: string, alias: boolean = false): Schema` +#### `object.getDefaultFromShape(): Record` + +Produces a default object value by walking the object shape and calling `default()` +on each field. This is the default behavior of `getDefault()` but allows for +building out an object skeleton regardless of the default(). + +#### `object.from(fromKey: string, toKey: string, alias: boolean = false): this` Transforms the specified key to a new key. If `alias` is `true` then the old key will be left. @@ -1321,15 +1326,25 @@ schema.format('YYYY-MM-DD').cast('It is 2012-05-25'); // => Fri May 25 2012 00:0 ## TypeScript Support -If you are using TypeScript installing the Yup typings is recommended: +`yup` comes with robust typescript support! However, because of how dynamic `yup` is +not everything can be statically typed safely, but for most cases it's "Good Enough". -```sh -npm install -D @types/yup +Not that `yup` schema actually produce _two_ different types: the result of casting an input, and the value after validation. +Why are these types different? Because a schema can produce a value via casting that +would not pass validation! + +```js +const schema = string().nullable().required(); + +schema.cast(null); // -> null +schema.validateSync(null); // ValidationError this is required! ``` -You can now infer a TypeScript type alias using the exported `InferType`. Given the following Yup schema: +By itself this seems weird, but has it uses when handling user input. To get a +TypeScript type that matches all possible `cast()` values, use `yup.TypeOf`. +To produce a type that matches a valid object for the schema use `yup.Asserts>` -```TypeScript +```ts import * as yup from 'yup'; const personSchema = yup.object({ @@ -1339,84 +1354,53 @@ const personSchema = yup.object({ // TypeScript. Both will have the same effect on the resulting type by // excluding `undefined`, but `required` will also disallow empty strings. .defined(), - nickName: yup - .string() - .defined() - .nullable(), + // defaults also affect the possible output type! + // schema with default values won't produce `undefined` values. Remember object schema + // have a default value built in. + nickName: yup.string().default('').nullable(), gender: yup .mixed() // Note `as const`: this types the array as `["male", "female", "other"]` // instead of `string[]`. .oneOf(['male', 'female', 'other'] as const) .defined(), - email: yup - .string() - .nullable() - .notRequired() - .email(), - birthDate: yup - .date() - .nullable() - .notRequired() - .min(new Date(1900, 0, 1)), -}).defined(); + email: yup.string().nullable().notRequired().email(), + birthDate: yup.date().nullable().notRequired().min(new Date(1900, 0, 1)), +}); ``` You can derive the TypeScript type as follows: -```TypeScript -type Person = yup.InferType; -``` +```ts +import type { Asserts, TypeOf } from 'yup' -Which is equivalent to the following TypeScript type alias: +type parsed: Typeof = personSchema.cast(json); -```TypeScript -type Person = { - firstName: string; - nickName: string | null; - gender: "male" | "female" | "other"; - email?: string | null | undefined; - birthDate?: Date | null | undefined; -} +const validated: Asserts = personSchema.validateSync(parsed); ``` -Making the following objects valid both for TypeScript and Yup validation: +You can also go the other direction, specifying an interface and ensuring that a schema would match it: -```TypeScript -const minimalPerson: Person = { - firstName: "Matt", - nickName: null, - gender: "male" -}; +```ts +import { string, object, number, SchemaOf } from 'yup'; -const fullPerson: Person = { - firstName: "Matt", - nickName: "The Hammer", - gender: "male", - email: "matt@the-hammer.com", - birthDate: new Date(1976, 9, 5) -}; -``` - -You can also go the other direction, specifying an interface and ensuring that a schema matches it: - -```TypeScript type Person = { firstName: string; -} +}; // ✔️ compiles -const goodPersonSchema: yup.ObjectSchema = yup.object({ - firstName: yup.string().defined() +const goodPersonSchema: SchemaOf = object({ + firstName: string().defined(), }).defined(); // ❌ errors: // "Type 'number | undefined' is not assignable to type 'string'." -const badPersonSchema: yup.ObjectSchema = yup.object({ - firstName: yup.number() +const badPersonSchema: SchemaOf = object({ + firstName: number(), }); ``` -### TypeScript setting +### TypeScript settings -For `yup.InferType` to work correctly with required and nullable types you have to set `strict: true` or `strictNullChecks: true` in your tsconfig.json. +For type utilties to work correctly with required and nullable types you have +to set `strict: true` or `strictNullChecks: true` in your tsconfig.json. diff --git a/src/Base.ts b/src/Base.ts index c4783bafc..9f063127d 100644 --- a/src/Base.ts +++ b/src/Base.ts @@ -25,15 +25,15 @@ import { } from './types'; import { ValidationError } from '.'; -import type { Asserts, Presence, ResolveOutput, Unset } from './util/types'; +import type { Asserts } from './util/types'; import ReferenceSet from './util/ReferenceSet'; import Reference from './Reference'; -const UNSET = 'unset' as const; +// const UNSET = 'unset' as const; export type SchemaSpec = { nullable: boolean; - presence: Presence; + presence: 'required' | 'defined' | 'optional'; default?: TDefault | (() => TDefault); abortEarly?: boolean; strip?: boolean; @@ -48,11 +48,15 @@ export type SchemaOptions = { spec?: SchemaSpec; }; -export type AnyBase = BaseSchema; +export type Schema = BaseSchema< + Type, + TContext, + TOut +>; -export interface CastOptions { +export interface CastOptions { parent?: any; - context?: {}; + context?: TContext; assert?: boolean; stripUnknown?: boolean; // XXX: should be private? @@ -136,7 +140,7 @@ export default abstract class BaseSchema< label: undefined, meta: undefined, nullable: false, - presence: UNSET, + presence: 'optional', ...options?.spec, }; } @@ -158,7 +162,7 @@ export default abstract class BaseSchema< // if the nested value is a schema we can skip cloning, since // they are already immutable - const next: AnyBase = Object.create(Object.getPrototypeOf(this)); + const next: Schema = Object.create(Object.getPrototypeOf(this)); // @ts-expect-error this is readonly next.type = this.type; @@ -196,6 +200,14 @@ export default abstract class BaseSchema< return next; } + // withContext(): BaseSchema< + // TCast, + // TContext, + // TOutput + // > { + // return this as any; + // } + withMutation(fn: (schema: this) => T): T { let before = this._mutate; this._mutate = true; @@ -204,13 +216,9 @@ export default abstract class BaseSchema< return result; } - // concat( - // schema: TOther, - // ): TOther extends BaseSchema - // ? BaseSchema - // : never; - concat(schema: AnyBase): AnyBase; - concat(schema: AnyBase): AnyBase { + concat(schema: this): this; + concat(schema: Schema): Schema; + concat(schema: Schema): Schema { if (!schema || schema === this) return this; if (schema.type !== this.type && this.type !== 'mixed') @@ -226,8 +234,8 @@ export default abstract class BaseSchema< // if (combined.spec.nullable === UNSET) // mergedSpec.nullable = base.spec.nullable; - if (combined.spec.presence === UNSET) - mergedSpec.presence = base.spec.presence; + // if (combined.spec.presence === UNSET) + // mergedSpec.presence = base.spec.presence; combined.spec = mergedSpec; @@ -292,7 +300,7 @@ export default abstract class BaseSchema< * @param {*=} options.parent * @param {*=} options.context */ - cast(value: any, options: CastOptions = {}): TCast { + cast(value: any, options: CastOptions = {}): TCast { let resolvedSchema = this.resolve({ value, ...options, @@ -324,7 +332,7 @@ export default abstract class BaseSchema< return result; } - protected _cast(rawValue: any, _options: CastOptions) { + protected _cast(rawValue: any, _options: CastOptions) { let value = rawValue === undefined ? rawValue @@ -342,7 +350,7 @@ export default abstract class BaseSchema< protected _validate( _value: any, - options: InternalOptions = {}, + options: InternalOptions = {}, cb: Callback, ): void { let { @@ -565,10 +573,14 @@ export default abstract class BaseSchema< * If an exclusive test is added to a schema with non-exclusive tests of the same name * the previous tests are removed and further tests of the same name will replace each other. */ - test(options: TestConfig): this; - test(test: TestFunction): this; - test(name: string, test: TestFunction): this; - test(name: string, message: Message, test: TestFunction): this; + test(options: TestConfig): this; + test(test: TestFunction): this; + test(name: string, test: TestFunction): this; + test( + name: string, + message: Message, + test: TestFunction, + ): this; test(...args: any[]) { let opts: TestConfig; @@ -760,7 +772,7 @@ export default interface BaseSchema { is: BaseSchema['oneOf']; not: BaseSchema['notOneOf']; nope: BaseSchema['notOneOf']; - optional: BaseSchema['notRequired']; + optional(): any; } // @ts-expect-error diff --git a/src/Condition.ts b/src/Condition.ts index 0510fb70f..d4148cd56 100644 --- a/src/Condition.ts +++ b/src/Condition.ts @@ -20,10 +20,10 @@ export type ConditionOptions = | ConditionBuilder | ConditionConfig; -export type ResolveOptions = { +export type ResolveOptions = { value?: any; parent?: any; - context?: any; + context?: TContext; }; class Condition { diff --git a/src/Lazy.ts b/src/Lazy.ts index cfea80b2a..beb6a947f 100644 --- a/src/Lazy.ts +++ b/src/Lazy.ts @@ -2,15 +2,17 @@ import isSchema from './util/isSchema'; import type { Callback, ValidateOptions } from './types'; import type { ResolveOptions } from './Condition'; -import type { AnyBase, CastOptions } from './Base'; +import type { Schema, CastOptions } from './Base'; import { TypedSchema, TypeOf } from './util/types'; -export type LazyBuilder = ( +type ContextOf = T extends Schema ? C : never; + +export type LazyBuilder = ( value: any, options: ResolveOptions, ) => T; -export function create(builder: LazyBuilder) { +export function create(builder: LazyBuilder) { return new Lazy(builder); } @@ -22,7 +24,7 @@ export type LazyType = LazyReturnValue extends TypedSchema ? TypeOf> : never; -class Lazy { +class Lazy> implements TypedSchema { type = 'lazy' as const; __isYupSchema__ = true; @@ -32,7 +34,10 @@ class Lazy { constructor(private builder: LazyBuilder) {} - private _resolve = (value: any, options: ResolveOptions = {}): T => { + private _resolve = ( + value: any, + options: ResolveOptions = {}, + ): T => { let schema = this.builder(value, options); if (!isSchema(schema)) @@ -41,10 +46,10 @@ class Lazy { return schema.resolve(options); }; - resolve(options: ResolveOptions) { + resolve(options: ResolveOptions) { return this._resolve(options.value, options); } - cast(value: any, options?: CastOptions): T['__inputType'] { + cast(value: any, options?: CastOptions): T['__inputType'] { return this._resolve(value, options).cast(value, options); } @@ -57,13 +62,20 @@ class Lazy { return this._resolve(value, options).validate(value, options, maybeCb); } - validateSync(value: any, options?: ValidateOptions): T['__outputType'] { + validateSync( + value: any, + options?: ValidateOptions, + ): T['__outputType'] { return this._resolve(value, options).validateSync(value, options); } - validateAt(path: string, value: any, options?: ValidateOptions) { + validateAt(path: string, value: any, options?: ValidateOptions) { return this._resolve(value, options).validateAt(path, value, options); } - validateSyncAt(path: string, value: any, options?: ValidateOptions) { + validateSyncAt( + path: string, + value: any, + options?: ValidateOptions, + ) { return this._resolve(value, options).validateSyncAt(path, value, options); } describe() { diff --git a/src/ValidationError.ts b/src/ValidationError.ts index a715b761e..69f0cdca5 100644 --- a/src/ValidationError.ts +++ b/src/ValidationError.ts @@ -19,7 +19,8 @@ export default class ValidationError extends Error { message: string | ((params: Params) => string) | unknown, params: Params, ) { - params.path = params.label || params.path || 'this'; + const path = params.label || params.path || 'this'; + if (path !== params.path) params = { ...params, path }; if (typeof message === 'string') return message.replace(strReg, (_, key) => printValue(params[key])); diff --git a/src/array.ts b/src/array.ts index 9c5f7fe5b..7bf37fc5f 100644 --- a/src/array.ts +++ b/src/array.ts @@ -1,7 +1,7 @@ import isAbsent from './util/isAbsent'; import isSchema from './util/isSchema'; import printValue from './util/printValue'; -import { array, array as locale, MixedLocale } from './locale'; +import { array as locale, MixedLocale } from './locale'; import runTests, { RunTest } from './util/runTests'; import type { AnyObject, @@ -14,32 +14,24 @@ import type { } from './types'; import ValidationError from './ValidationError'; import type Reference from './Reference'; -import { - Asserts, - Defined, - If, - StrictNonNullable, - Thunk, - TypeOf, -} from './util/types'; +import { Asserts, Defined, If, Thunk, TypeOf } from './util/types'; import BaseSchema, { - AnyBase, + Schema, SchemaInnerTypeDescription, SchemaSpec, } from './Base'; -import { string } from '.'; type RejectorFn = (value: any, index: number, array: any[]) => boolean; export function create< C extends AnyObject = AnyObject, - T extends AnyBase = AnyBase + T extends Schema = Schema >(type?: T) { return new ArraySchema(type); } class BaseArraySchema< - T extends AnyBase, + T extends Schema, C extends AnyObject, TIn extends Maybe[]>, TOut extends Maybe[]> @@ -75,7 +67,7 @@ class BaseArraySchema< return this.innerType; } - protected _cast(_value: any, _opts: InternalOptions) { + protected _cast(_value: any, _opts: InternalOptions) { const value = super._cast(_value, _opts); //should ignore nulls here @@ -99,7 +91,7 @@ class BaseArraySchema< protected _validate( _value: any, - options: InternalOptions = {}, + options: InternalOptions = {}, callback: Callback, ) { let errors = [] as ValidationError[]; @@ -172,21 +164,9 @@ class BaseArraySchema< return next; } - // concat>( - // schema: TOther, - // ): TOther extends ArraySchema - // ? // hoooo boy - // ArraySchema< - // T, - // | (TypeOf & TypeOf)[] - // | PreserveOptionals - // | PreserveOptionals, - // | (Asserts & Asserts)[] - // | PreserveOptionals - // | PreserveOptionals, - // P extends Unset ? TPresence : P - // > - // : never; + concat>( + schema: TOther, + ): TOther; concat(schema: any): any; concat(schema: any): any { let next = super.concat(schema) as this; @@ -201,7 +181,7 @@ class BaseArraySchema< return next; } - of(schema: TInner): ArraySchema { + of(schema: TInner): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -288,7 +268,7 @@ class BaseArraySchema< } export default class ArraySchema< - T extends AnyBase, + T extends Schema, TContext extends AnyObject = AnyObject, TIn extends Maybe[]> = TypeOf[] | undefined > extends BaseArraySchema[] | Optionals> {} @@ -297,8 +277,8 @@ export default class ArraySchema< // Interfaces // -interface DefinedArraySchema< - T extends AnyBase, +export interface DefinedArraySchema< + T extends Schema, TContext extends AnyObject, TIn extends Maybe[]> > extends BaseArraySchema< @@ -319,15 +299,16 @@ interface DefinedArraySchema< required( msg?: MixedLocale['required'], ): RequiredArraySchema; + optional(): ArraySchema; notRequired(): ArraySchema; nullable(isNullable?: true): DefinedArraySchema; nullable( isNullable: false, - ): RequiredArraySchema>; + ): RequiredArraySchema>; } -interface RequiredArraySchema< - T extends AnyBase, +export interface RequiredArraySchema< + T extends Schema, TContext extends AnyObject, TIn extends Maybe[]> > extends BaseArraySchema[]> { @@ -341,15 +322,16 @@ interface RequiredArraySchema< defined(msg?: MixedLocale['defined']): DefinedArraySchema; required(msg?: MixedLocale['required']): this; + optional(): ArraySchema; notRequired(): ArraySchema; nullable(isNullable?: true): RequiredArraySchema; nullable( isNullable: false, - ): RequiredArraySchema>; + ): RequiredArraySchema>; } export default interface ArraySchema< - T extends AnyBase, + T extends Schema, TContext extends AnyObject = AnyObject, TIn extends Maybe[]> = TypeOf[] | undefined > extends BaseArraySchema[] | Optionals> { @@ -365,14 +347,10 @@ export default interface ArraySchema< required( msg?: MixedLocale['required'], ): RequiredArraySchema; + + optional(): this; notRequired(): this; nullable(isNullable?: true): ArraySchema; - nullable(isNullable: false): ArraySchema>; + nullable(isNullable: false): ArraySchema>; } - -let s = create(string().required()).required().nullable().notRequired(); - -let c = s.cast(''); - -let f = s.validateSync('')?.map; diff --git a/src/boolean.ts b/src/boolean.ts index b0dcdf625..e4e8ec45e 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -1,7 +1,9 @@ import BaseSchema from './Base'; import type { MixedLocale } from './locale'; -import type { AnyObject, Maybe } from './types'; -import type { Defined, StrictNonNullable } from './util/types'; +import type { AnyObject, Maybe, Optionals } from './types'; +import type { Defined } from './util/types'; +import { boolean as locale } from './locale'; +import isAbsent from './util/isAbsent'; export function create() { return new BooleanSchema(); @@ -31,6 +33,34 @@ export default class BooleanSchema< return typeof v === 'boolean'; } + + isTrue( + message = locale.isValue, + ): BooleanSchema> { + return this.test({ + message, + name: 'is-value', + exclusive: true, + params: { value: 'true' }, + test(value) { + return isAbsent(value) || value === true; + }, + }) as any; + } + + isFalse( + message = locale.isValue, + ): BooleanSchema> { + return this.test({ + message, + name: 'is-value', + exclusive: true, + params: { value: 'false' }, + test(value) { + return isAbsent(value) || value === false; + }, + }) as any; + } } export default interface BooleanSchema< @@ -38,41 +68,47 @@ export default interface BooleanSchema< TContext extends AnyObject = AnyObject, TOut extends TType = TType > extends BaseSchema { + concat>(schema: TOther): TOther; + default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? BooleanSchema - : BooleanSchema>; + ? BooleanSchema + : BooleanSchema, TContext>; - defined(msg?: MixedLocale['defined']): BooleanSchema; - required(msg?: MixedLocale['required']): BooleanSchema; + defined(msg?: MixedLocale['defined']): DefinedBooleanSchema; + required( + msg?: MixedLocale['required'], + ): RequiredBooleanSchema; + optional(): BooleanSchema; notRequired(): BooleanSchema; - // optional(): BooleanSchema; nullable(isNullable?: true): BooleanSchema; - nullable(isNullable: false): BooleanSchema>; + nullable(isNullable: false): BooleanSchema>; } -interface DefinedBooleanSchema< +export interface DefinedBooleanSchema< TType extends Maybe, TContext extends AnyObject = AnyObject > extends BooleanSchema> { default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? BooleanSchema - : BooleanSchema>; + ? BooleanSchema + : BooleanSchema, TContext>; - defined(msg?: MixedLocale['defined']): this; - required(msg?: MixedLocale['required']): RequiredBooleanSchema; - notRequired(): BooleanSchema; - // optional(): BooleanSchema; + defined(msg?: MixedLocale['defined']): DefinedBooleanSchema; + required( + msg?: MixedLocale['required'], + ): RequiredBooleanSchema; + optional(): BooleanSchema; + notRequired(): BooleanSchema; nullable(isNullable?: true): DefinedBooleanSchema; - nullable(isNullable: false): DefinedBooleanSchema>; + nullable(isNullable: false): DefinedBooleanSchema>; } -interface RequiredBooleanSchema< +export interface RequiredBooleanSchema< TType extends Maybe, TContext extends AnyObject = AnyObject > extends BooleanSchema> { @@ -83,12 +119,14 @@ interface RequiredBooleanSchema< : BooleanSchema, TContext>; defined(msg?: MixedLocale['defined']): DefinedBooleanSchema; - required(msg?: MixedLocale['required']): this; + required( + msg?: MixedLocale['required'], + ): RequiredBooleanSchema; + optional(): BooleanSchema; notRequired(): BooleanSchema; - // optional(): BooleanSchema; nullable(isNullable?: true): RequiredBooleanSchema; nullable( isNullable: false, - ): RequiredBooleanSchema, TContext>; + ): RequiredBooleanSchema, TContext>; } diff --git a/src/date.ts b/src/date.ts index 6ef951a4b..4fd8a4374 100644 --- a/src/date.ts +++ b/src/date.ts @@ -4,14 +4,8 @@ import isoParse from './util/isodate'; import { date as locale, MixedLocale } from './locale'; import isAbsent from './util/isAbsent'; import Ref from './Reference'; -import type { Maybe } from './types'; -import type { - Defined, - Nullability, - Presence, - StrictNonNullable, - Unset, -} from './util/types'; +import type { AnyObject, Maybe } from './types'; +import type { Defined, If, Thunk } from './util/types'; import BaseSchema from './Base'; let invalidDate = new Date(''); @@ -25,8 +19,9 @@ export function create() { export default class DateSchema< TType extends Maybe = Date | undefined, - TPresence extends Presence = Unset -> extends BaseSchema { + TContext extends AnyObject = AnyObject, + TOut extends TType = TType +> extends BaseSchema { constructor() { super({ type: 'date' }); @@ -96,19 +91,69 @@ export default class DateSchema< export default interface DateSchema< TType extends Maybe, - TPresence extends Presence -> extends BaseSchema { - default>( - def: TNextDefault | (() => TNextDefault), - ): TNextDefault extends undefined - ? DateSchema - : DateSchema, TPresence>; - - defined(msg?: MixedLocale['defined']): DateSchema; + TContext extends AnyObject = AnyObject, + TOut extends TType = TType +> extends BaseSchema { + concat>(schema: TOther): TOther; + + default>( + def: Thunk, + ): If< + D, + DateSchema, + DateSchema, TContext> + >; + + defined(msg?: MixedLocale['defined']): DefinedDateSchema; + + required(msg?: MixedLocale['required']): RequiredDateSchema; + optional(): DateSchema; + notRequired(): DateSchema; + + nullable(isNullable?: true): DateSchema; + nullable(isNullable: false): DateSchema, TContext>; +} - required(msg?: MixedLocale['required']): DateSchema; - notRequired(): DateSchema; +export interface DefinedDateSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends DateSchema> { + default>( + def: Thunk, + ): If< + D, + DefinedDateSchema, + DefinedDateSchema, TContext> + >; + + defined(msg?: MixedLocale['defined']): this; + required(msg?: MixedLocale['required']): RequiredDateSchema; + optional(): DateSchema; + notRequired(): DateSchema; + nullable(isNullable?: true): RequiredDateSchema; + nullable( + isNullable: false, + ): RequiredDateSchema, TContext>; +} - nullable(isNullable?: true): DateSchema; - nullable(isNullable: false): DateSchema, TPresence>; +export interface RequiredDateSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends DateSchema> { + default>( + def: Thunk, + ): If< + D, + RequiredDateSchema, + RequiredDateSchema, TContext> + >; + + defined(msg?: MixedLocale['defined']): DefinedDateSchema; + required(msg?: MixedLocale['required']): RequiredDateSchema; + optional(): DateSchema; + notRequired(): DateSchema; + nullable(isNullable?: true): RequiredDateSchema; + nullable( + isNullable: false, + ): RequiredDateSchema, TContext>; } diff --git a/src/index.ts b/src/index.ts index 551c5de75..4fb36a942 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import BoolSchema, { create as boolCreate } from './boolean'; import StringSchema, { create as stringCreate } from './string'; import NumberSchema, { create as numberCreate } from './number'; import DateSchema, { create as dateCreate } from './date'; -import ObjectSchema, { create as objectCreate, ObjectSchemaOf } from './object'; +import ObjectSchema, { AnyObject, create as objectCreate } from './object'; import ArraySchema, { create as arrayCreate } from './array'; import { create as refCreate } from './Reference'; import { create as lazyCreate } from './Lazy'; @@ -11,14 +11,9 @@ import ValidationError from './ValidationError'; import reach from './util/reach'; import isSchema from './util/isSchema'; import setLocale from './setLocale'; -import type { AnyBase as Schema } from './Base'; -import type { - TypeOf, - Asserts, - Nullability, - Presence, - Unset, -} from './util/types'; +import type { Schema } from './Base'; +import type { TypeOf, Asserts } from './util/types'; +import { Maybe } from './types'; function addMethod( schemaType: (...aarg: any[]) => T, @@ -42,16 +37,13 @@ function addMethod(schemaType: any, name: string, fn: any) { schemaType.prototype[name] = fn; } -export type { - TypeOf, - Asserts, - Nullability, - Presence, - Unset, - Asserts as InferType, - ObjectSchemaOf, - Schema, -}; +type SchemaOf = T extends AnyObject + ? ObjectSchema<{ [k in keyof T]: SchemaOf }> + : T extends Array + ? ArraySchema> + : Schema, AnyObject, T>; + +export type { SchemaOf, TypeOf, Asserts, Asserts as InferType, Schema }; export { mixedCreate as mixed, @@ -80,3 +72,11 @@ export { ObjectSchema, ArraySchema, }; + +export type { + CreateErrorOptions, + TestContext, + TestFunction, + TestOptions, + TestConfig, +} from './util/createValidation'; diff --git a/src/locale.ts b/src/locale.ts index 7f02ec6a0..16d232cbd 100644 --- a/src/locale.ts +++ b/src/locale.ts @@ -48,6 +48,10 @@ export interface ArrayLocale { max?: Message<{ max: number }>; } +export interface BooleanLocale { + isValue?: Message; +} + export let mixed: Required = { default: '${path} is invalid', required: '${path} is a required field', @@ -99,7 +103,7 @@ export let date: Required = { max: '${path} field must be at earlier than ${max}', }; -export let boolean = { +export let boolean: BooleanLocale = { isValue: '${path} field must be ${value}', }; diff --git a/src/mixed.ts b/src/mixed.ts index 0d7aa1137..5bcec77a5 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -1,31 +1,46 @@ import type { MixedLocale } from './locale'; -import { Maybe } from './types'; -import type { Defined, StrictNonNullable } from './util/types'; -import BaseSchema from './Base'; +import { AnyObject, Maybe, Optionals } from './types'; +import type { Defined } from './util/types'; +import BaseSchema, { Schema } from './Base'; export function create() { - return new MixedSchema(); + return new MixedSchema(); } -export default class MixedSchema extends BaseSchema< - TType, - TType -> {} +export default class MixedSchema< + TType = any, + TContext = AnyObject, + TOut = TType +> extends BaseSchema {} -export default interface MixedSchema { +export default interface MixedSchema< + TType = any, + TContext = AnyObject, + TOut = TType +> { default>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined - ? MixedSchema - : MixedSchema, Defined>; + ? MixedSchema + : MixedSchema, TContext>; - defined(msg?: MixedLocale['defined']): MixedSchema>; + concat(schema: this): this; + concat( + schema: BaseSchema, + ): MixedSchema< + TType | IT, + TContext & IC, + NonNullable | IO | Optionals + >; + defined( + msg?: MixedLocale['defined'], + ): MixedSchema>; required( msg?: MixedLocale['required'], - ): MixedSchema>; - notRequired(): MixedSchema; + ): MixedSchema>; + notRequired(): MixedSchema; - nullable(isNullable?: true): MixedSchema; - nullable(isNullable: false): MixedSchema>; + nullable(isNullable?: true): MixedSchema; + nullable(isNullable: false): MixedSchema, TContext>; } diff --git a/src/number.ts b/src/number.ts index a0140d03d..8d2496892 100644 --- a/src/number.ts +++ b/src/number.ts @@ -1,16 +1,8 @@ -import MixedSchema from './mixed'; import { MixedLocale, number as locale } from './locale'; import isAbsent from './util/isAbsent'; import type { AnyObject, Maybe } from './types'; import type Reference from './Reference'; -import type { - Defined, - If, - Thunk, - Presence, - StrictNonNullable, - Unset, -} from './util/types'; +import type { Defined, If, Thunk } from './util/types'; import BaseSchema from './Base'; let isNaN = (value: Maybe) => value != +value!; @@ -140,72 +132,78 @@ export default class NumberSchema< // // Number Interfaces // -interface DefinedNumberSchema< - TType extends Maybe, - TContext extends AnyObject = AnyObject -> extends NumberSchema> { + +export default interface NumberSchema< + TType extends Maybe = number | undefined, + TContext extends AnyObject = AnyObject, + TOut extends TType = TType +> extends BaseSchema { + concat>(schema: TOther): TOther; + default>( def: Thunk, ): If< D, - DefinedNumberSchema, - DefinedNumberSchema, TContext> + NumberSchema, + NumberSchema, TContext> >; - defined(msg?: MixedLocale['defined']): this; + defined(msg?: MixedLocale['defined']): DefinedNumberSchema; + required( msg?: MixedLocale['required'], ): RequiredNumberSchema; + optional(): NumberSchema; notRequired(): NumberSchema; - nullable(isNullable?: true): RequiredNumberSchema; - nullable( - isNullable: false, - ): RequiredNumberSchema, TContext>; + + nullable(isNullable?: true): NumberSchema; + nullable(isNullable: false): NumberSchema, TContext>; } -interface RequiredNumberSchema< +export interface DefinedNumberSchema< TType extends Maybe, TContext extends AnyObject = AnyObject -> extends NumberSchema> { +> extends NumberSchema> { default>( def: Thunk, ): If< D, - RequiredNumberSchema, - RequiredNumberSchema, TContext> + DefinedNumberSchema, + DefinedNumberSchema, TContext> >; - defined(msg?: MixedLocale['defined']): DefinedNumberSchema; + defined(msg?: MixedLocale['defined']): this; required( msg?: MixedLocale['required'], ): RequiredNumberSchema; + optional(): NumberSchema; notRequired(): NumberSchema; nullable(isNullable?: true): RequiredNumberSchema; nullable( isNullable: false, - ): RequiredNumberSchema, TContext>; + ): RequiredNumberSchema, TContext>; } -export default interface NumberSchema< - TType extends Maybe = number | undefined, - TContext extends AnyObject = AnyObject, - TOut extends TType = TType -> extends BaseSchema { +export interface RequiredNumberSchema< + TType extends Maybe, + TContext extends AnyObject = AnyObject +> extends NumberSchema> { default>( def: Thunk, ): If< D, - NumberSchema, - NumberSchema, TContext> + RequiredNumberSchema, + RequiredNumberSchema, TContext> >; defined(msg?: MixedLocale['defined']): DefinedNumberSchema; - required( msg?: MixedLocale['required'], ): RequiredNumberSchema; + optional(): NumberSchema; notRequired(): NumberSchema; - - nullable(isNullable?: true): NumberSchema; - nullable(isNullable: false): NumberSchema, TContext>; + nullable(isNullable?: true): RequiredNumberSchema; + nullable( + isNullable: false, + ): RequiredNumberSchema, TContext>; } diff --git a/src/object.ts b/src/object.ts index f5e9d92a7..e58c3ea9b 100644 --- a/src/object.ts +++ b/src/object.ts @@ -11,33 +11,18 @@ import sortByKeyOrder from './util/sortByKeyOrder'; import runTests from './util/runTests'; import { InternalOptions, Callback, Maybe, Optionals, Preserve } from './types'; import ValidationError from './ValidationError'; -import type { - Presence, - Unset, - TypedSchema, - Defined, - StrictNonNullable, - InferPresence, -} from './util/types'; +import type { TypedSchema, Defined } from './util/types'; import type Reference from './Reference'; -import Lazy, { LazyType } from './Lazy'; +import Lazy from './Lazy'; import BaseSchema, { - AnyBase, + Schema, SchemaObjectDescription, SchemaSpec, } from './Base'; export type AnyObject = Record; -type ShapeOf = { - [k in keyof T]: BaseSchema, any, Maybe>; -}; - -export type ObjectSchemaOf> = ObjectSchema< - ShapeOf ->; - -export type ObjectShape = Record>; +export type ObjectShape = Record>; export function create(spec?: TShape) { return new ObjectSchema(spec); @@ -53,7 +38,7 @@ export type DefaultFromShape = { : undefined; }; -type AssignShape = { +type Assign = { [P in keyof T]: P extends keyof U ? U[P] : T[P]; } & U; @@ -130,7 +115,7 @@ abstract class BaseObjectSchema< return isObject(value) || typeof value === 'function'; } - protected _cast(_value: any, options: InternalOptions = {}) { + protected _cast(_value: any, options: InternalOptions = {}) { let value = super._cast(_value, options); //should ignore nulls here @@ -202,7 +187,7 @@ abstract class BaseObjectSchema< protected _validate( _value: any, - opts: InternalOptions = {}, + opts: InternalOptions = {}, callback: Callback, ) { let errors = [] as ValidationError[]; @@ -295,14 +280,14 @@ abstract class BaseObjectSchema< concat>( schema: TOther, - ): TOther extends ObjectSchema + ): TOther extends ObjectSchema ? ObjectSchema< TShape & S, - TypeOfShape | Optionals, - AssertsShape | Optionals + TContext & C, + TypeOfShape | Optionals > : never; - concat(schema: AnyBase): AnyBase; + concat(schema: this): this; concat(schema: any): any { let next = super.concat(schema) as any; @@ -346,7 +331,11 @@ abstract class BaseObjectSchema< shape( additions: TNextShape, excludes: [string, string][] = [], - ): ObjectSchema> { + ): ObjectSchema< + Assign, + TContext, + TypeOfShape> | Optionals + > { let next = this.clone(); let fields = Object.assign(next.fields, additions); @@ -492,14 +481,15 @@ export default interface ObjectSchema< defined(msg?: MixedLocale['defined']): ObjectSchema; required(msg?: MixedLocale['required']): ObjectSchema; + optional(): this; notRequired(): this; nullable(isNullable?: true): ObjectSchema; nullable( isNullable: false, - ): ObjectSchema>; + ): ObjectSchema>; } -interface DefinedObjectSchema< +export interface DefinedObjectSchema< TShape extends ObjectShape, TContext extends AnyObject, TIn extends Maybe> @@ -519,6 +509,8 @@ interface DefinedObjectSchema< required( msg?: MixedLocale['required'], ): RequiredObjectSchema; + + optional(): ObjectSchema; notRequired(): ObjectSchema; nullable( isNullable?: true, @@ -528,7 +520,7 @@ interface DefinedObjectSchema< ): DefinedObjectSchema>; } -interface RequiredObjectSchema< +export interface RequiredObjectSchema< TShape extends ObjectShape, TContext extends AnyObject, TIn extends Maybe> @@ -543,6 +535,7 @@ interface RequiredObjectSchema< msg?: MixedLocale['defined'], ): DefinedObjectSchema; required(msg?: MixedLocale['required']): this; + optional(): ObjectSchema; notRequired(): ObjectSchema; nullable( isNullable?: true, diff --git a/src/string.ts b/src/string.ts index b6cf2f7e1..9b8c9bcef 100644 --- a/src/string.ts +++ b/src/string.ts @@ -1,15 +1,8 @@ -import { MixedLocale, string as locale, string } from './locale'; +import { MixedLocale, string as locale } from './locale'; import isAbsent from './util/isAbsent'; import type Reference from './Reference'; import type { Message, Maybe, AnyObject } from './types'; -import type { - Defined, - If, - Presence, - StrictNonNullable, - Thunk, - Unset, -} from './util/types'; +import type { Defined, If, Thunk } from './util/types'; import BaseSchema from './Base'; // eslint-disable-next-line @@ -30,8 +23,8 @@ export type MatchOptions = { let objStringTag = {}.toString(); -export function create() { - return new StringSchema(); +export function create() { + return new StringSchema(); } export default class StringSchema< @@ -207,7 +200,7 @@ export default class StringSchema< // // String Interfaces // -interface DefinedStringSchema< +export interface DefinedStringSchema< TType extends Maybe, TContext extends AnyObject = AnyObject > extends StringSchema> { @@ -215,18 +208,23 @@ interface DefinedStringSchema< def: Thunk, ): If< D, - DefinedStringSchema, - DefinedStringSchema> + DefinedStringSchema, + DefinedStringSchema, TContext> >; defined(msg?: MixedLocale['defined']): this; - required(msg?: MixedLocale['required']): RequiredStringSchema; - notRequired(): StringSchema; - nullable(isNullable?: true): RequiredStringSchema; - nullable(isNullable: false): RequiredStringSchema>; + required( + msg?: MixedLocale['required'], + ): RequiredStringSchema; + optional(): StringSchema; + notRequired(): StringSchema; + nullable(isNullable?: true): RequiredStringSchema; + nullable( + isNullable: false, + ): RequiredStringSchema, TContext>; } -interface RequiredStringSchema< +export interface RequiredStringSchema< TType extends Maybe, TContext extends AnyObject = AnyObject > extends StringSchema> { @@ -234,15 +232,20 @@ interface RequiredStringSchema< def: Thunk, ): If< D, - RequiredStringSchema, - RequiredStringSchema> + RequiredStringSchema, + RequiredStringSchema, TContext> >; - defined(msg?: MixedLocale['defined']): DefinedStringSchema; - required(msg?: MixedLocale['required']): RequiredStringSchema; - notRequired(): StringSchema; - nullable(isNullable?: true): RequiredStringSchema; - nullable(isNullable: false): RequiredStringSchema>; + defined(msg?: MixedLocale['defined']): DefinedStringSchema; + required( + msg?: MixedLocale['required'], + ): RequiredStringSchema; + optional(): StringSchema; + notRequired(): StringSchema; + nullable(isNullable?: true): RequiredStringSchema; + nullable( + isNullable: false, + ): RequiredStringSchema, TContext>; } export default interface StringSchema< @@ -250,6 +253,8 @@ export default interface StringSchema< TContext extends AnyObject = AnyObject, TOut extends TType = TType > extends BaseSchema { + concat>(schema: TOther): TOther; + default>( def: Thunk, ): If< @@ -262,8 +267,13 @@ export default interface StringSchema< required( msg?: MixedLocale['required'], ): RequiredStringSchema; - notRequired(): StringSchema; + optional(): StringSchema; + notRequired(): StringSchema; nullable(isNullable?: true): StringSchema; - nullable(isNullable: false): StringSchema, TContext>; + nullable(isNullable: false): StringSchema, TContext>; + withContext(): StringSchema< + Exclude, + TNextContext + >; } diff --git a/src/types.ts b/src/types.ts index 4ccfd69bc..acca5c15a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,13 +1,13 @@ -import type { AnyBase } from './Base'; +import type { Schema } from './Base'; import type Lazy from './Lazy'; export type AnyObject = Record; -export type SchemaLike = AnyBase | Lazy; +export type SchemaLike = Schema | Lazy; export type Callback = (err: Error | null, value?: T) => void; -export type TransformFunction = ( +export type TransformFunction = ( this: T, value: any, originalValue: any, @@ -43,7 +43,7 @@ export interface InternalOptions parent?: any; path?: string; sync?: boolean; - from?: { schema: AnyBase; value: any }[]; + from?: { schema: Schema; value: any }[]; } export interface MessageParams { diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts index 5cceb7ea0..f7dc3dff7 100644 --- a/src/util/createValidation.ts +++ b/src/util/createValidation.ts @@ -9,31 +9,31 @@ import { ExtraParams, } from '../types'; import Reference from '../Reference'; -import type { AnyBase } from '../Base'; +import type { Schema } from '../Base'; export type CreateErrorOptions = { path?: string; - message?: string; - params?: object; + message?: Message; + params?: ExtraParams; type?: string; }; -export type TestContext = { +export type TestContext = { path: string; - options: ValidateOptions; + options: ValidateOptions; parent: any; schema: any; // TODO: Schema; resolve: (value: T | Reference) => T; createError: (params?: CreateErrorOptions) => ValidationError; }; -export type TestFunction = ( - this: TestContext, +export type TestFunction = ( + this: TestContext, value: T, - context: TestContext, + context: TestContext, ) => boolean | ValidationError | Promise; -export type TestOptions = { +export type TestOptions = { value: any; path?: string; label?: string; @@ -43,10 +43,10 @@ export type TestOptions = { sync?: boolean; }; -export type TestConfig = { +export type TestConfig = { name?: string; message?: Message; - test: TestFunction; + test: TestFunction; params?: ExtraParams; exclusive?: boolean; }; @@ -61,7 +61,7 @@ export default function createValidation(config: { params?: ExtraParams; message?: Message; }) { - function validate( + function validate( { value, path = '', diff --git a/src/util/runTests.ts b/src/util/runTests.ts index 876a617a1..b065a46fa 100644 --- a/src/util/runTests.ts +++ b/src/util/runTests.ts @@ -29,12 +29,14 @@ export default function runTests(options: TestRunOptions, cb: Callback): void { let callback = once(cb); let count = tests.length; - - if (!count) return callback(null, value); - const nestedErrors = [] as ValidationError[]; errors = errors ? errors : []; + if (!count) + return errors.length + ? callback(new ValidationError(errors, value, path)) + : callback(null, value); + for (let i = 0; i < tests.length; i++) { const test = tests[i]; diff --git a/src/util/sortFields.ts b/src/util/sortFields.ts index 9f2d7efa5..e0af5c379 100644 --- a/src/util/sortFields.ts +++ b/src/util/sortFields.ts @@ -5,12 +5,10 @@ import { split } from 'property-expr'; import Ref from '../Reference'; import isSchema from './isSchema'; -import type MixedSchema from '../mixed'; -import type Reference from '../Reference'; -import type Lazy from '../Lazy'; +import { ObjectShape } from '../object'; export default function sortFields( - fields: Record>, + fields: ObjectShape, excludes: readonly string[] = [], ) { let edges = [] as Array<[string, string]>; @@ -31,7 +29,7 @@ export default function sortFields( if (!~nodes.indexOf(key)) nodes.push(key); if (Ref.isRef(value) && value.isSibling) addNode(value.path, key); - else if (isSchema(value) && value.deps) + else if (isSchema(value) && 'deps' in value) value.deps.forEach((path) => addNode(path, key)); } diff --git a/src/util/types.ts b/src/util/types.ts index 58d893ad4..6637f1508 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -1,41 +1,5 @@ -import { Maybe } from '../types'; - -export type Unset = 'unset'; -export type Presence = 'required' | 'defined' | 'optional' | Unset; -export type Nullability = 'nullable' | 'nonnullable' | Unset; - -export type InferPresence = T extends null - ? 'unset' - : T extends undefined - ? 'defined' - : 'required'; - -export type StrictNonNullable = T extends null ? never : T; - export type Defined = T extends undefined ? never : T; -export type TypeDef = Nullability | Presence | ''; - -export type Default = D extends undefined ? T | undefined : T; - -export type ResolveDefault = undefined> = - | TType - | TDefault; - -export type ResolveInput< - TType, - TNull = Unset, - TDefault = undefined -> = TNull extends 'nullable' - ? Default | null - : StrictNonNullable; - -export type ResolveOutput = TPresent extends 'required' - ? NonNullable - : TPresent extends 'defined' - ? Defined - : TType; // - export type TypedSchema = { __inputType: any; __outputType: any; diff --git a/test/types.ts b/test/types.ts index 241c4532f..03168471b 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,22 +1,21 @@ /* eslint-disable no-unused-labels */ /* eslint-disable no-unused-expressions */ -import { array, string, object, mixed, number, ref, lazy } from '../src'; +import { + array, + string, + object, + mixed, + number, + ref, + lazy, + SchemaOf, +} from '../src'; import type { AssertsShape, DefaultFromShape, - ObjectSchemaOf, TypeOfShape, } from '../src/object'; -import { ResolveOutput, Unset } from '../src/util/types'; - -// let schema = object({ -// str: string().nullable(), -// }).shape({ -// num: number(), -// }); - -// const fff = mixed().nullable(); mixed().required().nullable(); @@ -24,42 +23,6 @@ string().required().nullable(); /** Type utils */ { - // $ExpectType string - type _d4 = ResolveOutput; - - // $ExpectType string - type _d5 = ResolveOutput; - - // $ExpectType string - type _o1 = ResolveOutput; - - // $ExpectType string | undefined - type _o2 = ResolveOutput; - - // $ExpectType string - type _o22 = ResolveOutput; - - // $ExpectType string - type _o3 = ResolveOutput; - - // $ExpectType string - type _o4 = ResolveOutput; - - // $ExpectType string - type _o5 = ResolveOutput; - - // $ExpectType string | null | undefined - type _o6 = ResolveOutput; - - // $ExpectType string | null - type _o7 = ResolveOutput; - - // $ExpectType string | null - type _o8 = ResolveOutput; - - // $ExpectType string - type _o9 = ResolveOutput; - const strRequired = string().required(); // $ExpectType string | undefined @@ -67,7 +30,8 @@ string().required().nullable(); // const strNullableRequired = string().nullable().required(); - // $ExpectType string | null | undefined + + // $ExpectType Maybe strNullableRequired.cast(''); // $ExpectType string @@ -77,7 +41,7 @@ string().required().nullable(); // const strNullable = string().nullable(); - // $ExpectType string | null | undefined + // $ExpectType Maybe strNullable.validateSync(''); const strDefined = string().default(''); @@ -192,12 +156,12 @@ string().required().nullable(); merge.cast({}).other; } -ObjectSchemaOf: { +SchemaOf: { type Person = { firstName: string; }; - type PersonSchema = ObjectSchemaOf; + type PersonSchema = SchemaOf; const _t: PersonSchema = object({ firstName: string().defined(), @@ -278,12 +242,16 @@ ObjectSchemaOf: { // { // $ExpectType string - mixed().required().concat(mixed()).validateSync(''); + mixed().concat(mixed().required()).validateSync(''); - let _f = mixed().notRequired(); + let _f = mixed(); - // $ExpectType string | undefined - const _oo = mixed().required().concat(_f).validateSync(''); + // $ExpectType string | number | undefined + const _oo = mixed() + .required() + .concat(_f) + .notRequired() + .validateSync(''); const _o = object({ str: string(), @@ -296,3 +264,14 @@ ObjectSchemaOf: { // $ExpectType MixedSchema // string().oneOf(['hi' as const]); } + +// Context: { +// type Context = { isCool: boolean }; + +// const schema = object({ +// str: string().when('$isCool', { +// is: true, +// then: string().required(), +// }), +// }); +// } From 3b9c9841ad76ecfbbc89c18821284e5326005dfd Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 1 Dec 2020 09:10:39 -0500 Subject: [PATCH 29/31] finish --- README.md | 6 ++-- src/object.ts | 81 ++++++++++++++++++++++++++++++++++++++++++++------- test/types.ts | 24 +++++++++++++++ 3 files changed, 98 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 096306589..74755d79a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane + - [Install](#install) - [Usage](#usage) - [Using a custom locale dictionary](#using-a-custom-locale-dictionary) @@ -89,13 +90,14 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane - [`object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema`](#objectshapefields-object-nosortedges-arraystring-string-schema) - [`object.pick(keys: string[]): Schema`](#objectpickkeys-string-schema) - [`object.omit(keys: string[]): Schema`](#objectomitkeys-string-schema) - - [`object.from(fromKey: string, toKey: string, alias: boolean = false): Schema`](#objectfromfromkey-string-tokey-string-alias-boolean--false-schema) + - [`object.getDefaultFromShape(): Record`](#objectgetdefaultfromshape-recordstring-unknown) + - [`object.from(fromKey: string, toKey: string, alias: boolean = false): this`](#objectfromfromkey-string-tokey-string-alias-boolean--false-this) - [`object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema`](#objectnounknownonlyknownkeys-boolean--true-message-string--function-schema) - [`object.camelCase(): Schema`](#objectcamelcase-schema) - [`object.constantCase(): Schema`](#objectconstantcase-schema) - [Extending Schema Types](#extending-schema-types) - [TypeScript Support](#typescript-support) - - [TypeScript setting](#typescript-setting) + - [TypeScript settings](#typescript-settings) diff --git a/src/object.ts b/src/object.ts index e58c3ea9b..d674b35b8 100644 --- a/src/object.ts +++ b/src/object.ts @@ -15,14 +15,19 @@ import type { TypedSchema, Defined } from './util/types'; import type Reference from './Reference'; import Lazy from './Lazy'; import BaseSchema, { - Schema, + Schema as AnySchema, SchemaObjectDescription, SchemaSpec, } from './Base'; +export type Assign = { + [P in keyof T]: P extends keyof U ? U[P] : T[P]; +} & + U; + export type AnyObject = Record; -export type ObjectShape = Record>; +export type ObjectShape = Record>; export function create(spec?: TShape) { return new ObjectSchema(spec); @@ -38,11 +43,6 @@ export type DefaultFromShape = { : undefined; }; -type Assign = { - [P in keyof T]: P extends keyof U ? U[P] : T[P]; -} & - U; - export type TypeOfShape = { [K in keyof Shape]: Shape[K] extends TypedSchema ? Shape[K]['__inputType'] @@ -357,7 +357,12 @@ abstract class BaseObjectSchema< pick( keys: TKey[], - ): ObjectSchema> { + ): BaseObjectSchema< + Pick, + TContext, + TypeOfShape> | Optionals, + AssertsShape> | Optionals + > { const picked: any = {}; for (const key of keys) { if (this.fields[key]) picked[key] = this.fields[key]; @@ -371,7 +376,12 @@ abstract class BaseObjectSchema< omit( keys: TKey[], - ): ObjectSchema> { + ): BaseObjectSchema< + Omit, + TContext, + TypeOfShape> | Optionals, + AssertsShape> | Optionals + > { const next = this.clone() as any; const fields = next.fields; next.fields = {}; @@ -479,14 +489,33 @@ export default interface ObjectSchema< ? ObjectSchema : ObjectSchema>; - defined(msg?: MixedLocale['defined']): ObjectSchema; - required(msg?: MixedLocale['required']): ObjectSchema; + defined( + msg?: MixedLocale['defined'], + ): DefinedObjectSchema; + required( + msg?: MixedLocale['required'], + ): RequiredObjectSchema; optional(): this; notRequired(): this; nullable(isNullable?: true): ObjectSchema; nullable( isNullable: false, ): ObjectSchema>; + + pick( + keys: TKey[], + ): ObjectSchema< + Pick, + TContext, + TypeOfShape> | Optionals + >; + omit( + keys: TKey[], + ): ObjectSchema< + Omit, + TContext, + TypeOfShape> | Optionals + >; } export interface DefinedObjectSchema< @@ -518,6 +547,21 @@ export interface DefinedObjectSchema< nullable( isNullable: false, ): DefinedObjectSchema>; + + pick( + keys: TKey[], + ): DefinedObjectSchema< + Pick, + TContext, + TypeOfShape> | Optionals + >; + omit( + keys: TKey[], + ): DefinedObjectSchema< + Omit, + TContext, + TypeOfShape> | Optionals + >; } export interface RequiredObjectSchema< @@ -543,4 +587,19 @@ export interface RequiredObjectSchema< nullable( isNullable: false, ): RequiredObjectSchema>; + + pick( + keys: TKey[], + ): RequiredObjectSchema< + Pick, + TContext, + TypeOfShape> | Optionals + >; + omit( + keys: TKey[], + ): RequiredObjectSchema< + Omit, + TContext, + TypeOfShape> | Optionals + >; } diff --git a/test/types.ts b/test/types.ts index 03168471b..5619b6e13 100644 --- a/test/types.ts +++ b/test/types.ts @@ -156,6 +156,30 @@ string().required().nullable(); merge.cast({}).other; } +ObjectPick: { + const schema = object({ + age: number(), + name: string().required(), + }) + .nullable() + .required(); + + // $ExpectType number | undefined + schema.pick(['age']).validateSync({ age: '1' }).age; +} + +ObjectOmit: { + const schema = object({ + age: number(), + name: string().required(), + }) + .nullable() + .required(); + + // $ExpectType string + schema.omit(['age']).validateSync({ name: '1' }).name; +} + SchemaOf: { type Person = { firstName: string; From 68871b7c2368531cbfca6fea368da5beb8cc9c5d Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Tue, 1 Dec 2020 09:13:32 -0500 Subject: [PATCH 30/31] Apply suggestions from code review --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74755d79a..b90af11ae 100644 --- a/README.md +++ b/README.md @@ -1376,7 +1376,7 @@ You can derive the TypeScript type as follows: ```ts import type { Asserts, TypeOf } from 'yup' -type parsed: Typeof = personSchema.cast(json); +const parsed: Typeof = personSchema.cast(json); const validated: Asserts = personSchema.validateSync(parsed); ``` From 238bbc004b0cf362c80ce10eb8e1662f19cb0c2e Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Thu, 3 Dec 2020 13:36:15 -0500 Subject: [PATCH 31/31] docs --- README.md | 370 +++++++++-------------------------- docs/extending.md | 103 ++++++++++ docs/typescript.md | 100 ++++++++++ src/Lazy.ts | 11 +- src/Reference.ts | 2 +- src/array.ts | 59 +++--- src/boolean.ts | 4 +- src/date.ts | 7 +- src/index.ts | 13 +- src/mixed.ts | 4 +- src/number.ts | 4 +- src/object.ts | 47 ++--- src/{Base.ts => schema.ts} | 29 ++- src/string.ts | 4 +- src/types.ts | 9 +- src/util/ReferenceSet.ts | 2 +- src/util/createValidation.ts | 2 +- test-setup.js | 6 +- test/mixed.js | 8 +- test/types.ts | 4 +- test/yup.js | 46 ++++- 21 files changed, 444 insertions(+), 390 deletions(-) create mode 100644 docs/extending.md create mode 100644 docs/typescript.md rename src/{Base.ts => schema.ts} (97%) diff --git a/README.md b/README.md index b90af11ae..4bd44d483 100644 --- a/README.md +++ b/README.md @@ -4,102 +4,12 @@ Yup is a JavaScript schema builder for value parsing and validation. Define a sc Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leaner and built with client-side validation as its primary use-case. Yup separates the parsing and validating functions into separate steps. `cast()` transforms data while `validate` checks that the input is the correct shape. Each can be performed together (such as HTML form validation) or seperately (such as deserializing trusted data from APIs). -**Try it out:** https://runkit.com/jquense/yup# +## Docs - - - - -- [Install](#install) -- [Usage](#usage) - - [Using a custom locale dictionary](#using-a-custom-locale-dictionary) - [API](#api) - - [`yup`](#yup) - - [`yup.reach(schema: Schema, path: string, value?: object, context?: object): Schema`](#yupreachschema-schema-path-string-value-object-context-object-schema) - - [`yup.addMethod(schemaType: Schema, name: string, method: ()=> Schema): void`](#yupaddmethodschematype-schema-name-string-method--schema-void) - - [`yup.ref(path: string, options: { contextPrefix: string }): Ref`](#yuprefpath-string-options--contextprefix-string--ref) - - [`yup.lazy((value: any) => Schema): Lazy`](#yuplazyvalue-any--schema-lazy) - - [`ValidationError(errors: string | Array, value: any, path: string)`](#validationerrorerrors-string--arraystring-value-any-path-string) - - [mixed](#mixed) - - [`mixed.clone(): Schema`](#mixedclone-schema) - - [`mixed.label(label: string): Schema`](#mixedlabellabel-string-schema) - - [`mixed.meta(metadata: object): Schema`](#mixedmetametadata-object-schema) - - [`mixed.describe(): SchemaDescription`](#mixeddescribe-schemadescription) - - [`mixed.concat(schema: Schema): Schema`](#mixedconcatschema-schema-schema) - - [`mixed.validate(value: any, options?: object): Promise`](#mixedvalidatevalue-any-options-object-promiseany-validationerror) - - [`mixed.validateSync(value: any, options?: object): any`](#mixedvalidatesyncvalue-any-options-object-any) - - [`mixed.validateAt(path: string, value: any, options?: object): Promise`](#mixedvalidateatpath-string-value-any-options-object-promiseany-validationerror) - - [`mixed.validateSyncAt(path: string, value: any, options?: object): any`](#mixedvalidatesyncatpath-string-value-any-options-object-any) - - [`mixed.isValid(value: any, options?: object): Promise`](#mixedisvalidvalue-any-options-object-promiseboolean) - - [`mixed.isValidSync(value: any, options?: object): boolean`](#mixedisvalidsyncvalue-any-options-object-boolean) - - [`mixed.cast(value: any, options = {}): any`](#mixedcastvalue-any-options---any) - - [`mixed.isType(value: any): boolean`](#mixedistypevalue-any-boolean) - - [`mixed.strict(isStrict: boolean = false): Schema`](#mixedstrictisstrict-boolean--false-schema) - - [`mixed.strip(stripField: boolean = true): Schema`](#mixedstripstripfield-boolean--true-schema) - - [`mixed.withMutation(builder: (current: Schema) => void): void`](#mixedwithmutationbuilder-current-schema--void-void) - - [`mixed.default(value: any): Schema`](#mixeddefaultvalue-any-schema) - - [`mixed.getDefault(options?: object): Any`](#mixedgetdefaultoptions-object-any) - - [`mixed.nullable(isNullable: boolean = true): Schema`](#mixednullableisnullable-boolean--true-schema) - - [`mixed.required(message?: string | function): Schema`](#mixedrequiredmessage-string--function-schema) - - [`mixed.notRequired(): Schema` Alias: `optional()`](#mixednotrequired-schema-alias-optional) - - [`mixed.defined(): Schema`](#mixeddefined-schema) - - [`mixed.typeError(message: string): Schema`](#mixedtypeerrormessage-string-schema) - - [`mixed.oneOf(arrayOfValues: Array, message?: string | function): Schema` Alias: `equals`](#mixedoneofarrayofvalues-arrayany-message-string--function-schema-alias-equals) - - [`mixed.notOneOf(arrayOfValues: Array, message?: string | function)`](#mixednotoneofarrayofvalues-arrayany-message-string--function) - - [`mixed.when(keys: string | Array, builder: object | (value, schema)=> Schema): Schema`](#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema) - - [`mixed.test(name: string, message: string | function, test: function): Schema`](#mixedtestname-string-message-string--function-test-function-schema) - - [`mixed.test(options: object): Schema`](#mixedtestoptions-object-schema) - - [`mixed.transform((currentValue: any, originalValue: any) => any): Schema`](#mixedtransformcurrentvalue-any-originalvalue-any--any-schema) - - [string](#string) - - [`string.required(message?: string | function): Schema`](#stringrequiredmessage-string--function-schema) - - [`string.length(limit: number | Ref, message?: string | function): Schema`](#stringlengthlimit-number--ref-message-string--function-schema) - - [`string.min(limit: number | Ref, message?: string | function): Schema`](#stringminlimit-number--ref-message-string--function-schema) - - [`string.max(limit: number | Ref, message?: string | function): Schema`](#stringmaxlimit-number--ref-message-string--function-schema) - - [`string.matches(regex: Regex, message?: string | function): Schema`](#stringmatchesregex-regex-message-string--function-schema) - - [`string.matches(regex: Regex, options: { message: string, excludeEmptyString: bool }): Schema`](#stringmatchesregex-regex-options--message-string-excludeemptystring-bool--schema) - - [`string.email(message?: string | function): Schema`](#stringemailmessage-string--function-schema) - - [`string.url(message?: string | function): Schema`](#stringurlmessage-string--function-schema) - - [`string.uuid(message?: string | function): Schema`](#stringuuidmessage-string--function-schema) - - [`string.ensure(): Schema`](#stringensure-schema) - - [`string.trim(message?: string | function): Schema`](#stringtrimmessage-string--function-schema) - - [`string.lowercase(message?: string | function): Schema`](#stringlowercasemessage-string--function-schema) - - [`string.uppercase(message?: string | function): Schema`](#stringuppercasemessage-string--function-schema) - - [number](#number) - - [`number.min(limit: number | Ref, message?: string | function): Schema`](#numberminlimit-number--ref-message-string--function-schema) - - [`number.max(limit: number | Ref, message?: string | function): Schema`](#numbermaxlimit-number--ref-message-string--function-schema) - - [`number.lessThan(max: number | Ref, message?: string | function): Schema`](#numberlessthanmax-number--ref-message-string--function-schema) - - [`number.moreThan(min: number | Ref, message?: string | function): Schema`](#numbermorethanmin-number--ref-message-string--function-schema) - - [`number.positive(message?: string | function): Schema`](#numberpositivemessage-string--function-schema) - - [`number.negative(message?: string | function): Schema`](#numbernegativemessage-string--function-schema) - - [`number.integer(message?: string | function): Schema`](#numberintegermessage-string--function-schema) - - [`number.truncate(): Schema`](#numbertruncate-schema) - - [`number.round(type: 'floor' | 'ceil' | 'trunc' | 'round' = 'round'): Schema`](#numberroundtype-floor--ceil--trunc--round--round-schema) - - [boolean](#boolean) - - [date](#date) - - [`date.min(limit: Date | string | Ref, message?: string | function): Schema`](#dateminlimit-date--string--ref-message-string--function-schema) - - [`date.max(limit: Date | string | Ref, message?: string | function): Schema`](#datemaxlimit-date--string--ref-message-string--function-schema) - - [array](#array) - - [`array.of(type: Schema): Schema`](#arrayoftype-schema-schema) - - [`array.length(length: number | Ref, message?: string | function): Schema`](#arraylengthlength-number--ref-message-string--function-schema) - - [`array.min(limit: number | Ref, message?: string | function): Schema`](#arrayminlimit-number--ref-message-string--function-schema) - - [`array.max(limit: number | Ref, message?: string | function): Schema`](#arraymaxlimit-number--ref-message-string--function-schema) - - [`array.ensure(): Schema`](#arrayensure-schema) - - [`array.compact(rejector: (value) => boolean): Schema`](#arraycompactrejector-value--boolean-schema) - - [object](#object) - - [Object schema defaults](#object-schema-defaults) - - [`object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema`](#objectshapefields-object-nosortedges-arraystring-string-schema) - - [`object.pick(keys: string[]): Schema`](#objectpickkeys-string-schema) - - [`object.omit(keys: string[]): Schema`](#objectomitkeys-string-schema) - - [`object.getDefaultFromShape(): Record`](#objectgetdefaultfromshape-recordstring-unknown) - - [`object.from(fromKey: string, toKey: string, alias: boolean = false): this`](#objectfromfromkey-string-tokey-string-alias-boolean--false-this) - - [`object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema`](#objectnounknownonlyknownkeys-boolean--true-message-string--function-schema) - - [`object.camelCase(): Schema`](#objectcamelcase-schema) - - [`object.constantCase(): Schema`](#objectconstantcase-schema) -- [Extending Schema Types](#extending-schema-types) -- [TypeScript Support](#typescript-support) - - [TypeScript settings](#typescript-settings) - - +- [Extending yup](docs/extending) +- [TypeScript support](docs/typescript) +- [Playground](https://runkit.com/jquense/yup#) ## Install @@ -116,12 +26,6 @@ import 'core-js/es6/set'; import 'core-js/es6/map'; ``` -If you are using TypeScript installing the Yup typings is recommended - -```sh -npm install -D @types/yup -``` - ## Usage You define and create schema objects. Schema objects are immutable, so each call of a method returns a _new_ schema object. When using es module syntax, yup exports everything as a named export @@ -235,6 +139,94 @@ schema.validate({ name: 'jimmy', age: 11 }).catch(function (err) { ## API + + + + +- [`yup`](#yup) + - [`yup.reach(schema: Schema, path: string, value?: object, context?: object): Schema`](#yupreachschema-schema-path-string-value-object-context-object-schema) + - [`yup.addMethod(schemaType: Schema, name: string, method: ()=> Schema): void`](#yupaddmethodschematype-schema-name-string-method--schema-void) + - [`yup.ref(path: string, options: { contextPrefix: string }): Ref`](#yuprefpath-string-options--contextprefix-string--ref) + - [`yup.lazy((value: any) => Schema): Lazy`](#yuplazyvalue-any--schema-lazy) + - [`ValidationError(errors: string | Array, value: any, path: string)`](#validationerrorerrors-string--arraystring-value-any-path-string) +- [mixed](#mixed) + - [`mixed.clone(): Schema`](#mixedclone-schema) + - [`mixed.label(label: string): Schema`](#mixedlabellabel-string-schema) + - [`mixed.meta(metadata: object): Schema`](#mixedmetametadata-object-schema) + - [`mixed.describe(): SchemaDescription`](#mixeddescribe-schemadescription) + - [`mixed.concat(schema: Schema): Schema`](#mixedconcatschema-schema-schema) + - [`mixed.validate(value: any, options?: object): Promise`](#mixedvalidatevalue-any-options-object-promiseany-validationerror) + - [`mixed.validateSync(value: any, options?: object): any`](#mixedvalidatesyncvalue-any-options-object-any) + - [`mixed.validateAt(path: string, value: any, options?: object): Promise`](#mixedvalidateatpath-string-value-any-options-object-promiseany-validationerror) + - [`mixed.validateSyncAt(path: string, value: any, options?: object): any`](#mixedvalidatesyncatpath-string-value-any-options-object-any) + - [`mixed.isValid(value: any, options?: object): Promise`](#mixedisvalidvalue-any-options-object-promiseboolean) + - [`mixed.isValidSync(value: any, options?: object): boolean`](#mixedisvalidsyncvalue-any-options-object-boolean) + - [`mixed.cast(value: any, options = {}): any`](#mixedcastvalue-any-options---any) + - [`mixed.isType(value: any): boolean`](#mixedistypevalue-any-boolean) + - [`mixed.strict(isStrict: boolean = false): Schema`](#mixedstrictisstrict-boolean--false-schema) + - [`mixed.strip(stripField: boolean = true): Schema`](#mixedstripstripfield-boolean--true-schema) + - [`mixed.withMutation(builder: (current: Schema) => void): void`](#mixedwithmutationbuilder-current-schema--void-void) + - [`mixed.default(value: any): Schema`](#mixeddefaultvalue-any-schema) + - [`mixed.getDefault(options?: object): Any`](#mixedgetdefaultoptions-object-any) + - [`mixed.nullable(isNullable: boolean = true): Schema`](#mixednullableisnullable-boolean--true-schema) + - [`mixed.required(message?: string | function): Schema`](#mixedrequiredmessage-string--function-schema) + - [`mixed.notRequired(): Schema` Alias: `optional()`](#mixednotrequired-schema-alias-optional) + - [`mixed.defined(): Schema`](#mixeddefined-schema) + - [`mixed.typeError(message: string): Schema`](#mixedtypeerrormessage-string-schema) + - [`mixed.oneOf(arrayOfValues: Array, message?: string | function): Schema` Alias: `equals`](#mixedoneofarrayofvalues-arrayany-message-string--function-schema-alias-equals) + - [`mixed.notOneOf(arrayOfValues: Array, message?: string | function)`](#mixednotoneofarrayofvalues-arrayany-message-string--function) + - [`mixed.when(keys: string | Array, builder: object | (value, schema)=> Schema): Schema`](#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema) + - [`mixed.test(name: string, message: string | function, test: function): Schema`](#mixedtestname-string-message-string--function-test-function-schema) + - [`mixed.test(options: object): Schema`](#mixedtestoptions-object-schema) + - [`mixed.transform((currentValue: any, originalValue: any) => any): Schema`](#mixedtransformcurrentvalue-any-originalvalue-any--any-schema) +- [string](#string) + - [`string.required(message?: string | function): Schema`](#stringrequiredmessage-string--function-schema) + - [`string.length(limit: number | Ref, message?: string | function): Schema`](#stringlengthlimit-number--ref-message-string--function-schema) + - [`string.min(limit: number | Ref, message?: string | function): Schema`](#stringminlimit-number--ref-message-string--function-schema) + - [`string.max(limit: number | Ref, message?: string | function): Schema`](#stringmaxlimit-number--ref-message-string--function-schema) + - [`string.matches(regex: Regex, message?: string | function): Schema`](#stringmatchesregex-regex-message-string--function-schema) + - [`string.matches(regex: Regex, options: { message: string, excludeEmptyString: bool }): Schema`](#stringmatchesregex-regex-options--message-string-excludeemptystring-bool--schema) + - [`string.email(message?: string | function): Schema`](#stringemailmessage-string--function-schema) + - [`string.url(message?: string | function): Schema`](#stringurlmessage-string--function-schema) + - [`string.uuid(message?: string | function): Schema`](#stringuuidmessage-string--function-schema) + - [`string.ensure(): Schema`](#stringensure-schema) + - [`string.trim(message?: string | function): Schema`](#stringtrimmessage-string--function-schema) + - [`string.lowercase(message?: string | function): Schema`](#stringlowercasemessage-string--function-schema) + - [`string.uppercase(message?: string | function): Schema`](#stringuppercasemessage-string--function-schema) +- [number](#number) + - [`number.min(limit: number | Ref, message?: string | function): Schema`](#numberminlimit-number--ref-message-string--function-schema) + - [`number.max(limit: number | Ref, message?: string | function): Schema`](#numbermaxlimit-number--ref-message-string--function-schema) + - [`number.lessThan(max: number | Ref, message?: string | function): Schema`](#numberlessthanmax-number--ref-message-string--function-schema) + - [`number.moreThan(min: number | Ref, message?: string | function): Schema`](#numbermorethanmin-number--ref-message-string--function-schema) + - [`number.positive(message?: string | function): Schema`](#numberpositivemessage-string--function-schema) + - [`number.negative(message?: string | function): Schema`](#numbernegativemessage-string--function-schema) + - [`number.integer(message?: string | function): Schema`](#numberintegermessage-string--function-schema) + - [`number.truncate(): Schema`](#numbertruncate-schema) + - [`number.round(type: 'floor' | 'ceil' | 'trunc' | 'round' = 'round'): Schema`](#numberroundtype-floor--ceil--trunc--round--round-schema) +- [boolean](#boolean) +- [date](#date) + - [`date.min(limit: Date | string | Ref, message?: string | function): Schema`](#dateminlimit-date--string--ref-message-string--function-schema) + - [`date.max(limit: Date | string | Ref, message?: string | function): Schema`](#datemaxlimit-date--string--ref-message-string--function-schema) +- [array](#array) + - [`array.of(type: Schema): Schema`](#arrayoftype-schema-schema) + - [`array.length(length: number | Ref, message?: string | function): Schema`](#arraylengthlength-number--ref-message-string--function-schema) + - [`array.min(limit: number | Ref, message?: string | function): Schema`](#arrayminlimit-number--ref-message-string--function-schema) + - [`array.max(limit: number | Ref, message?: string | function): Schema`](#arraymaxlimit-number--ref-message-string--function-schema) + - [`array.ensure(): Schema`](#arrayensure-schema) + - [`array.compact(rejector: (value) => boolean): Schema`](#arraycompactrejector-value--boolean-schema) +- [object](#object) + - [Object schema defaults](#object-schema-defaults) + - [`object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema`](#objectshapefields-object-nosortedges-arraystring-string-schema) + - [`object.pick(keys: string[]): Schema`](#objectpickkeys-string-schema) + - [`object.omit(keys: string[]): Schema`](#objectomitkeys-string-schema) + - [`object.getDefaultFromShape(): Record`](#objectgetdefaultfromshape-recordstring-unknown) + - [`object.from(fromKey: string, toKey: string, alias: boolean = false): this`](#objectfromfromkey-string-tokey-string-alias-boolean--false-this) + - [`object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema`](#objectnounknownonlyknownkeys-boolean--true-message-string--function-schema) + - [`object.camelCase(): Schema`](#objectcamelcase-schema) + - [`object.constantCase(): Schema`](#objectconstantcase-schema) + + + ### `yup` The module export. @@ -1228,181 +1220,3 @@ Transforms all object keys to camelCase #### `object.constantCase(): Schema` Transforms all object keys to CONSTANT_CASE. - -## Extending Schema Types - -The simplest way to extend an existing type is just to cache a configured schema and use that through your application. - -```js -let yup = require('yup'); -let parseFormats = ['MMM dd, yyy']; -let invalidDate = new Date(''); - -module.exports = yup.date().transform(function (value, originalValue) { - if (this.isType(value)) return value; - // the default coercion transform failed so let's try it with Moment instead - value = Moment(originalValue, parseFormats); - return value.isValid() ? value.toDate() : invalidDate; -}); -``` - -Alternatively, each schema is a normal JavaScript constructor function that you can mutate or delegate to -using the normal patterns. Generally you should not inherit from `mixed` unless you know what you are doing, -better to think of it as an abstract class. The other types are fair game though. - -You should keep in mind some basic guidelines when extending schemas: - -- never mutate an existing schema, always `clone()` and then mutate the new one before returning it. - Built-in methods like `test` and `transform` take care of this for you, so you can safely use them (see below) without worrying -- transforms should never mutate the `value` passed in, and should return an invalid object when one exists - (`NaN`, `InvalidDate`, etc) instead of `null` for bad values. -- by the time validations run the `value` is guaranteed to be the correct type, however if `nullable` is - set then `null` is a valid value for that type, so don't assume that a property or method exists on the value. - -**Adjust core Types** - -```js -let invalidDate = new Date(''); - -function parseDateFromFormats(formats, parseStrict) { - return this.transform(function (value, originalValue) { - if (this.isType(value)) return value; - - value = Moment(originalValue, formats, parseStrict); - - return value.isValid() ? value.toDate() : invalidDate; - }); -} - -// `addMethod` doesn't do anything special; it's -// equivalent to: yup.date.prototype.format = parseDateFromFormats -yup.addMethod(yup.date, 'format', parseDateFromFormats); -``` - -**Creating new Types** - -Yup schema use the common constructor pattern for modeling inheritance. You can use any -utility or pattern that works with that pattern. The below demonstrates using the ES6 class -syntax since it's less verbose, but you absolutely aren't required to use it. - -```js -import { DateSchema } from 'yup'; - -let invalidDate = new Date(''); // our failed to coerce value - -class MomentDateSchemaType extends DateSchema { - static create() { - return MomentDateSchemaType(); - } - constructor() { - super(); - this._validFormats = []; - - this.withMutation(() => { - this.transform(function (value, originalvalue) { - if (this.isType(value)) - // we have a valid value - return value; - return Moment(originalValue, this._validFormats, true); - }); - }); - } - - _typeCheck(value) { - return ( - super._typeCheck(value) || (moment.isMoment(value) && value.isValid()) - ); - } - - format(formats) { - if (!formats) throw new Error('must enter a valid format'); - let next = this.clone(); - next._validFormats = {}.concat(formats); - } -} - -let schema = MomentDateSchemaType.create(); - -schema.format('YYYY-MM-DD').cast('It is 2012-05-25'); // => Fri May 25 2012 00:00:00 GMT-0400 (Eastern Daylight Time) -``` - -## TypeScript Support - -`yup` comes with robust typescript support! However, because of how dynamic `yup` is -not everything can be statically typed safely, but for most cases it's "Good Enough". - -Not that `yup` schema actually produce _two_ different types: the result of casting an input, and the value after validation. -Why are these types different? Because a schema can produce a value via casting that -would not pass validation! - -```js -const schema = string().nullable().required(); - -schema.cast(null); // -> null -schema.validateSync(null); // ValidationError this is required! -``` - -By itself this seems weird, but has it uses when handling user input. To get a -TypeScript type that matches all possible `cast()` values, use `yup.TypeOf`. -To produce a type that matches a valid object for the schema use `yup.Asserts>` - -```ts -import * as yup from 'yup'; - -const personSchema = yup.object({ - firstName: yup - .string() - // Here we use `defined` instead of `required` to more closely align with - // TypeScript. Both will have the same effect on the resulting type by - // excluding `undefined`, but `required` will also disallow empty strings. - .defined(), - // defaults also affect the possible output type! - // schema with default values won't produce `undefined` values. Remember object schema - // have a default value built in. - nickName: yup.string().default('').nullable(), - gender: yup - .mixed() - // Note `as const`: this types the array as `["male", "female", "other"]` - // instead of `string[]`. - .oneOf(['male', 'female', 'other'] as const) - .defined(), - email: yup.string().nullable().notRequired().email(), - birthDate: yup.date().nullable().notRequired().min(new Date(1900, 0, 1)), -}); -``` - -You can derive the TypeScript type as follows: - -```ts -import type { Asserts, TypeOf } from 'yup' - -const parsed: Typeof = personSchema.cast(json); - -const validated: Asserts = personSchema.validateSync(parsed); -``` - -You can also go the other direction, specifying an interface and ensuring that a schema would match it: - -```ts -import { string, object, number, SchemaOf } from 'yup'; - -type Person = { - firstName: string; -}; - -// ✔️ compiles -const goodPersonSchema: SchemaOf = object({ - firstName: string().defined(), -}).defined(); - -// ❌ errors: -// "Type 'number | undefined' is not assignable to type 'string'." -const badPersonSchema: SchemaOf = object({ - firstName: number(), -}); -``` - -### TypeScript settings - -For type utilties to work correctly with required and nullable types you have -to set `strict: true` or `strictNullChecks: true` in your tsconfig.json. diff --git a/docs/extending.md b/docs/extending.md new file mode 100644 index 000000000..67e5cc432 --- /dev/null +++ b/docs/extending.md @@ -0,0 +1,103 @@ +# Extending Schema + +For simple cases where you want to reuse common schema configurations, creating +and passing around instances works great and is automatically typed correctly + +```js +import * as yup from 'yup'; + +const requiredString = yup.string().required().default(''); + +const momentDate = (parseFormats = ['MMM dd, yyy']) => + yup.date().transform(function (value, originalValue) { + if (this.isType(value)) return value; + + // the default coercion transform failed so let's try it with Moment instead + value = Moment(originalValue, parseFormats); + return value.isValid() ? value.toDate() : yup.date.INVALID_DATE; + }); + +export { momentDate, requiredString }; +``` + +Schema are immutable so each can be configured further without changing the original. + +## Extending Schema with new methods + +`yup` provides a `addMethod()` utility for extending built-in schema: + +```js +function parseDateFromFormats(formats, parseStrict) { + return this.transform(function (value, originalValue) { + if (this.isType(value)) return value; + + value = Moment(originalValue, formats, parseStrict); + + return value.isValid() ? value.toDate() : yup.date.INVALID_DATE; + }); +} + +yup.addMethod(yup.date, 'format', parseDateFromFormats); +``` + +Note that `addMethod` isn't really magic, it mutates the prototype of the passed in schema. + +> Note: if you are using TypeScript you also need to adjust the class or interface +> see the [typescript](./typescript) docs for details. + +## Creating new Schema types + +If you're use case calls for creating an entirely new type. inheriting from +and existing schema class may be best: Generally you should not inheriting from +the abstract `Schema` unless you know what you are doing. The other types are fair game though. + +You should keep in mind some basic guidelines when extending schemas: + +- never mutate an existing schema, always `clone()` and then mutate the new one before returning it. + Built-in methods like `test` and `transform` take care of this for you, so you can safely use them (see below) without worrying + +- transforms should never mutate the `value` passed in, and should return an invalid object when one exists + (`NaN`, `InvalidDate`, etc) instead of `null` for bad values. + +- by the time validations run the `value` is guaranteed to be the correct type, however it still may + be `null` or `undefined` + +```js +import { DateSchema } from 'yup'; + +class MomentDateSchema extends DateSchema { + static create() { + return MomentDateSchema(); + } + + constructor() { + super(); + this._validFormats = []; + + this.withMutation(() => { + this.transform(function (value, originalvalue) { + if (this.isType(value)) + // we have a valid value + return value; + return Moment(originalValue, this._validFormats, true); + }); + }); + } + + _typeCheck(value) { + return ( + super._typeCheck(value) || (moment.isMoment(value) && value.isValid()) + ); + } + + format(formats) { + if (!formats) throw new Error('must enter a valid format'); + let next = this.clone(); + next._validFormats = {}.concat(formats); + } +} + +let schema = new MomentDateSchema(); + +schema.format('YYYY-MM-DD').cast('It is 2012-05-25'); // => Fri May 25 2012 00:00:00 GMT-0400 (Eastern Daylight Time) +``` diff --git a/docs/typescript.md b/docs/typescript.md new file mode 100644 index 000000000..3d675f69f --- /dev/null +++ b/docs/typescript.md @@ -0,0 +1,100 @@ +## TypeScript Support + +`yup` comes with robust typescript support! However, because of how dynamic `yup` is +not everything can be statically typed safely, but for most cases it's "Good Enough". + +Not that `yup` schema actually produce _two_ different types: the result of casting an input, and the value after validation. +Why are these types different? Because a schema can produce a value via casting that +would not pass validation! + +```js +const schema = string().nullable().required(); + +schema.cast(null); // -> null +schema.validateSync(null); // ValidationError this is required! +``` + +By itself this seems weird, but has it uses when handling user input. To get a +TypeScript type that matches all possible `cast()` values, use `yup.TypeOf`. +To produce a type that matches a valid object for the schema use `yup.Asserts>` + +```ts +import * as yup from 'yup'; + +const personSchema = yup.object({ + firstName: yup + .string() + // Here we use `defined` instead of `required` to more closely align with + // TypeScript. Both will have the same effect on the resulting type by + // excluding `undefined`, but `required` will also disallow empty strings. + .defined(), + // defaults also affect the possible output type! + // schema with default values won't produce `undefined` values. Remember object schema + // have a default value built in. + nickName: yup.string().default('').nullable(), + gender: yup + .mixed() + // Note `as const`: this types the array as `["male", "female", "other"]` + // instead of `string[]`. + .oneOf(['male', 'female', 'other'] as const) + .defined(), + email: yup.string().nullable().notRequired().email(), + birthDate: yup.date().nullable().notRequired().min(new Date(1900, 0, 1)), +}); +``` + +You can derive the TypeScript type as follows: + +```ts +import type { Asserts, TypeOf } from 'yup'; + +const parsed: Typeof = personSchema.cast(json); + +const validated: Asserts = personSchema.validateSync( + parsed, +); +``` + +You can also go the other direction, specifying an interface and ensuring that a schema would match it: + +```ts +import { string, object, number, SchemaOf } from 'yup'; + +type Person = { + firstName: string; +}; + +// ✔️ compiles +const goodPersonSchema: SchemaOf = object({ + firstName: string().defined(), +}).defined(); + +// ❌ errors: +// "Type 'number | undefined' is not assignable to type 'string'." +const badPersonSchema: SchemaOf = object({ + firstName: number(), +}); +``` + +### TypeScript settings + +For type utilties to work correctly with required and nullable types you have +to set `strict: true` or `strictNullChecks: true` in your tsconfig.json. + +### Extending built-in types + +You can use TypeScript's interface merging behavior to extend the schema types +if needed. Type extensions should go in an "ambient" type def file such as your +`globals.d.ts`. + +```ts +declare module 'yup' { + class StringSchema { + myMethod(param: string): this; + } +} +``` + +> Watch out!: If your method needs to adjust schema generics, you likely +> need to also extend the Required*, and Defined* interfaces associated with +> each basic type. Consult the core types for examples on how to do this diff --git a/src/Lazy.ts b/src/Lazy.ts index beb6a947f..8aaad37ae 100644 --- a/src/Lazy.ts +++ b/src/Lazy.ts @@ -2,17 +2,17 @@ import isSchema from './util/isSchema'; import type { Callback, ValidateOptions } from './types'; import type { ResolveOptions } from './Condition'; -import type { Schema, CastOptions } from './Base'; +import type { AnySchema, CastOptions } from './schema'; import { TypedSchema, TypeOf } from './util/types'; -type ContextOf = T extends Schema ? C : never; +type ContextOf = T extends AnySchema ? C : never; -export type LazyBuilder = ( +export type LazyBuilder = ( value: any, options: ResolveOptions, ) => T; -export function create(builder: LazyBuilder) { +export function create(builder: LazyBuilder) { return new Lazy(builder); } @@ -24,7 +24,8 @@ export type LazyType = LazyReturnValue extends TypedSchema ? TypeOf> : never; -class Lazy> implements TypedSchema { +class Lazy> + implements TypedSchema { type = 'lazy' as const; __isYupSchema__ = true; diff --git a/src/Reference.ts b/src/Reference.ts index ae81a138d..c57b472a9 100644 --- a/src/Reference.ts +++ b/src/Reference.ts @@ -1,5 +1,5 @@ import { getter } from 'property-expr'; -import type { SchemaRefDescription } from './Base'; +import type { SchemaRefDescription } from './schema'; const prefixes = { context: '$', diff --git a/src/array.ts b/src/array.ts index 7bf37fc5f..5e66cb21d 100644 --- a/src/array.ts +++ b/src/array.ts @@ -16,25 +16,25 @@ import ValidationError from './ValidationError'; import type Reference from './Reference'; import { Asserts, Defined, If, Thunk, TypeOf } from './util/types'; import BaseSchema, { - Schema, + AnySchema, SchemaInnerTypeDescription, SchemaSpec, -} from './Base'; +} from './schema'; -type RejectorFn = (value: any, index: number, array: any[]) => boolean; +export type RejectorFn = (value: any, index: number, array: any[]) => boolean; export function create< C extends AnyObject = AnyObject, - T extends Schema = Schema + T extends AnySchema = AnySchema >(type?: T) { - return new ArraySchema(type); + return new ArraySchema(type) as OptionalArraySchema; } -class BaseArraySchema< - T extends Schema, - C extends AnyObject, - TIn extends Maybe[]>, - TOut extends Maybe[]> +export default class ArraySchema< + T extends AnySchema, + C extends AnyObject = AnyObject, + TIn extends Maybe[]> = TypeOf[] | undefined, + TOut extends Maybe[]> = Asserts[] | Optionals > extends BaseSchema { innerType?: T; @@ -164,7 +164,7 @@ class BaseArraySchema< return next; } - concat>( + concat>( schema: TOther, ): TOther; concat(schema: any): any; @@ -181,7 +181,7 @@ class BaseArraySchema< return next; } - of(schema: TInner): ArraySchema { + of(schema: TInner): ArraySchema { // FIXME: this should return a new instance of array without the default to be var next = this.clone(); @@ -267,26 +267,17 @@ class BaseArraySchema< } } -export default class ArraySchema< - T extends Schema, - TContext extends AnyObject = AnyObject, - TIn extends Maybe[]> = TypeOf[] | undefined -> extends BaseArraySchema[] | Optionals> {} +create.prototype = ArraySchema.prototype; // // Interfaces // export interface DefinedArraySchema< - T extends Schema, + T extends AnySchema, TContext extends AnyObject, TIn extends Maybe[]> -> extends BaseArraySchema< - T, - TContext, - TIn, - Asserts[] | Preserve - > { +> extends ArraySchema[] | Preserve> { default>( def: Thunk, ): If< @@ -308,10 +299,10 @@ export interface DefinedArraySchema< } export interface RequiredArraySchema< - T extends Schema, + T extends AnySchema, TContext extends AnyObject, TIn extends Maybe[]> -> extends BaseArraySchema[]> { +> extends ArraySchema[]> { default>( def: Thunk, ): If< @@ -330,11 +321,11 @@ export interface RequiredArraySchema< ): RequiredArraySchema>; } -export default interface ArraySchema< - T extends Schema, +export interface OptionalArraySchema< + T extends AnySchema, TContext extends AnyObject = AnyObject, TIn extends Maybe[]> = TypeOf[] | undefined -> extends BaseArraySchema[] | Optionals> { +> extends ArraySchema { default>( def: Thunk, ): If< @@ -348,9 +339,11 @@ export default interface ArraySchema< msg?: MixedLocale['required'], ): RequiredArraySchema; - optional(): this; - notRequired(): this; + optional(): ArraySchema; + notRequired(): ArraySchema; - nullable(isNullable?: true): ArraySchema; - nullable(isNullable: false): ArraySchema>; + nullable(isNullable?: true): RequiredArraySchema; + nullable( + isNullable: false, + ): RequiredArraySchema>; } diff --git a/src/boolean.ts b/src/boolean.ts index e4e8ec45e..421189b4d 100644 --- a/src/boolean.ts +++ b/src/boolean.ts @@ -1,4 +1,4 @@ -import BaseSchema from './Base'; +import BaseSchema from './schema'; import type { MixedLocale } from './locale'; import type { AnyObject, Maybe, Optionals } from './types'; import type { Defined } from './util/types'; @@ -63,6 +63,8 @@ export default class BooleanSchema< } } +create.prototype = BooleanSchema.prototype; + export default interface BooleanSchema< TType extends Maybe, TContext extends AnyObject = AnyObject, diff --git a/src/date.ts b/src/date.ts index 4fd8a4374..946921242 100644 --- a/src/date.ts +++ b/src/date.ts @@ -6,7 +6,7 @@ import isAbsent from './util/isAbsent'; import Ref from './Reference'; import type { AnyObject, Maybe } from './types'; import type { Defined, If, Thunk } from './util/types'; -import BaseSchema from './Base'; +import BaseSchema from './schema'; let invalidDate = new Date(''); @@ -22,6 +22,8 @@ export default class DateSchema< TContext extends AnyObject = AnyObject, TOut extends TType = TType > extends BaseSchema { + static INVALID_DATE = invalidDate; + constructor() { super({ type: 'date' }); @@ -89,6 +91,9 @@ export default class DateSchema< } } +create.prototype = DateSchema.prototype; +create.INVALID_DATE = invalidDate; + export default interface DateSchema< TType extends Maybe, TContext extends AnyObject = AnyObject, diff --git a/src/index.ts b/src/index.ts index 4fb36a942..f63e1e2b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,16 +11,16 @@ import ValidationError from './ValidationError'; import reach from './util/reach'; import isSchema from './util/isSchema'; import setLocale from './setLocale'; -import type { Schema } from './Base'; +import BaseSchema, { AnySchema } from './schema'; import type { TypeOf, Asserts } from './util/types'; import { Maybe } from './types'; -function addMethod( - schemaType: (...aarg: any[]) => T, +function addMethod( + schemaType: (...arg: any[]) => T, name: string, fn: (this: T, ...args: any[]) => T, ): void; -function addMethod Schema>( +function addMethod AnySchema>( schemaType: T, name: string, fn: (this: InstanceType, ...args: any[]) => InstanceType, @@ -41,9 +41,9 @@ type SchemaOf = T extends AnyObject ? ObjectSchema<{ [k in keyof T]: SchemaOf }> : T extends Array ? ArraySchema> - : Schema, AnyObject, T>; + : BaseSchema, AnyObject, T>; -export type { SchemaOf, TypeOf, Asserts, Asserts as InferType, Schema }; +export type { SchemaOf, TypeOf, Asserts, Asserts as InferType, AnySchema }; export { mixedCreate as mixed, @@ -64,6 +64,7 @@ export { }; export { + BaseSchema, MixedSchema, BoolSchema, StringSchema, diff --git a/src/mixed.ts b/src/mixed.ts index 5bcec77a5..165d72130 100644 --- a/src/mixed.ts +++ b/src/mixed.ts @@ -2,7 +2,7 @@ import type { MixedLocale } from './locale'; import { AnyObject, Maybe, Optionals } from './types'; import type { Defined } from './util/types'; -import BaseSchema, { Schema } from './Base'; +import BaseSchema from './schema'; export function create() { return new MixedSchema(); @@ -14,6 +14,8 @@ export default class MixedSchema< TOut = TType > extends BaseSchema {} +create.prototype = MixedSchema.prototype; + export default interface MixedSchema< TType = any, TContext = AnyObject, diff --git a/src/number.ts b/src/number.ts index 8d2496892..5014957ca 100644 --- a/src/number.ts +++ b/src/number.ts @@ -3,7 +3,7 @@ import isAbsent from './util/isAbsent'; import type { AnyObject, Maybe } from './types'; import type Reference from './Reference'; import type { Defined, If, Thunk } from './util/types'; -import BaseSchema from './Base'; +import BaseSchema from './schema'; let isNaN = (value: Maybe) => value != +value!; @@ -129,6 +129,8 @@ export default class NumberSchema< } } +create.prototype = NumberSchema.prototype; + // // Number Interfaces // diff --git a/src/object.ts b/src/object.ts index d674b35b8..7c6f99230 100644 --- a/src/object.ts +++ b/src/object.ts @@ -18,7 +18,7 @@ import BaseSchema, { Schema as AnySchema, SchemaObjectDescription, SchemaSpec, -} from './Base'; +} from './schema'; export type Assign = { [P in keyof T]: P extends keyof U ? U[P] : T[P]; @@ -29,10 +29,6 @@ export type AnyObject = Record; export type ObjectShape = Record>; -export function create(spec?: TShape) { - return new ObjectSchema(spec); -} - export type DefaultFromShape = { [K in keyof Shape]: Shape[K] extends ObjectSchema ? DefaultFromShape @@ -73,11 +69,11 @@ function unknown(ctx: ObjectSchema, value: any) { const defaultSort = sortByKeyOrder([]); -abstract class BaseObjectSchema< +export default class ObjectSchema< TShape extends ObjectShape, - TContext extends AnyObject, - TIn extends Maybe, - TOut extends Maybe + TContext extends AnyObject = AnyObject, + TIn extends Maybe> = TypeOfShape, + TOut extends Maybe> = AssertsShape > extends BaseSchema { fields: TShape = Object.create(null); @@ -357,7 +353,7 @@ abstract class BaseObjectSchema< pick( keys: TKey[], - ): BaseObjectSchema< + ): ObjectSchema< Pick, TContext, TypeOfShape> | Optionals, @@ -376,7 +372,7 @@ abstract class BaseObjectSchema< omit( keys: TKey[], - ): BaseObjectSchema< + ): ObjectSchema< Omit, TContext, TypeOfShape> | Optionals, @@ -462,27 +458,18 @@ abstract class BaseObjectSchema< } } -export default class ObjectSchema< - TShape extends ObjectShape, - TContext extends AnyObject = AnyObject, - TIn extends Maybe> = TypeOfShape -> extends BaseObjectSchema< - TShape, - TContext, - TIn, - AssertsShape | Optionals -> {} +export function create(spec?: TShape) { + return new ObjectSchema(spec); +} + +create.prototype = ObjectSchema.prototype; export default interface ObjectSchema< TShape extends ObjectShape, TContext extends AnyObject = AnyObject, - TIn extends Maybe> = TypeOfShape -> extends BaseObjectSchema< - TShape, - TContext, - TIn, - AssertsShape | Optionals - > { + TIn extends Maybe> = TypeOfShape, + TOut extends Maybe> = AssertsShape +> extends BaseSchema { default>>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined @@ -522,7 +509,7 @@ export interface DefinedObjectSchema< TShape extends ObjectShape, TContext extends AnyObject, TIn extends Maybe> -> extends BaseObjectSchema< +> extends ObjectSchema< TShape, TContext, TIn, @@ -568,7 +555,7 @@ export interface RequiredObjectSchema< TShape extends ObjectShape, TContext extends AnyObject, TIn extends Maybe> -> extends BaseObjectSchema> { +> extends ObjectSchema> { default>>( def: TNextDefault | (() => TNextDefault), ): TNextDefault extends undefined diff --git a/src/Base.ts b/src/schema.ts similarity index 97% rename from src/Base.ts rename to src/schema.ts index 9f063127d..ed1fa16a7 100644 --- a/src/Base.ts +++ b/src/schema.ts @@ -48,7 +48,7 @@ export type SchemaOptions = { spec?: SchemaSpec; }; -export type Schema = BaseSchema< +export type AnySchema = BaseSchema< Type, TContext, TOut @@ -162,7 +162,7 @@ export default abstract class BaseSchema< // if the nested value is a schema we can skip cloning, since // they are already immutable - const next: Schema = Object.create(Object.getPrototypeOf(this)); + const next: AnySchema = Object.create(Object.getPrototypeOf(this)); // @ts-expect-error this is readonly next.type = this.type; @@ -217,8 +217,8 @@ export default abstract class BaseSchema< } concat(schema: this): this; - concat(schema: Schema): Schema; - concat(schema: Schema): Schema { + concat(schema: AnySchema): AnySchema; + concat(schema: AnySchema): AnySchema { if (!schema || schema === this) return this; if (schema.type !== this.type && this.type !== 'mixed') @@ -337,7 +337,7 @@ export default abstract class BaseSchema< rawValue === undefined ? rawValue : this.transforms.reduce( - (value, fn) => fn.call(this, value, rawValue), + (value, fn) => fn.call(this, value, rawValue, this), rawValue, ); @@ -450,17 +450,14 @@ export default abstract class BaseSchema< return result; } - async isValid( - value: any, - options?: ValidateOptions, - ): Promise { - try { - await this.validate(value, options); - return true; - } catch (err) { - if (ValidationError.isError(err)) return false; - throw err; - } + isValid(value: any, options?: ValidateOptions): Promise { + return this.validate(value, options).then( + () => true, + (err) => { + if (ValidationError.isError(err)) return false; + throw err; + }, + ); } isValidSync( diff --git a/src/string.ts b/src/string.ts index 9b8c9bcef..ca46393d8 100644 --- a/src/string.ts +++ b/src/string.ts @@ -3,7 +3,7 @@ import isAbsent from './util/isAbsent'; import type Reference from './Reference'; import type { Message, Maybe, AnyObject } from './types'; import type { Defined, If, Thunk } from './util/types'; -import BaseSchema from './Base'; +import BaseSchema from './schema'; // eslint-disable-next-line let rEmail = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; @@ -197,6 +197,8 @@ export default class StringSchema< } } +create.prototype = StringSchema.prototype; + // // String Interfaces // diff --git a/src/types.ts b/src/types.ts index acca5c15a..95e650d2a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,16 +1,17 @@ -import type { Schema } from './Base'; +import type { AnySchema } from './schema'; import type Lazy from './Lazy'; export type AnyObject = Record; -export type SchemaLike = Schema | Lazy; +export type SchemaLike = AnySchema | Lazy; export type Callback = (err: Error | null, value?: T) => void; -export type TransformFunction = ( +export type TransformFunction = ( this: T, value: any, originalValue: any, + schema: T, ) => any; export interface ValidateOptions { @@ -43,7 +44,7 @@ export interface InternalOptions parent?: any; path?: string; sync?: boolean; - from?: { schema: Schema; value: any }[]; + from?: { schema: AnySchema; value: any }[]; } export interface MessageParams { diff --git a/src/util/ReferenceSet.ts b/src/util/ReferenceSet.ts index a18919770..a6ac2a167 100644 --- a/src/util/ReferenceSet.ts +++ b/src/util/ReferenceSet.ts @@ -1,4 +1,4 @@ -import type { SchemaRefDescription } from '../Base'; +import type { SchemaRefDescription } from '../schema'; import Reference from '../Reference'; export default class ReferenceSet { diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts index f7dc3dff7..97f08fe65 100644 --- a/src/util/createValidation.ts +++ b/src/util/createValidation.ts @@ -9,7 +9,7 @@ import { ExtraParams, } from '../types'; import Reference from '../Reference'; -import type { Schema } from '../Base'; +import type { Schema } from '../schema'; export type CreateErrorOptions = { path?: string; diff --git a/test-setup.js b/test-setup.js index 533818cc6..825229e98 100644 --- a/test-setup.js +++ b/test-setup.js @@ -20,11 +20,11 @@ Object.defineProperty( global.TestHelpers = require('./test/helpers'); if (global.YUP_USE_SYNC) { - const { MixedSchema } = require('./src'); // eslint-disable-line global-require + const { BaseSchema } = require('./src'); // eslint-disable-line global-require - const { validate } = MixedSchema.prototype; + const { validate } = BaseSchema.prototype; - MixedSchema.prototype.validate = function (value, options = {}, maybeCb) { + BaseSchema.prototype.validate = function (value, options = {}, maybeCb) { let run = false; options.sync = true; diff --git a/test/mixed.js b/test/mixed.js index 806b06878..c6551bf41 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -675,11 +675,11 @@ describe('Mixed Types ', () => { await inst.isValid('a').should.become(true); }); - it('concat should maintain explicit nullability', async function () { - let inst = string().nullable().concat(string().default('hi')); + // xit('concat should maintain explicit nullability', async function () { + // let inst = string().nullable().concat(string().default('hi')); - await inst.isValid(null).should.become(true); - }); + // await inst.isValid(null).should.become(true); + // }); it('concat should maintain explicit presence', async function () { let inst = string().required().concat(string()); diff --git a/test/types.ts b/test/types.ts index 5619b6e13..604e28640 100644 --- a/test/types.ts +++ b/test/types.ts @@ -49,7 +49,7 @@ string().required().nullable(); // $ExpectType string const _strDefined = strDefined.getDefault(); - const strDefault = string().nullable().default(''); + const strDefault = string().nullable().default('').trim(); // $ExpectType string | null strDefault.cast(''); @@ -59,7 +59,7 @@ string().required().nullable(); // // - const strDefaultRequired = string().nullable().required().default(''); + const strDefaultRequired = string().nullable().required().default('').trim(); // $ExpectType string | null strDefaultRequired.cast(''); diff --git a/test/yup.js b/test/yup.js index 7ef3cb78d..c996eacc8 100644 --- a/test/yup.js +++ b/test/yup.js @@ -1,6 +1,22 @@ import reach, { getIn } from '../src/util/reach'; -import { object, array, string, lazy, number, ValidationError } from '../src'; +import { + addMethod, + object, + array, + string, + lazy, + number, + boolean, + date, + ValidationError, + ObjectSchema, + ArraySchema, + StringSchema, + NumberSchema, + BoolSchema, + DateSchema, +} from '../src'; describe('Yup', function () { it('cast should not assert on undefined', () => { @@ -173,4 +189,32 @@ describe('Yup', function () { .should.be.rejected(); err.message.should.match(/must be a `number` type/); }); + + describe('addMethod', () => { + test.each([ + ['object', object], + ['array', array], + ['string', string], + ['number', number], + ['boolean', boolean], + ['date', date], + ])('should work with factories: %s', (_msg, factory) => { + addMethod(factory, 'foo', () => 'here'); + + expect(factory().foo()).to.equal('here'); + }); + + test.each([ + ['object', ObjectSchema], + ['array', ArraySchema], + ['string', StringSchema], + ['number', NumberSchema], + ['boolean', BoolSchema], + ['date', DateSchema], + ])('should work with classes: %s', (_msg, ctor) => { + addMethod(ctor, 'foo', () => 'here'); + + expect(new ctor().foo()).to.equal('here'); + }); + }); });