UNPKG

myzod

Version:

Schema Validation with typescript type inference.

1,304 lines (1,303 loc) 47.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LazyType = exports.PartialType = exports.EnumType = exports.IntersectionType = exports.UnionType = exports.TupleType = exports.ArrayType = exports.ObjectType = exports.DateType = exports.NullableType = exports.OptionalType = exports.UnknownType = exports.LiteralType = exports.NullType = exports.UndefinedType = exports.BigIntType = exports.NumberType = exports.BooleanType = exports.StringType = exports.ValidationError = exports.Type = exports.keySignature = void 0; const keySignature = Symbol('keySignature'); exports.keySignature = keySignature; function shallowClone(value) { // create a new object with the same prototype then copy all enumerable own properties return Object.assign(Object.create(Object.getPrototypeOf(value)), value); } const typeErrSym = Symbol('typeError'); const coercionTypeSymbol = Symbol('coercion'); class Type { constructor() { } or(schema) { return new UnionType([this, schema]); } optional() { if (this instanceof OptionalType) { return shallowClone(this); } return new OptionalType(this); } nullable() { if (this instanceof NullableType) { return shallowClone(this); } return new NullableType(this); } try(value) { try { return this.parse.apply(this, arguments); } catch (err) { return err; } } map(fn) { return new MTypeClass(this, fn); } onTypeError(msg) { const cpy = shallowClone(this); cpy[typeErrSym] = msg; return cpy; } typeError(msg) { const errMsg = (() => { const typErrValue = this[typeErrSym]; if (typErrValue === undefined) { return msg; } if (typeof typErrValue === 'function') { return typErrValue(); } return typErrValue; })(); return new ValidationError(errMsg); } } exports.Type = Type; class MTypeClass extends Type { constructor(schema, mapFn) { super(); this.schema = schema; this.mapFn = mapFn; this.predicates = null; this[coercionTypeSymbol] = true; } parse(value) { const ret = value === undefined && this.defaultValue ? typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue : this.mapFn(this.schema.parse(value)); if (this.predicates) { applyPredicates(this.predicates, ret); } return ret; } and(other) { throw new Error('mapped types cannot be intersected'); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { return withDefault(this, value); } } function flattenCollectedErrorsTreeIntoMessages(collectedErrors, path = []) { const messages = []; for (const [key, value] of Object.entries(collectedErrors)) { if (value === undefined) { continue; } const newPath = [...path, key]; if (value.collectedErrors) { messages.push(...flattenCollectedErrorsTreeIntoMessages(value.collectedErrors, newPath)); } else { messages.push(`error parsing object at path: "${prettyPrintPath(newPath)}" - ${value.message}`); } } return messages; } class ValidationError extends Error { // @ts-ignore constructor(message, path, collectedErrors) { if (collectedErrors !== undefined) { message = flattenCollectedErrorsTreeIntoMessages(collectedErrors).join('\n'); } super(message); this.name = 'MyZodError'; this.path = path; this.collectedErrors = collectedErrors; } } exports.ValidationError = ValidationError; function typeOf(value) { if (value === null) { return 'null'; } if (Array.isArray(value)) { return 'array'; } return typeof value; } function prettyPrintPath(path) { return path.reduce((acc, elem, idx) => { if (typeof elem === 'number') { acc += `[${elem}]`; } else if (idx === 0) { acc += elem; } else { acc += '.' + elem; } return acc; }, ''); } const allowUnknownSymbol = Symbol('allowUnknown'); const shapekeysSymbol = Symbol('shapeKeys'); const normalizePredicates = (predicate) => { if (!predicate) { return null; } if (typeof predicate === 'function') { return [{ func: predicate }]; } if (Array.isArray(predicate)) { return predicate; } return [predicate]; }; const applyPredicates = (predicates, value) => { try { for (const predicate of predicates) { if (!predicate.func(value)) { throw new ValidationError(predicate.errMsg ? typeof predicate.errMsg === 'function' ? predicate.errMsg(value) : predicate.errMsg : 'failed anonymous predicate function'); } } } catch (err) { if (err instanceof ValidationError) { throw err; } throw new ValidationError(err.message); } }; const appendPredicate = (predicates, pred) => { if (!predicates) { return [pred]; } return [...predicates, pred]; }; const withPredicate = (schema, predicate) => { const cpy = shallowClone(schema); cpy.predicates = appendPredicate(cpy.predicates, predicate); return cpy; }; const withDefault = (schema, value) => { const cpy = shallowClone(schema); cpy[coercionTypeSymbol] = true; cpy.defaultValue = value; return cpy; }; class StringType extends Type { constructor(opts) { super(); this.predicates = normalizePredicates(opts === null || opts === void 0 ? void 0 : opts.predicate); this.defaultValue = opts === null || opts === void 0 ? void 0 : opts.default; this[coercionTypeSymbol] = (opts === null || opts === void 0 ? void 0 : opts.default) !== undefined; let self = this; if (typeof (opts === null || opts === void 0 ? void 0 : opts.min) !== 'undefined') { self = self.min(opts.min); } if (typeof (opts === null || opts === void 0 ? void 0 : opts.max) !== 'undefined') { self = self.max(opts.max); } if (typeof (opts === null || opts === void 0 ? void 0 : opts.pattern) !== 'undefined') { self = self.pattern(opts.pattern); } if (opts === null || opts === void 0 ? void 0 : opts.valid) { self = self.valid(opts.valid); } return self; } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { if (typeof value !== 'string') { throw this.typeError('expected type to be string but got ' + typeOf(value)); } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } and(schema) { return new IntersectionType(this, schema); } pattern(regexp, errMsg) { return this.withPredicate(value => regexp.test(value), errMsg || `expected string to match pattern ${regexp} but did not`); } min(x, errMsg) { return this.withPredicate((value) => value.length >= x, errMsg || ((value) => `expected string to have length greater than or equal to ${x} but had length ${value.length}`)); } max(x, errMsg) { return this.withPredicate((value) => value.length <= x, errMsg || ((value) => `expected string to have length less than or equal to ${x} but had length ${value.length}`)); } valid(list, errMsg) { return this.withPredicate((value) => list.includes(value), errMsg || `expected string to be one of: ${JSON.stringify(list)}`); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { return withDefault(this, value); } } exports.StringType = StringType; class BooleanType extends Type { constructor(defaultValue) { super(); this.defaultValue = defaultValue; this[coercionTypeSymbol] = defaultValue !== undefined; } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { if (typeof value !== 'boolean') { throw this.typeError('expected type to be boolean but got ' + typeOf(value)); } return value; } and(schema) { return new IntersectionType(this, schema); } default(value) { return withDefault(this, value); } } exports.BooleanType = BooleanType; class NumberType extends Type { constructor(opts = {}) { super(); this.coerceFlag = opts.coerce; this.predicates = normalizePredicates(opts.predicate); this.defaultValue = opts.default; this[coercionTypeSymbol] = !!opts.coerce || opts.default !== undefined; let self = this; if (typeof opts.max !== 'undefined') { self = self.max(opts.max); } if (typeof opts.min !== 'undefined') { self = self.min(opts.min); } return self; } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { if (this.coerceFlag && typeof value === 'string') { const number = parseFloat(value); if (isNaN(number)) { throw this.typeError('expected type to be number but got string'); } return this.parse(number); } if (typeof value !== 'number') { throw this.typeError('expected type to be number but got ' + typeOf(value)); } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } and(schema) { return new IntersectionType(this, schema); } min(x, errMsg) { return this.withPredicate(value => value >= x, errMsg || (value => `expected number to be greater than or equal to ${x} but got ${value}`)); } max(x, errMsg) { return this.withPredicate(value => value <= x, errMsg || (value => `expected number to be less than or equal to ${x} but got ${value}`)); } coerce(value) { return new NumberType({ predicate: this.predicates || undefined, coerce: value !== undefined ? value : true, default: this.defaultValue, }); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { return withDefault(this, value); } } exports.NumberType = NumberType; class BigIntType extends Type { constructor(opts = {}) { super(); this[coercionTypeSymbol] = true; this.predicates = normalizePredicates(opts.predicate); this.defaultValue = opts.default; } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { try { const int = BigInt(value); if (this.predicates) { applyPredicates(this.predicates, int); } return int; } catch (err) { if (err instanceof ValidationError) { throw err; } throw this.typeError('expected type to be bigint interpretable - ' + err.message.toLowerCase()); } } and(schema) { return new IntersectionType(this, schema); } min(x, errMsg) { return this.withPredicate(value => value >= x, errMsg || (value => `expected bigint to be greater than or equal to ${x} but got ${value}`)); } max(x, errMsg) { return this.withPredicate(value => value <= x, errMsg || (value => `expected bigint to be less than or equal to ${x} but got ${value}`)); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { return withDefault(this, value); } } exports.BigIntType = BigIntType; class UndefinedType extends Type { parse(value) { if (value !== undefined) { throw this.typeError('expected type to be undefined but got ' + typeOf(value)); } return value; } and(schema) { return new IntersectionType(this, schema); } } exports.UndefinedType = UndefinedType; class NullType extends Type { constructor() { super(); } parse(value = this.defaultValue) { if (value !== null) { throw this.typeError('expected type to be null but got ' + typeOf(value)); } return value; } and(schema) { return new IntersectionType(this, schema); } default() { return withDefault(this, null); } } exports.NullType = NullType; class LiteralType extends Type { constructor(literal) { super(); this.literal = literal; } parse(value = this.defaultValue) { if (value !== this.literal) { const typeofValue = typeof value !== 'object' ? JSON.stringify(value) : typeOf(value); throw this.typeError(`expected value to be literal ${JSON.stringify(this.literal)} but got ${typeofValue}`); } return value; } and(schema) { return new IntersectionType(this, schema); } default() { return withDefault(this, this.literal); } } exports.LiteralType = LiteralType; class UnknownType extends Type { constructor() { super(); } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { return value; } and(schema) { return new IntersectionType(this, schema); } default(value) { return withDefault(this, value); } } exports.UnknownType = UnknownType; class OptionalType extends Type { constructor(schema) { super(); this.schema = schema; this[coercionTypeSymbol] = this.schema[coercionTypeSymbol]; this[shapekeysSymbol] = this.schema[shapekeysSymbol]; this[allowUnknownSymbol] = this.schema[allowUnknownSymbol]; } parse(value, opts) { if (value === undefined) { return undefined; } //@ts-ignore return this.schema.parse(value, opts); } required() { return shallowClone(this.schema); } and(schema) { return new IntersectionType(this, schema); } } exports.OptionalType = OptionalType; class NullableType extends Type { constructor(schema) { super(); this.schema = schema; this[coercionTypeSymbol] = this.schema[coercionTypeSymbol]; this[shapekeysSymbol] = this.schema[shapekeysSymbol]; this[allowUnknownSymbol] = this.schema[allowUnknownSymbol]; } parse( //@ts-ignore value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { if (value === null) { return null; } return this.schema.parse(value); } and(schema) { return new IntersectionType(this, schema); } required() { return shallowClone(this.schema); } default(value) { return withDefault(this, value); } } exports.NullableType = NullableType; class DateType extends Type { constructor(opts) { super(); this[coercionTypeSymbol] = true; this.predicates = normalizePredicates(opts === null || opts === void 0 ? void 0 : opts.predicate); this.defaultValue = opts === null || opts === void 0 ? void 0 : opts.default; } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { const date = typeof value === 'string' ? this.stringToDate(value) : this.assertDate(value); if (this.predicates) { applyPredicates(this.predicates, date); } return date; } and(schema) { return new IntersectionType(this, schema); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { return withDefault(this, value); } stringToDate(str) { const date = new Date(str); if (isNaN(date.getTime())) { throw this.typeError(`expected date string to be valid date`); } return date; } assertDate(date) { if (!(date instanceof Date)) { throw this.typeError('expected type Date but got ' + typeOf(date)); } return date; } } exports.DateType = DateType; class ObjectType extends Type { constructor(objectShape, opts) { super(); this.objectShape = objectShape; this.predicates = normalizePredicates(opts === null || opts === void 0 ? void 0 : opts.predicate); this.defaultValue = opts === null || opts === void 0 ? void 0 : opts.default; this.shouldCollectErrors = (opts === null || opts === void 0 ? void 0 : opts.collectErrors) === true; const keys = Object.keys(this.objectShape); this[keySignature] = this.objectShape[keySignature]; this[allowUnknownSymbol] = (opts === null || opts === void 0 ? void 0 : opts.allowUnknown) === true; this[shapekeysSymbol] = keys; this[coercionTypeSymbol] = this.defaultValue !== undefined || this[allowUnknownSymbol] || Object.values(this.objectShape).some(schema => schema[coercionTypeSymbol]) || !!(this.objectShape[keySignature] && this.objectShape[keySignature][coercionTypeSymbol]); this._parse = this.selectParser(); } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue, parseOpts = {}) { if (typeof value !== 'object' || value === null || Array.isArray(value)) { throw this.typeError('expected type to be object but got ' + typeOf(value)); } const keys = this[shapekeysSymbol]; const allowUnknown = parseOpts.allowUnknown || this[allowUnknownSymbol]; if (!allowUnknown && !this.objectShape[keySignature]) { const illegalKeys = []; for (const k in value) { if (!keys.includes(k)) { illegalKeys.push(k); } } if (illegalKeys.length > 0) { throw this.typeError('unexpected keys on object: ' + JSON.stringify(illegalKeys)); } } return this._parse(value, parseOpts); } buildPathError(err, key, parseOpts) { const path = err.path ? [key, ...err.path] : [key]; const msg = parseOpts.suppressPathErrMsg ? err.message : `error parsing object at path: "${prettyPrintPath(path)}" - ${err.message}`; return new ValidationError(msg, path, err.collectedErrors); } selectParser() { if (this[shapekeysSymbol].length === 0 && this[keySignature]) { if (this[coercionTypeSymbol] && this.shouldCollectErrors) { return this.parseRecordConvCollect; } if (this[coercionTypeSymbol]) { return this.parseRecordConv; } if (this.shouldCollectErrors) { return this.parseRecordCollect; } return this.parseRecord; } if (this[keySignature]) { if (this[coercionTypeSymbol] && this.shouldCollectErrors) { return this.parseMixRecordConvCollect; } if (this[coercionTypeSymbol]) { return this.parseMixRecordConv; } if (this.shouldCollectErrors) { return this.parseMixRecordCollect; } return this.parseMixRecord; } if (this[coercionTypeSymbol] && this.shouldCollectErrors) { return this.parseObjectConvCollect; } if (this[coercionTypeSymbol]) { return this.parseObjectConv; } if (this.shouldCollectErrors) { return this.parseObjectCollect; } return this.parseObject; } parseObject(value, parseOpts) { for (const key of this[shapekeysSymbol]) { try { const schema = this.objectShape[key]; if (schema instanceof UnknownType && !value.hasOwnProperty(key)) { throw schema.typeError(`expected key "${key}" of unknown type to be present on object`); } schema.parse(value[key], { suppressPathErrMsg: true }); } catch (err) { throw this.buildPathError(err, key, parseOpts); } } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } parseObjectCollect(value, parseOpts) { let hasError = false; const errs = {}; for (const key of this[shapekeysSymbol]) { const schema = this.objectShape[key]; if (schema instanceof UnknownType && !value.hasOwnProperty(key)) { hasError = true; errs[key] = this.buildPathError(schema.typeError(`expected key "${key}" of unknown type to be present on object`), key, { suppressPathErrMsg: true }); continue; } const result = schema.try(value[key], { suppressPathErrMsg: true }); if (result instanceof ValidationError) { hasError = true; errs[key] = this.buildPathError(result, key, { suppressPathErrMsg: true }); } } if (hasError) { throw new ValidationError('', undefined, errs); } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } parseObjectConv(value, parseOpts) { const convVal = {}; for (const key of this[shapekeysSymbol]) { try { const schema = this.objectShape[key]; if (schema instanceof UnknownType && !value.hasOwnProperty(key)) { throw schema.typeError(`expected key "${key}" of unknown type to be present on object`); } convVal[key] = schema.parse(value[key], { suppressPathErrMsg: true }); } catch (err) { throw this.buildPathError(err, key, parseOpts); } } if (this.predicates) { applyPredicates(this.predicates, convVal); } return convVal; } parseObjectConvCollect(value, parseOpts) { const convVal = {}; const errs = {}; let hasError = false; for (const key of this[shapekeysSymbol]) { const schema = this.objectShape[key]; if (schema instanceof UnknownType && !value.hasOwnProperty(key)) { hasError = true; errs[key] = this.buildPathError(schema.typeError(`expected key "${key}" of unknown type to be present on object`), key, { suppressPathErrMsg: true }); continue; } const result = schema.try(value[key], { suppressPathErrMsg: true }); if (result instanceof ValidationError) { hasError = true; errs[key] = this.buildPathError(result, key, { suppressPathErrMsg: true }); } else { convVal[key] = result; } } if (hasError) { throw new ValidationError('', undefined, errs); } if (this.predicates) { applyPredicates(this.predicates, convVal); } return convVal; } parseRecord(value, parseOpts) { for (const key in value) { try { this[keySignature].parse(value[key], { suppressPathErrMsg: true }); } catch (err) { throw this.buildPathError(err, key, parseOpts); } } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } parseRecordCollect(value, parseOpts) { let hasError = false; const errs = {}; for (const key in value) { const result = this[keySignature].try(value[key], { suppressPathErrMsg: true }); if (result instanceof ValidationError) { hasError = true; errs[key] = this.buildPathError(result, key, { suppressPathErrMsg: true }); } } if (hasError) { throw new ValidationError('', undefined, errs); } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } parseRecordConv(value, parseOpts) { const convVal = {}; for (const key in value) { try { convVal[key] = this[keySignature].parse(value[key], { suppressPathErrMsg: true }); } catch (err) { throw this.buildPathError(err, key, parseOpts); } } if (this.predicates) { applyPredicates(this.predicates, convVal); } return convVal; } parseRecordConvCollect(value, parseOpts) { const convVal = {}; const errs = {}; let hasError = false; for (const key in value) { const result = this[keySignature].try(value[key], { suppressPathErrMsg: true }); if (result instanceof ValidationError) { hasError = true; errs[key] = this.buildPathError(result, key, { suppressPathErrMsg: true }); } else { convVal[key] = result; } } if (hasError) { throw new ValidationError('', undefined, errs); } if (this.predicates) { applyPredicates(this.predicates, convVal); } return convVal; } parseMixRecord(value, parseOpts) { for (const key of new Set(Object.keys(value).concat(this[shapekeysSymbol]))) { try { (this.objectShape[key] || this[keySignature]).parse(value[key], { suppressPathErrMsg: true }); } catch (err) { throw this.buildPathError(err, key, parseOpts); } } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } parseMixRecordCollect(value, parseOpts) { let hasError = false; const errs = {}; for (const key of new Set(Object.keys(value).concat(this[shapekeysSymbol]))) { const result = (this.objectShape[key] || this[keySignature]).try(value[key], { suppressPathErrMsg: true, }); if (result instanceof ValidationError) { hasError = true; errs[key] = this.buildPathError(result, key, { suppressPathErrMsg: true }); } } if (hasError) { throw new ValidationError('', undefined, errs); } if (this.predicates) { applyPredicates(this.predicates, value); } return value; } parseMixRecordConv(value, parseOpts) { const convVal = {}; for (const key of new Set(Object.keys(value).concat(this[shapekeysSymbol]))) { try { convVal[key] = (this.objectShape[key] || this[keySignature]).parse(value[key], { suppressPathErrMsg: true, }); } catch (err) { throw this.buildPathError(err, key, parseOpts); } } if (this.predicates) { applyPredicates(this.predicates, convVal); } return convVal; } parseMixRecordConvCollect(value, parseOpts) { const convVal = {}; const errs = {}; let hasError = false; for (const key of new Set(Object.keys(value).concat(this[shapekeysSymbol]))) { const result = (this.objectShape[key] || this[keySignature]).try(value[key], { suppressPathErrMsg: true, }); if (result instanceof ValidationError) { hasError = true; errs[key] = this.buildPathError(result, key, { suppressPathErrMsg: true }); } else { convVal[key] = result; } } if (hasError) { throw new ValidationError('', undefined, errs); } if (this.predicates) { applyPredicates(this.predicates, convVal); } return convVal; } and(schema) { if (schema instanceof ObjectType) { const keySet = new Set([...this[shapekeysSymbol], ...schema[shapekeysSymbol]]); const intersectShape = Array.from(keySet).reduce((acc, key) => { if (this.objectShape[key] && schema.objectShape[key]) { acc[key] = this.objectShape[key].and(schema.objectShape[key]); } else if (this.objectShape[key]) { acc[key] = this.objectShape[key]; } else { acc[key] = schema.objectShape[key]; } return acc; }, {}); const selfKeySig = this.objectShape[keySignature]; const targetKeySig = schema[keySignature]; if (selfKeySig && targetKeySig) { intersectShape[keySignature] = selfKeySig.and(targetKeySig); } else if (selfKeySig || targetKeySig) { intersectShape[keySignature] = selfKeySig || targetKeySig; } return new ObjectType(intersectShape); } return new IntersectionType(this, schema); } pick(keys, opts) { const pickedShape = keys.reduce((acc, key) => { if (this.objectShape[key] || this.objectShape[keySignature]) { acc[key] = this.objectShape[key] || this.objectShape[keySignature]; } return acc; }, {}); return new ObjectType(pickedShape, opts); } omit(keys, opts) { const pickedKeys = this[shapekeysSymbol].filter((x) => !keys.includes(x)); if (!this[keySignature]) { return this.pick(pickedKeys, opts); } return this.pick(pickedKeys, opts).and(new ObjectType({ [keySignature]: this[keySignature] })); } partial(opts) { const originalShape = this.objectShape; const shape = Object.keys(originalShape).reduce((acc, key) => { if (opts === null || opts === void 0 ? void 0 : opts.deep) { acc[key] = toPartialSchema(originalShape[key], opts).optional(); } else { acc[key] = originalShape[key].optional(); } return acc; }, {}); const keysig = originalShape[keySignature]; if (keysig) { if (opts === null || opts === void 0 ? void 0 : opts.deep) { shape[keySignature] = toPartialSchema(keysig, opts).optional(); } else { shape[keySignature] = keysig.optional(); } } // Do not transfer predicates or default value to new object shape as this would not be type-safe return new ObjectType(shape, { allowUnknown: this[allowUnknownSymbol] }); } shape() { return Object.assign({}, this.objectShape); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { const cpy = withDefault(this, value); cpy._parse = cpy.selectParser(); return cpy; } collectErrors(value = true) { const cpy = shallowClone(this); cpy.shouldCollectErrors = value; cpy._parse = cpy.selectParser(); return cpy; } allowUnknownKeys(value = true) { const cpy = shallowClone(this); cpy[allowUnknownSymbol] = value; cpy[coercionTypeSymbol] = cpy[coercionTypeSymbol] || value; cpy._parse = cpy.selectParser(); return cpy; } } exports.ObjectType = ObjectType; class ArrayType extends Type { constructor(schema, opts = {}) { super(); this.schema = schema; this.predicates = normalizePredicates(opts.predicate); this.defaultValue = opts.default; this.coerceFn = opts.coerce; this[coercionTypeSymbol] = typeof this.coerceFn === 'function' || this.defaultValue !== undefined || this.schema[coercionTypeSymbol]; this._parse = this.schema instanceof ObjectType || this.schema instanceof ArrayType || this.schema instanceof LazyType ? (elem, parseOptions) => this.schema.parse(elem, { allowUnknown: parseOptions === null || parseOptions === void 0 ? void 0 : parseOptions.allowUnknown, suppressPathErrMsg: true, }) : (elem) => this.schema.parse(elem); let self = this; if (typeof opts.length !== 'undefined') { self = self.length(opts.length); } if (typeof opts.min !== 'undefined') { self = self.min(opts.min); } if (typeof opts.max !== 'undefined') { self = self.max(opts.max); } if (opts.unique === true) { self = self.unique(); } return self; } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue, parseOptions) { if (typeof value === 'string' && typeof this.coerceFn === 'function' && !(parseOptions === null || parseOptions === void 0 ? void 0 : parseOptions.coerced)) { try { return this.parse(this.coerceFn(value), Object.assign(Object.assign({}, parseOptions), { coerced: true })); } catch (e) { if (e instanceof ValidationError) { throw e; } throw new ValidationError('error coercing string value to array - ' + e.message); } } if (!Array.isArray(value)) { throw this.typeError('expected an array but got ' + typeOf(value)); } const convValue = this[coercionTypeSymbol] ? [] : undefined; for (let i = 0; i < value.length; i++) { try { if (convValue) { convValue[i] = this._parse(value[i]); } else { this._parse(value[i], parseOptions); } } catch (err) { const path = err.path ? [i, ...err.path] : [i]; const msg = (parseOptions === null || parseOptions === void 0 ? void 0 : parseOptions.suppressPathErrMsg) ? err.message : `error at ${prettyPrintPath(path)} - ${err.message}`; throw new ValidationError(msg, path); } } if (this.predicates) { applyPredicates(this.predicates, convValue || value); } return convValue || value; } length(value, errMsg) { return this.withPredicate(arr => arr.length === value, errMsg || (arr => `expected array to have length ${value} but got ${arr.length}`)); } min(value, errMsg) { return this.withPredicate(arr => arr.length >= value, errMsg || (arr => `expected array to have length greater than or equal to ${value} but got ${arr.length}`)); } max(value, errMsg) { return this.withPredicate(arr => arr.length <= value, errMsg || (arr => `expected array to have length less than or equal to ${value} but got ${arr.length}`)); } unique() { return this.withPredicate(arr => { const seenMap = new Map(); arr.forEach((elem, idx) => { const seenAt = seenMap.get(elem); if (seenAt) { throw new ValidationError(`expected array to be unique but found same element at indexes ${seenAt[0]} and ${idx}`); } seenMap.set(elem, [idx]); }); return true; }); } and(schema) { if (schema instanceof ArrayType) { return new ArrayType(this.schema.and(schema.schema)); } return new IntersectionType(this, schema); } coerce(fn) { return new ArrayType(this.schema, { default: this.defaultValue, coerce: fn, predicate: this.predicates || undefined, }); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { return withDefault(this, value); } } exports.ArrayType = ArrayType; class TupleType extends Type { constructor(schemas, opts) { super(); this.schemas = schemas; this.predicates = normalizePredicates(opts === null || opts === void 0 ? void 0 : opts.predicate); this.defaultValue = opts === null || opts === void 0 ? void 0 : opts.default; this[coercionTypeSymbol] = this.defaultValue !== undefined || schemas.some(schema => schema[coercionTypeSymbol]); } parse(value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { if (!Array.isArray(value)) { throw this.typeError('expected tuple value to be type array but got ' + typeOf(value)); } if (value.length !== this.schemas.length) { throw this.typeError(`expected tuple length to be ${this.schemas.length} but got ${value.length}`); } const convValue = this[coercionTypeSymbol] ? [] : undefined; for (let i = 0; i < this.schemas.length; i++) { try { if (convValue) { convValue.push(this.schemas[i].parse(value[i])); } else { this.schemas[i].parse(value[i]); } } catch (err) { throw new ValidationError(`error parsing tuple at index ${i}: ${err.message}`); } } if (this.predicates) { applyPredicates(this.predicates, convValue || value); } return convValue || value; } and(schema) { if (schema instanceof TupleType) { const otherSchemaArray = schema.schemas; const nextSchemasArray = []; for (let i = 0; i < Math.max(this.schemas.length, otherSchemaArray.length); i++) { const current = this.schemas[i]; const other = otherSchemaArray[i]; if (current && other) { nextSchemasArray.push(current.and(other)); } else if (current) { nextSchemasArray.push(current); } else { nextSchemasArray.push(other); } } return new TupleType(nextSchemasArray); } return new IntersectionType(this, schema); } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } default(value) { return withDefault(this, value); } } exports.TupleType = TupleType; class UnionType extends Type { constructor(schemas, opts) { super(); this.schemas = schemas; this.strict = (opts === null || opts === void 0 ? void 0 : opts.strict) !== false; this.defaultValue = opts === null || opts === void 0 ? void 0 : opts.default; this[coercionTypeSymbol] = (opts === null || opts === void 0 ? void 0 : opts.default) !== undefined || schemas.some(schema => schema[coercionTypeSymbol]); } parse( //@ts-ignore value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { const errors = new Set(); for (const schema of this.schemas) { try { if (this.strict === false && schema instanceof ObjectType) { return schema.parse(value, { allowUnknown: true }); } return schema.parse(value); } catch (err) { errors.add(err.message); } } const messages = Array.from(errors); if (messages.length === 1) { throw this.typeError(messages[0]); } throw this.typeError('No union satisfied:\n ' + messages.join('\n ')); } and(schema) { const schemaIntersections = this.schemas.map(x => x.and(schema)); return new UnionType(schemaIntersections, { strict: this.strict }); } default(value) { return withDefault(this, value); } } exports.UnionType = UnionType; function asUnionType(schema) { if (schema instanceof UnionType) { return schema; } if (schema instanceof IntersectionType && schema._schema instanceof UnionType) { return schema._schema; } return null; } class IntersectionType extends Type { constructor(left, right) { super(); this.left = left; this.right = right; this[coercionTypeSymbol] = this.left[coercionTypeSymbol] && this.right[coercionTypeSymbol]; // if (this[coercionTypeSymbol] && Object.getPrototypeOf(this.left) !== Object.getPrototypeOf(this.right)) { // } this[allowUnknownSymbol] = !!(this.left[allowUnknownSymbol] || this.right[allowUnknownSymbol]); if (this.left[shapekeysSymbol] && this.right[shapekeysSymbol]) { //@ts-ignore this[shapekeysSymbol] = Array.from(new Set([...this.left[shapekeysSymbol], ...this.right[shapekeysSymbol]])); } this._schema = (() => { if (this.left instanceof MTypeClass) { this.left.and(this.right); // throw error } if (this.right instanceof MTypeClass) { this.right.and(this.left); // throw err } const leftUnion = asUnionType(this.left); if (leftUnion) { return leftUnion.and(this.right); } const rightUnion = asUnionType(this.right); if (rightUnion) { return rightUnion.and(this.left); } if (this.left instanceof PartialType) { return new IntersectionType(this.left.schema, this.right); } if (this.right instanceof PartialType) { return new IntersectionType(this.left, this.right.schema); } return null; })(); } parse(value, opts) { const allowUnknown = (opts === null || opts === void 0 ? void 0 : opts.allowUnknown) || this[allowUnknownSymbol]; if (!allowUnknown && this[shapekeysSymbol]) { const expectedShapeKeys = this[shapekeysSymbol]; const invalidKeys = Object.keys(value).filter((key) => !expectedShapeKeys.includes(key)); if (invalidKeys.length > 0) { throw this.typeError('unexpected keys on object ' + JSON.stringify(invalidKeys)); } } if (this._schema) { // @ts-ignore return this._schema.parse(value, opts); } this.left.parse(value); this.right.parse(value); return value; } and(schema) { return new IntersectionType(this, schema); } } exports.IntersectionType = IntersectionType; class EnumType extends Type { constructor(enumeration, opts = {}) { super(); this.enumeration = enumeration; this.values = Object.values(enumeration); this.predicates = null; this.coerceOpt = opts.coerce; this.defaultValue = opts.defaultValue; this[coercionTypeSymbol] = this.defaultValue !== undefined || this.coerceOpt !== undefined; } parse( //@ts-ignore value = typeof this.defaultValue === 'function' ? this.defaultValue() : this.defaultValue) { let coercedValue = value; if (typeof value === 'string' && this.coerceOpt === 'lower') { coercedValue = value.toLowerCase(); } else if (typeof value === 'string' && this.coerceOpt === 'upper') { coercedValue = value.toUpperCase(); } if (!this.values.includes(coercedValue)) { throw this.typeError(`error ${JSON.stringify(value)} not part of enum values`); } if (this.predicates) { applyPredicates(this.predicates, coercedValue); } return coercedValue; } check(value) { return this.values.includes(value); } and(schema) { return new IntersectionType(this, schema); } default(value) { return withDefault(this, value); } coerce(opt) { return new EnumType(this.enumeration, { defaultValue: this.defaultValue, coerce: opt }); } enum() { return this.enumeration; } withPredicate(fn, errMsg) { return withPredicate(this, { func: fn, errMsg }); } } exports.EnumType = EnumType; function toPartialSchema(schema, opts) { if (schema instanceof ObjectType) { return schema.partial({ deep: (opts === null || opts === void 0 ? void 0 : opts.deep) || false }); } if (schema instanceof IntersectionType) { return new IntersectionType(toPartialSchema(schema.left, opts), toPartialSchema(schema.right, opts)); } if (schema instanceof UnionType) { return new UnionType(schema.schemas.map((schema) => toPartialSchema(schema, opts))); } if (schema instanceof ArrayType) { if (opts === null || opts === void 0 ? void 0 : opts.deep) { return new ArrayType(toPartialSchema(schema.schema, opts).optional()); } return new ArrayType(schema.schema.optional()); } return schema; } class PartialType extends Type { constructor(schema, opts) { super(); this.schema = toPartialSchema(schema, opts); this[coercionTypeSymbol] = this.schema[coercionTypeSymbol]; } parse(value) { return this.schema.parse(value); } and(schema) { return new IntersectionType(this, schema); } } exports.PartialType = PartialType; // @ts-ignore class LazyType extends Type { constructor(fn) { super(); this.fn = fn; // Since we can't know what the schema is we can't assume its not a coercionType and we need to disable the optimization this[coercionTypeSymbol] = true; } parse(value, opts) { const schema = this.fn(); if ((opts === null || opts === void 0 ? void 0 : opts.suppressPathErrMsg) && schema instanceof ObjectType) { return schema.parse(value, opts); } return schema.parse(value); } and(schema) { return new IntersectionType(this, schema); } } exports.LazyType = LazyType;