diff --git a/benchmarks.md b/benchmarks.md index cc2d17e..111bdb3 100644 --- a/benchmarks.md +++ b/benchmarks.md @@ -21,9 +21,9 @@ node build/benchmarks/flat_object.js =============================== Benchmarking with flat object =============================== -Vine x 9,348,814 ops/sec ±1.16% (87 runs sampled) -Zod x 1,163,370 ops/sec ±0.64% (87 runs sampled) -Yup x 407,485 ops/sec ±0.65% (89 runs sampled) +Vine x 9,744,651 ops/sec ±0.65% (86 runs sampled) +Zod x 1,277,525 ops/sec ±0.70% (89 runs sampled) +Yup x 521,218 ops/sec ±0.27% (89 runs sampled) Fastest is Vine ``` @@ -42,9 +42,9 @@ node build/benchmarks/nested_object.js ================================= Benchmarking with nested object ================================= -Vine x 8,404,075 ops/sec ±0.39% (88 runs sampled) -Zod x 542,439 ops/sec ±1.00% (88 runs sampled) -Yup x 189,718 ops/sec ±0.74% (88 runs sampled) +Vine x 8,501,749 ops/sec ±0.47% (86 runs sampled) +Zod x 589,418 ops/sec ±0.17% (89 runs sampled) +Yup x 233,343 ops/sec ±0.25% (89 runs sampled) Fastest is Vine ``` @@ -63,9 +63,9 @@ node build/benchmarks/array.js ====================== Benchmarking arrays ====================== -Vine x 6,733,943 ops/sec ±0.84% (87 runs sampled) -Zod x 400,623 ops/sec ±0.75% (88 runs sampled) -Yup x 115,169 ops/sec ±0.43% (88 runs sampled) +Vine x 6,622,071 ops/sec ±0.55% (89 runs sampled) +Zod x 424,309 ops/sec ±0.15% (90 runs sampled) +Yup x 136,655 ops/sec ±0.22% (88 runs sampled) Fastest is Vine ``` @@ -86,7 +86,7 @@ node build/benchmarks/union.js ======================= Benchmarking unions ======================= -Vine x 7,809,771 ops/sec ±0.37% (87 runs sampled) -Zod x 180,376 ops/sec ±0.35% (89 runs sampled) +Vine x 8,495,306 ops/sec ±0.37% (86 runs sampled) +Zod x 317,658 ops/sec ±0.23% (88 runs sampled) Fastest is Vine ``` diff --git a/benchmarks/array.ts b/benchmarks/array.ts index f58ea73..a0bb629 100644 --- a/benchmarks/array.ts +++ b/benchmarks/array.ts @@ -63,28 +63,19 @@ suite .add('Vine', { defer: true, fn: function (deferred: any) { - vineSchema - .validate(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + vineSchema.validate(getData()).then(() => deferred.resolve()) }, }) .add('Zod', { defer: true, fn: function (deferred: any) { - zodSchema - .parseAsync(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + zodSchema.parseAsync(getData()).then(() => deferred.resolve()) }, }) .add('Yup', { defer: true, fn: function (deferred: any) { - yupSchema - .validate(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + yupSchema.validate(getData()).then(() => deferred.resolve()) }, }) .on('cycle', function (event: any) { @@ -93,4 +84,4 @@ suite .on('complete', function (this: any) { console.log('Fastest is ' + this.filter('fastest').map('name')) }) - .run({ async: false }) + .run({ async: true }) diff --git a/benchmarks/flat_object.ts b/benchmarks/flat_object.ts index e776566..e6eccfb 100644 --- a/benchmarks/flat_object.ts +++ b/benchmarks/flat_object.ts @@ -39,28 +39,19 @@ suite .add('Vine', { defer: true, fn: function (deferred: any) { - vineSchema - .validate(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + vineSchema.validate(getData()).then(() => deferred.resolve()) }, }) .add('Zod', { defer: true, fn: function (deferred: any) { - zodSchema - .parseAsync(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + zodSchema.parseAsync(getData()).then(() => deferred.resolve()) }, }) .add('Yup', { defer: true, fn: function (deferred: any) { - yupSchema - .validate(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + yupSchema.validate(getData()).then(() => deferred.resolve()) }, }) .on('cycle', function (event: any) { @@ -69,4 +60,4 @@ suite .on('complete', function (this: any) { console.log('Fastest is ' + this.filter('fastest').map('name')) }) - .run({ async: false }) + .run({ async: true }) diff --git a/benchmarks/nested_object.ts b/benchmarks/nested_object.ts index 75df394..a0b8df1 100644 --- a/benchmarks/nested_object.ts +++ b/benchmarks/nested_object.ts @@ -57,28 +57,19 @@ suite .add('Vine', { defer: true, fn: function (deferred: any) { - vineSchema - .validate(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + vineSchema.validate(getData()).then(() => deferred.resolve()) }, }) .add('Zod', { defer: true, fn: function (deferred: any) { - zodSchema - .parseAsync(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + zodSchema.parseAsync(getData()).then(() => deferred.resolve()) }, }) .add('Yup', { defer: true, fn: function (deferred: any) { - yupSchema - .validate(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + yupSchema.validate(getData()).then(() => deferred.resolve()) }, }) .on('cycle', function (event: any) { @@ -87,4 +78,4 @@ suite .on('complete', function (this: any) { console.log('Fastest is ' + this.filter('fastest').map('name')) }) - .run({ async: false }) + .run({ async: true }) diff --git a/benchmarks/union.ts b/benchmarks/union.ts index 6901bf7..42d5dcf 100644 --- a/benchmarks/union.ts +++ b/benchmarks/union.ts @@ -55,19 +55,13 @@ suite .add('Vine', { defer: true, fn: function (deferred: any) { - vineSchema - .validate(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + vineSchema.validate(getData()).then(() => deferred.resolve()) }, }) .add('Zod', { defer: true, fn: function (deferred: any) { - zodSchema - .parseAsync(getData()) - .then(() => deferred.resolve()) - .catch(console.log) + zodSchema.parseAsync(getData()).then(() => deferred.resolve()) }, }) .on('cycle', function (event: any) { @@ -76,4 +70,4 @@ suite .on('complete', function (this: any) { console.log('Fastest is ' + this.filter('fastest').map('name')) }) - .run({ async: false }) + .run({ async: true }) diff --git a/src/vine/validator.ts b/src/vine/validator.ts index d0cd607..5d813f8 100644 --- a/src/vine/validator.ts +++ b/src/vine/validator.ts @@ -8,7 +8,7 @@ */ import { Compiler, refsBuilder } from '@vinejs/compiler' -import type { MessagesProviderContact, Refs } from '@vinejs/compiler/types' +import type { MessagesProviderContact } from '@vinejs/compiler/types' import { messages } from '../defaults.js' import { OTYPE, PARSE } from '../symbols.js' @@ -42,11 +42,6 @@ export class VineValidator< */ declare [OTYPE]: Schema[typeof OTYPE] - /** - * Validator to use to validate metadata - */ - #metaDataValidator?: MetaDataValidator - /** * Messages provider to use on the validator */ @@ -71,38 +66,6 @@ export class VineValidator< } } - /** - * Refs computed from the compiled output - */ - #refs: Refs - - /** - * Compiled validator function - */ - #validateFn: ReturnType - - constructor( - schema: Schema, - options: { - convertEmptyStringsToNull: boolean - metaDataValidator?: MetaDataValidator - messagesProvider: MessagesProviderContact - errorReporter: () => ErrorReporterContract - } - ) { - const { compilerNode, refs } = this.#parse(schema) - - this.#refs = refs - this.#validateFn = new Compiler(compilerNode, { - convertEmptyStringsToNull: options.convertEmptyStringsToNull, - messages: COMPILER_ERROR_MESSAGES, - }).compile() - - this.errorReporter = options.errorReporter - this.messagesProvider = options.messagesProvider - this.#metaDataValidator = options.metaDataValidator - } - /** * Validate data against a schema. Optionally, you can share metaData with * the validator @@ -118,25 +81,69 @@ export class VineValidator< * }) * ``` */ - validate( + declare validate: ( data: any, ...[options]: [undefined] extends MetaData ? [options?: ValidationOptions | undefined] : [options: ValidationOptions] - ): Promise> { - let normalizedOptions = options || ({} as ValidationOptions) - if (normalizedOptions.meta && this.#metaDataValidator) { - this.#metaDataValidator(normalizedOptions.meta) + ) => Promise> + + constructor( + schema: Schema, + options: { + convertEmptyStringsToNull: boolean + metaDataValidator?: MetaDataValidator + messagesProvider: MessagesProviderContact + errorReporter: () => ErrorReporterContract } + ) { + /** + * Compile the schema to a re-usable function + */ + const { compilerNode, refs } = this.#parse(schema) + const metaDataValidator = options.metaDataValidator + const validateFn = new Compiler(compilerNode, { + convertEmptyStringsToNull: options.convertEmptyStringsToNull, + messages: COMPILER_ERROR_MESSAGES, + }).compile() - const errorReporter = normalizedOptions.errorReporter || this.errorReporter - const messagesProvider = normalizedOptions.messagesProvider || this.messagesProvider - return this.#validateFn( - data, - normalizedOptions.meta || {}, - this.#refs, - messagesProvider, - errorReporter() - ) + /** + * Assign error reporter and messages provider to public + * properties so that they can be overridden at the + * validator level. + */ + this.errorReporter = options.errorReporter + this.messagesProvider = options.messagesProvider + + /** + * Creating specialized functions with and without the + * metadata validator to optimize the runtime + * performance. + */ + if (metaDataValidator) { + this.validate = ( + data: any, + validateOptions?: ValidationOptions + ): Promise> => { + let normalizedOptions = validateOptions ?? ({} as ValidationOptions) + const meta = normalizedOptions.meta ?? {} + const errorReporter = normalizedOptions.errorReporter ?? this.errorReporter + const messagesProvider = normalizedOptions.messagesProvider ?? this.messagesProvider + + metaDataValidator!(meta) + return validateFn(data, meta, refs, messagesProvider, errorReporter()) + } + } else { + this.validate = ( + data: any, + validateOptions?: ValidationOptions + ): Promise> => { + let normalizedOptions = validateOptions ?? ({} as ValidationOptions) + const meta = normalizedOptions.meta ?? {} + const errorReporter = normalizedOptions.errorReporter ?? this.errorReporter + const messagesProvider = normalizedOptions.messagesProvider ?? this.messagesProvider + return validateFn(data, meta, refs, messagesProvider, errorReporter()) + } + } } }