Comparing version 1.2.2 to 1.3.0
@@ -16,3 +16,4 @@ export declare abstract class Type<T> { | ||
path?: (string | number)[]; | ||
constructor(message: string, path?: (string | number)[]); | ||
collectedErrors?: Record<string, ValidationError | undefined>; | ||
constructor(message: string, path?: (string | number)[], collectedErrors?: Record<string, ValidationError | undefined>); | ||
} | ||
@@ -196,2 +197,3 @@ export declare type Eval<T> = T extends any[] | Date ? T : { | ||
default?: InferObjectShape<T> | (() => InferObjectShape<T>); | ||
collectErrors?: boolean; | ||
}; | ||
@@ -203,4 +205,18 @@ export declare class ObjectType<T extends ObjectShape> extends Type<InferObjectShape<T>> implements WithPredicate<InferObjectShape<T>>, Defaultable<InferObjectShape<T>> { | ||
private readonly allowUnknown; | ||
private readonly shouldCollectErrors; | ||
constructor(objectShape: T, opts?: ObjectOptions<T>); | ||
parse(value?: unknown, parseOpts?: ObjectOptions<any> & PathOptions): InferObjectShape<T>; | ||
private buildPathError; | ||
private parseObject; | ||
private parseObjectCollect; | ||
private parseObjectConv; | ||
private parseObjectConvCollect; | ||
private parseRecord; | ||
private parseRecordCollect; | ||
private parseRecordConv; | ||
private parseRecordConvCollect; | ||
private parseMixRecord; | ||
private parseMixRecordCollect; | ||
private parseMixRecordConv; | ||
private parseMixRecordConvCollect; | ||
and<K extends AnyType>(schema: K): IntersectionResult<this, K>; | ||
@@ -230,2 +246,3 @@ pick<K extends T extends { | ||
default(value: InferObjectShape<T> | (() => InferObjectShape<T>)): ObjectType<T>; | ||
collectErrors(value?: boolean): ObjectType<T>; | ||
} | ||
@@ -232,0 +249,0 @@ export declare type ArrayOptions<T extends AnyType> = { |
@@ -24,3 +24,3 @@ "use strict"; | ||
try { | ||
return this.parse(value); | ||
return this.parse.apply(this, arguments); | ||
} | ||
@@ -40,6 +40,13 @@ catch (err) { | ||
class ValidationError extends Error { | ||
constructor(message, path) { | ||
// @ts-ignore | ||
constructor(message, path, collectedErrors) { | ||
if (collectedErrors !== undefined) { | ||
message = Object.values(collectedErrors) | ||
.map(err => err === null || err === void 0 ? void 0 : err.message) | ||
.join('\n'); | ||
} | ||
super(message); | ||
this.name = 'MyZodError'; | ||
this.path = path; | ||
this.collectedErrors = collectedErrors; | ||
} | ||
@@ -463,2 +470,3 @@ } | ||
this.allowUnknown = (opts === null || opts === void 0 ? void 0 : opts.allowUnknown) === true; | ||
this.shouldCollectErrors = (opts === null || opts === void 0 ? void 0 : opts.collectErrors) === true; | ||
const keys = Object.keys(this.objectShape); | ||
@@ -498,53 +506,86 @@ this[exports.keySignature] = this.objectShape[exports.keySignature]; | ||
if (keys.length === 0 && keySig) { | ||
const convVal = this[exports.coercionTypeSymbol] ? {} : undefined; | ||
for (const key in value) { | ||
try { | ||
if (convVal) { | ||
convVal[key] = keySig.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
else { | ||
keySig.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
} | ||
catch (err) { | ||
const path = err.path ? [key, ...err.path] : [key]; | ||
const msg = parseOpts.suppressPathErrMsg | ||
? err.message | ||
: `error parsing object at path: "${prettyPrintPath(path)}" - ${err.message}`; | ||
throw new ValidationError(msg, path); | ||
} | ||
if (this[exports.coercionTypeSymbol] && this.shouldCollectErrors) { | ||
return this.parseRecordConvCollect(value, keySig, parseOpts); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal || value); | ||
else if (this[exports.coercionTypeSymbol]) { | ||
return this.parseRecordConv(value, keySig, parseOpts); | ||
} | ||
return convVal || value; | ||
else if (this.shouldCollectErrors) { | ||
return this.parseRecordCollect(value, keySig, parseOpts); | ||
} | ||
return this.parseRecord(value, keySig, parseOpts); | ||
} | ||
if (keySig) { | ||
const convVal = this[exports.coercionTypeSymbol] ? {} : undefined; | ||
for (const key in value) { | ||
try { | ||
if (convVal) { | ||
// @ts-ignore | ||
convVal[key] = (this.objectShape[key] || keySig).parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
else { | ||
// @ts-ignore | ||
(this.objectShape[key] || keySig).parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
if (this[exports.coercionTypeSymbol] && this.shouldCollectErrors) { | ||
return this.parseMixRecordConvCollect(value, keys, keySig, parseOpts); | ||
} | ||
else if (this[exports.coercionTypeSymbol]) { | ||
return this.parseMixRecordConv(value, keys, keySig, parseOpts); | ||
} | ||
else if (this.shouldCollectErrors) { | ||
return this.parseMixRecordCollect(value, keys, keySig, parseOpts); | ||
} | ||
return this.parseMixRecord(value, keys, keySig, parseOpts); | ||
} | ||
if (this[exports.coercionTypeSymbol] && this.shouldCollectErrors) { | ||
return this.parseObjectConvCollect(value, keys, parseOpts); | ||
} | ||
else if (this[exports.coercionTypeSymbol]) { | ||
return this.parseObjectConv(value, keys, parseOpts); | ||
} | ||
else if (this.shouldCollectErrors) { | ||
return this.parseObjectCollect(value, keys, parseOpts); | ||
} | ||
return this.parseObject(value, keys, 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); | ||
} | ||
parseObject(value, keys, parseOpts) { | ||
for (const key of keys) { | ||
try { | ||
const schema = this.objectShape[key]; | ||
if (schema instanceof UnknownType && !value.hasOwnProperty(key)) { | ||
throw new ValidationError(`expected key "${key}" of unknown type to be present on object`); | ||
} | ||
catch (err) { | ||
const path = err.path ? [key, ...err.path] : [key]; | ||
const msg = parseOpts.suppressPathErrMsg | ||
? err.message | ||
: `error parsing object at path: "${prettyPrintPath(path)}" - ${err.message}`; | ||
throw new ValidationError(msg, path); | ||
} | ||
schema.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal || value); | ||
catch (err) { | ||
throw this.buildPathError(err, key, parseOpts); | ||
} | ||
return convVal || value; | ||
} | ||
const convVal = this[exports.coercionTypeSymbol] ? (allowUnknown ? Object.assign({}, value) : {}) : undefined; | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, value); | ||
} | ||
return value; | ||
} | ||
parseObjectCollect(value, keys, parseOpts) { | ||
let hasError = false; | ||
const errs = {}; | ||
for (const key of keys) { | ||
const schema = this.objectShape[key]; | ||
if (schema instanceof UnknownType && !value.hasOwnProperty(key)) { | ||
throw new ValidationError(`expected key "${key}" of unknown type to be present on object`); | ||
} | ||
const result = schema.try(value[key], { suppressPathErrMsg: true }); | ||
if (result instanceof ValidationError) { | ||
hasError = true; | ||
errs[key] = this.buildPathError(result, key, parseOpts); | ||
} | ||
} | ||
if (hasError) { | ||
throw new ValidationError('', undefined, errs); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, value); | ||
} | ||
return value; | ||
} | ||
parseObjectConv(value, keys, parseOpts) { | ||
const convVal = {}; | ||
for (const key of keys) { | ||
try { | ||
@@ -555,22 +596,183 @@ const schema = this.objectShape[key]; | ||
} | ||
if (convVal) { | ||
convVal[key] = schema.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
else { | ||
schema.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
convVal[key] = schema.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
catch (err) { | ||
const path = err.path ? [key, ...err.path] : [key]; | ||
const msg = parseOpts.suppressPathErrMsg | ||
? err.message | ||
: `error parsing object at path: "${prettyPrintPath(path)}" - ${err.message}`; | ||
throw new ValidationError(msg, path); | ||
throw this.buildPathError(err, key, parseOpts); | ||
} | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal || value); | ||
applyPredicates(this.predicates, convVal); | ||
} | ||
return convVal || value; | ||
return convVal; | ||
} | ||
parseObjectConvCollect(value, keys, parseOpts) { | ||
const convVal = {}; | ||
const errs = {}; | ||
let hasError = false; | ||
for (const key of keys) { | ||
const schema = this.objectShape[key]; | ||
if (schema instanceof UnknownType && !value.hasOwnProperty(key)) { | ||
throw new ValidationError(`expected key "${key}" of unknown type to be present on object`); | ||
} | ||
const result = schema.try(value[key], { suppressPathErrMsg: true }); | ||
if (result instanceof ValidationError) { | ||
hasError = true; | ||
errs[key] = this.buildPathError(result, key, parseOpts); | ||
} | ||
else { | ||
convVal[key] = result; | ||
} | ||
} | ||
if (hasError) { | ||
throw new ValidationError('', undefined, errs); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal); | ||
} | ||
return convVal; | ||
} | ||
parseRecord(value, keySig, parseOpts) { | ||
for (const key in value) { | ||
try { | ||
keySig.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
catch (err) { | ||
throw this.buildPathError(err, key, parseOpts); | ||
} | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, value); | ||
} | ||
return value; | ||
} | ||
parseRecordCollect(value, keySig, parseOpts) { | ||
let hasError = false; | ||
const errs = {}; | ||
for (const key in value) { | ||
const result = keySig.try(value[key], { suppressPathErrMsg: true }); | ||
if (result instanceof ValidationError) { | ||
hasError = true; | ||
errs[key] = this.buildPathError(result, key, parseOpts); | ||
} | ||
} | ||
if (hasError) { | ||
throw new ValidationError('', undefined, errs); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, value); | ||
} | ||
return value; | ||
} | ||
parseRecordConv(value, keySig, parseOpts) { | ||
const convVal = {}; | ||
for (const key in value) { | ||
try { | ||
convVal[key] = keySig.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
catch (err) { | ||
throw this.buildPathError(err, key, parseOpts); | ||
} | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal); | ||
} | ||
return convVal; | ||
} | ||
parseRecordConvCollect(value, keySig, parseOpts) { | ||
const convVal = {}; | ||
const errs = {}; | ||
let hasError = false; | ||
for (const key in value) { | ||
const result = keySig.try(value[key], { suppressPathErrMsg: true }); | ||
if (result instanceof ValidationError) { | ||
hasError = true; | ||
errs[key] = this.buildPathError(result, key, parseOpts); | ||
} | ||
else { | ||
convVal[key] = result; | ||
} | ||
} | ||
if (hasError) { | ||
throw new ValidationError('', undefined, errs); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal); | ||
} | ||
return convVal; | ||
} | ||
parseMixRecord(value, keys, keySig, parseOpts) { | ||
for (const key of new Set(Object.keys(value).concat(keys))) { | ||
try { | ||
(this.objectShape[key] || keySig).parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
catch (err) { | ||
throw this.buildPathError(err, key, parseOpts); | ||
} | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, value); | ||
} | ||
return value; | ||
} | ||
parseMixRecordCollect(value, keys, keySig, parseOpts) { | ||
let hasError = false; | ||
const errs = {}; | ||
for (const key of new Set(Object.keys(value).concat(keys))) { | ||
const result = (this.objectShape[key] || keySig).try(value[key], { | ||
suppressPathErrMsg: true, | ||
}); | ||
if (result instanceof ValidationError) { | ||
hasError = true; | ||
errs[key] = this.buildPathError(result, key, parseOpts); | ||
} | ||
} | ||
if (hasError) { | ||
throw new ValidationError('', undefined, errs); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, value); | ||
} | ||
return value; | ||
} | ||
parseMixRecordConv(value, keys, keySig, parseOpts) { | ||
const convVal = {}; | ||
for (const key of new Set(Object.keys(value).concat(keys))) { | ||
try { | ||
convVal[key] = (this.objectShape[key] || keySig).parse(value[key], { | ||
suppressPathErrMsg: true, | ||
}); | ||
} | ||
catch (err) { | ||
throw this.buildPathError(err, key, parseOpts); | ||
} | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal); | ||
} | ||
return convVal; | ||
} | ||
parseMixRecordConvCollect(value, keys, keySig, parseOpts) { | ||
const convVal = {}; | ||
const errs = {}; | ||
let hasError = false; | ||
for (const key of new Set(Object.keys(value).concat(keys))) { | ||
const result = (this.objectShape[key] || keySig).try(value[key], { | ||
suppressPathErrMsg: true, | ||
}); | ||
if (result instanceof ValidationError) { | ||
hasError = true; | ||
errs[key] = this.buildPathError(result, key, parseOpts); | ||
} | ||
else { | ||
convVal[key] = result; | ||
} | ||
} | ||
if (hasError) { | ||
throw new ValidationError('', undefined, errs); | ||
} | ||
if (this.predicates) { | ||
applyPredicates(this.predicates, convVal); | ||
} | ||
return convVal; | ||
} | ||
and(schema) { | ||
@@ -650,2 +852,3 @@ if (schema instanceof ObjectType) { | ||
predicate: appendPredicate(this.predicates, { func: fn, errMsg }), | ||
collectErrors: this.shouldCollectErrors, | ||
}); | ||
@@ -658,4 +861,13 @@ } | ||
predicate: this.predicates || undefined, | ||
collectErrors: this.shouldCollectErrors, | ||
}); | ||
} | ||
collectErrors(value = true) { | ||
return new ObjectType(this.objectShape, { | ||
default: this.defaultValue, | ||
allowUnknown: this.allowUnknown, | ||
predicate: this.predicates || undefined, | ||
collectErrors: value, | ||
}); | ||
} | ||
} | ||
@@ -662,0 +874,0 @@ exports.ObjectType = ObjectType; |
{ | ||
"name": "myzod", | ||
"version": "1.2.2", | ||
"version": "1.3.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "./libs/index.js", |
@@ -526,2 +526,26 @@ # myzod | ||
#### object.collectErrors | ||
Object schemas have an option to collect all validation errors instead of the default throwing on first error. | ||
This is useful for form validation, but does incur a slight performance hit. | ||
```typescript | ||
const personSchema = myzod | ||
.object({ name: myzod.string(), lastName: myzod.string() }) | ||
.collectErrors(); | ||
personSchema.parse({ name: 1, lastName: 2 }) | ||
// throws an ValidationError with message: | ||
err.message = ` | ||
error parsing object at path: "name" - expected type to be string but got number | ||
error parsing object at path: "lastName" - expected type to be string but got number | ||
` | ||
err.collectedErrors = { | ||
name: ValidationError, | ||
lastName: ValidationError, | ||
} | ||
``` | ||
##### Key Signatures | ||
@@ -528,0 +552,0 @@ |
104660
1799
886