async-validator-next
v0.2.0
Published
An ESM-first, TypeScript-backed fork of [`yiminghe/async-validator`](https://github.com/yiminghe/async-validator) packaged with `tsdown`. It keeps the original API while aligning with this monorepo’s toolchain.
Readme
async-validator-next
An ESM-first, TypeScript-backed fork of yiminghe/async-validator packaged with tsdown. It keeps the original API while aligning with this monorepo’s toolchain.
Install
pnpm add async-validator-nextQuick Start
import Schema from 'async-validator-next'
const descriptor = {
name: { type: 'string', required: true },
age: {
type: 'number',
asyncValidator: (_, value) =>
new Promise((resolve, reject) => (value < 18 ? reject(new Error('too young')) : resolve())),
},
}
const validator = new Schema(descriptor)
validator.validate({ name: 'muji', age: 16 })
.then(() => { /* passed */ })
.catch(({ errors, fields }) => {
// errors: Error[]; fields: Record<string, Error[]>
})Common Rule Shapes
- Types:
string,number,boolean,method,regexp,integer,float,array,object,enum,date,url,hex,email,pattern,any. - Length/range:
len,min,max(strings, numbers, arrays). - Presence:
required,whitespace. - Structure:
fields(nested object),defaultField(array/object element rules),transform(value).
Validator Hooks
validator(rule, value, callback, source, options)Returnfalse,Error,Error[], or callcallback(error?). For pure sync, justreturn falseorthrow new Error().asyncValidator(rule, value, callback, source, options)Return a Promise or callcallback(error?).
Options
first: stop after the first rule that errors.firstFields: boolean or string[] to stop per-field.messages: deep-merged custom messages (see below).suppressWarning/suppressValidatorError: silence internal warnings or rethrows.keys: validate only specific fields.
Custom Messages
const messages = {
required: '%s required!',
types: { string: '%s must be a string' },
}
const schema = new Schema({ name: { type: 'string', required: true } })
schema.messages(messages) // deep merge
// or per-validate call:
// schema.validate(data, { messages }, cb)Nested / Array Validation
const descriptor = {
user: {
type: 'object',
required: true,
fields: {
email: { type: 'email', required: true },
},
},
tags: {
type: 'array',
defaultField: { type: 'string' },
},
}Enum / Pattern Rules
const descriptor = {
status: { type: 'enum', enum: ['open', 'closed'] },
slug: { pattern: /^[a-z0-9-]+$/ },
}First / FirstFields Example
schema.validate(data, { first: true }) // stops at first failing rule overall
schema.validate(data, { firstFields: true }) // stops per fieldSuppressing Warnings
import Schema from 'async-validator-next'
Schema.warning = () => {}
globalThis.ASYNC_VALIDATOR_NO_WARNING = 1Zod Adapter
- Install peer dep:
pnpm add zod. - Use
zodRule(zodSchema, message?)to wrap a Zod schema into async-validator:
import Schema, { zodRule } from 'async-validator-next'
import { z } from 'zod'
const schema = new Schema({
user: zodRule(
z.object({
profile: z.object({ email: z.string().email() }),
age: z.number().min(18),
}),
),
})
schema.validate({ user: { profile: { email: 'bad' }, age: 12 } })
.catch(({ errors }) => console.log(errors.map(e => e.message)))messagecan be a string or a formatter(issue, path) => string. Nested paths are appended to the field (user.profile.email).
Global Validation Overrides
Use setValidationConfig to override the built-in type validators for every schema instance (handy for company-specific URL/email/number checks):
import Schema, {
resetValidationConfig,
setValidationConfig,
} from 'async-validator-next'
setValidationConfig({
typeValidators: {
url: value => typeof value === 'string' && value.startsWith('https://internal.example'),
number: value => typeof value === 'number' && Number.isFinite(value) && value >= 0,
},
})
const schema = new Schema({
link: { type: 'url', required: true },
amount: { type: 'number' },
})
await schema.validate({ link: 'https://internal.example/docs', amount: 12 })
await schema.validate({ link: 'http://public.example/', amount: 12 }).catch(({ errors }) => {
console.error(errors[0].message) // "link is not a valid url"
})
resetValidationConfig()In tests you can keep overrides scoped by pairing setValidationConfig + resetValidationConfig in hooks:
beforeEach(() => setValidationConfig({ typeValidators: { email: v => v.endsWith('@corp.com') } }))
afterEach(() => resetValidationConfig())Instance Type Validators
Pass typeValidators to the constructor to override/extend type checks for a single validator instance. Instance validators run before the built-ins (and global config) and preserve the default error shapes/messages.
const validator = new Schema(
{ email: [{ type: 'email', required: true }] },
{
typeValidators: {
email: (rule, value) => myEmailCheck(value),
phone: (rule, value) => phoneRegex.test(value),
},
},
)When omitted, type validation falls back to the built-in rules.
Development
- Build:
pnpm -C packages/async-validator build(tsdown →dist/ESM bundle +.d.mts). - Test:
pnpm -C packages/async-validator test(Vitest). - Lint/types:
pnpm exec eslint packages/async-validator/src --ext .tsandpnpm exec tsc -p packages/async-validator/tsconfig.json --noEmit.
License
MIT (same as upstream). Original project: async-validator.
