Skip to content

Commit

Permalink
refactor: performance optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Jan 4, 2024
1 parent 4c88fa1 commit 3e35b83
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 111 deletions.
22 changes: 11 additions & 11 deletions benchmarks.md
Expand Up @@ -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
```

Expand All @@ -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
```

Expand All @@ -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
```

Expand All @@ -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
```
17 changes: 4 additions & 13 deletions benchmarks/array.ts
Expand Up @@ -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) {
Expand All @@ -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 })
17 changes: 4 additions & 13 deletions benchmarks/flat_object.ts
Expand Up @@ -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) {
Expand All @@ -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 })
17 changes: 4 additions & 13 deletions benchmarks/nested_object.ts
Expand Up @@ -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) {
Expand All @@ -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 })
12 changes: 3 additions & 9 deletions benchmarks/union.ts
Expand Up @@ -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) {
Expand All @@ -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 })
111 changes: 59 additions & 52 deletions src/vine/validator.ts
Expand Up @@ -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'
Expand Down Expand Up @@ -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
*/
Expand All @@ -71,38 +66,6 @@ export class VineValidator<
}
}

/**
* Refs computed from the compiled output
*/
#refs: Refs

/**
* Compiled validator function
*/
#validateFn: ReturnType<Compiler['compile']>

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
Expand All @@ -118,25 +81,69 @@ export class VineValidator<
* })
* ```
*/
validate(
declare validate: (
data: any,
...[options]: [undefined] extends MetaData
? [options?: ValidationOptions<MetaData> | undefined]
: [options: ValidationOptions<MetaData>]
): Promise<Infer<Schema>> {
let normalizedOptions = options || ({} as ValidationOptions<MetaData>)
if (normalizedOptions.meta && this.#metaDataValidator) {
this.#metaDataValidator(normalizedOptions.meta)
) => Promise<Infer<Schema>>

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<MetaData>
): Promise<Infer<Schema>> => {
let normalizedOptions = validateOptions ?? ({} as ValidationOptions<MetaData>)
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<MetaData>
): Promise<Infer<Schema>> => {
let normalizedOptions = validateOptions ?? ({} as ValidationOptions<MetaData>)
const meta = normalizedOptions.meta ?? {}
const errorReporter = normalizedOptions.errorReporter ?? this.errorReporter
const messagesProvider = normalizedOptions.messagesProvider ?? this.messagesProvider
return validateFn(data, meta, refs, messagesProvider, errorReporter())
}
}
}
}

0 comments on commit 3e35b83

Please sign in to comment.