"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _nanoclone = _interopRequireDefault(require("nanoclone")); var _locale = require("./locale"); var _Condition = _interopRequireDefault(require("./Condition")); var _runTests = _interopRequireDefault(require("./util/runTests")); var _createValidation = _interopRequireDefault(require("./util/createValidation")); var _printValue = _interopRequireDefault(require("./util/printValue")); var _Reference = _interopRequireDefault(require("./Reference")); var _reach = require("./util/reach"); var _toArray = _interopRequireDefault(require("./util/toArray")); var _ValidationError = _interopRequireDefault(require("./ValidationError")); var _ReferenceSet = _interopRequireDefault(require("./util/ReferenceSet")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } class BaseSchema { constructor(options) { this.deps = []; this.conditions = []; this._whitelist = new _ReferenceSet.default(); this._blacklist = new _ReferenceSet.default(); this.exclusiveTests = Object.create(null); this.tests = []; this.transforms = []; this.withMutation(() => { this.typeError(_locale.mixed.notType); }); this.type = (options == null ? void 0 : options.type) || 'mixed'; this.spec = _extends({ strip: false, strict: false, abortEarly: true, recursive: true, nullable: false, presence: 'optional' }, options == null ? void 0 : options.spec); } // TODO: remove get _type() { return this.type; } _typeCheck(_value) { return true; } clone(spec) { 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 = 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 = _extends({}, 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 = (0, _nanoclone.default)(_extends({}, this.spec, spec)); return next; } label(label) { var next = this.clone(); next.spec.label = label; return next; } meta(...args) { if (args.length === 0) return this.spec.meta; let next = this.clone(); next.spec.meta = Object.assign(next.spec.meta || {}, args[0]); return next; } // withContext(): BaseSchema< // TCast, // TContext, // TOutput // > { // return this as any; // } withMutation(fn) { let before = this._mutate; this._mutate = true; let result = fn(this); this._mutate = before; return result; } concat(schema) { 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 = _extends({}, base.spec, combined.spec); // if (combined.spec.nullable === UNSET) // mergedSpec.nullable = base.spec.nullable; // if (combined.spec.presence === UNSET) // mergedSpec.presence = base.spec.presence; combined.spec = mergedSpec; combined._typeError || (combined._typeError = base._typeError); combined._whitelistError || (combined._whitelistError = base._whitelistError); combined._blacklistError || (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; } isType(v) { if (this.spec.nullable && v === null) return true; return this._typeCheck(v); } resolve(options) { 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, options = {}) { let resolvedSchema = this.resolve(_extends({ value }, options)); let result = resolvedSchema._cast(value, options); if (value !== undefined && options.assert !== false && resolvedSchema.isType(result) !== true) { let formattedValue = (0, _printValue.default)(value); let formattedResult = (0, _printValue.default)(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; } _cast(rawValue, _options) { let value = rawValue === undefined ? rawValue : this.transforms.reduce((value, fn) => fn.call(this, value, rawValue, this), rawValue); if (value === undefined) { value = this.getDefault(); } return value; } _validate(_value, options = {}, cb) { 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, _extends({ 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); (0, _runTests.default)({ args, value, path, sync, tests: initialTests, endEarly: abortEarly }, err => { if (err) return void cb(err, value); (0, _runTests.default)({ tests: this.tests, args, path, sync, value, endEarly: abortEarly }, cb); }); } validate(value, options, maybeCb) { let schema = this.resolve(_extends({}, 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, options) { let schema = this.resolve(_extends({}, options, { value })); let result; schema._validate(value, _extends({}, options, { sync: true }), (err, value) => { if (err) throw err; result = value; }); return result; } isValid(value, options) { return this.validate(value, options).then(() => true, err => { if (_ValidationError.default.isError(err)) return false; throw err; }); } isValidSync(value, options) { try { this.validateSync(value, options); return true; } catch (err) { if (_ValidationError.default.isError(err)) return false; throw err; } } _getDefault() { let defaultValue = this.spec.default; if (defaultValue == null) { return defaultValue; } return typeof defaultValue === 'function' ? defaultValue.call(this) : (0, _nanoclone.default)(defaultValue); } getDefault(options) { let schema = this.resolve(options || {}); return schema._getDefault(); } default(def) { if (arguments.length === 0) { return this._getDefault(); } let next = this.clone({ default: def }); return next; } strict(isStrict = true) { var next = this.clone(); next.spec.strict = isStrict; return next; } _isPresent(value) { return value != null; } defined(message = _locale.mixed.defined) { return this.test({ message, name: 'defined', exclusive: true, test(value) { return value !== undefined; } }); } required(message = _locale.mixed.required) { return this.clone({ presence: 'required' }).withMutation(s => s.test({ message, name: 'required', exclusive: true, test(value) { return this.schema._isPresent(value); } })); } notRequired() { var next = this.clone({ presence: 'optional' }); next.tests = next.tests.filter(test => test.OPTIONS.name !== 'required'); return next; } nullable(isNullable = true) { var next = this.clone({ nullable: isNullable !== false }); 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. * 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(...args) { let opts; 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.mixed.default; if (typeof opts.test !== 'function') throw new TypeError('`test` is a required parameters'); let next = this.clone(); let validate = (0, _createValidation.default)(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(keys, options) { if (!Array.isArray(keys) && typeof keys !== 'string') { options = keys; keys = '.'; } let next = this.clone(); let deps = (0, _toArray.default)(keys).map(key => new _Reference.default(key)); deps.forEach(dep => { // @ts-ignore if (dep.isSibling) next.deps.push(dep.key); }); next.conditions.push(new _Condition.default(deps, options)); return next; } typeError(message) { var next = this.clone(); next._typeError = (0, _createValidation.default)({ 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, message = _locale.mixed.oneOf) { var next = this.clone(); enums.forEach(val => { next._whitelist.add(val); next._blacklist.delete(val); }); next._whitelistError = (0, _createValidation.default)({ 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, message = _locale.mixed.notOneOf) { var next = this.clone(); enums.forEach(val => { next._blacklist.add(val); next._whitelist.delete(val); }); next._blacklistError = (0, _createValidation.default)({ 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 = { 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; } } exports.default = BaseSchema; // @ts-expect-error BaseSchema.prototype.__isYupSchema__ = true; for (const method of ['validate', 'validateSync']) BaseSchema.prototype[`${method}At`] = function (path, value, options = {}) { const { parent, parentPath, schema } = (0, _reach.getIn)(this, path, value, options.context); return schema[method](parent && parent[parentPath], _extends({}, options, { parent, path })); }; for (const alias of ['equals', 'is']) BaseSchema.prototype[alias] = BaseSchema.prototype.oneOf; for (const alias of ['not', 'nope']) BaseSchema.prototype[alias] = BaseSchema.prototype.notOneOf; BaseSchema.prototype.optional = BaseSchema.prototype.notRequired;