Custom keywords for ata-validator. Adds instanceof and typeof checks that are not part of the JSON Schema spec.
Similar to ajv-keywords for ajv.
npm install @ata-project/keywordsconst { Validator } = require('ata-validator')
const { withKeywords } = require('@ata-project/keywords')
const v = withKeywords(new Validator({
type: 'object',
properties: {
createdAt: { instanceof: 'Date' },
pattern: { instanceof: 'RegExp' },
name: { type: 'string' },
},
required: ['name']
}))
v.validate({ name: 'Mert', createdAt: new Date() })
// { valid: true, errors: [] }
v.validate({ name: 'Mert', createdAt: 'not a date' })
// { valid: false, errors: [...] }withKeywords() compiles nothing at the time it is called. The schema walk, the generated check and the constructor lookups run on the first use of any entry point, so wrapping a validator costs a few accessors and about a microsecond; a constructor added to withKeywords.CONSTRUCTORS after wrapping and before the first call is used. A schema with no custom keyword settles to the validator's own entry points on that first call and pays nothing per call after.
instanceof and typeof have no JSON Schema spelling, so ata build and
toStandaloneModule() cannot carry them: a standalone module is generated from
the schema alone. A wrapped validator therefore refuses to compile rather than
emitting a module that accepts what the validator rejects.
toStandaloneModule: this validator enforces checks that are not in its schema,
so a standalone module would be weaker than the validator it came from.
Compile the validator before wrapping it if the extra checks are not needed on that path. Older ata versions emitted the weaker module without saying so, so this needs ata-validator 1.20.0 or newer to be reported.
Checks data instanceof Constructor. Supported constructors:
Object, Array, Function, Number, String, Date, RegExp, Promise, Map, Set, WeakMap, WeakSet, Buffer, Uint8Array, ArrayBuffer
Can be a string or array of strings:
{ instanceof: 'Date' }
{ instanceof: ['Date', 'String'] }Checks typeof data. Supported values:
undefined, string, number, object, function, boolean, symbol, bigint
{ typeof: 'function' }
{ typeof: ['string', 'number'] }instanceof and typeof are checked wherever they appear in the schema, not
only on top-level properties. Nested objects (properties), array elements
(items), and tuples (prefixItems) all recurse:
const v = withKeywords(new Validator({
type: 'object',
properties: {
images: {
type: 'array',
items: { properties: { takenAt: { instanceof: 'Date' } } }
}
}
}))
v.validate({ images: [{ takenAt: new Date() }] }) // valid
v.validate({ images: [{ takenAt: 'nope' }] }) // invalid, path /images/0/takenAtThe custom keywords run on every entry point that reports validity, not only
validate(): isValidObject(), validateJSON(), isValidJSON(),
validateAndParse() and the Standard Schema interface all apply them.
The JSON entry points check the parsed value, so instanceof on JSON text
rejects: parsed JSON holds plain objects, never class instances. Use typeof
for constraints that JSON input can satisfy.
The schema itself answers first, inside its own compiled function, so a value the schema turns down never pays for the custom keyword walk. When the schema accepts, the keywords run as one generated function that allocates nothing and reads only the properties they constrain; where code generation is blocked, a tree of closures answers identically. Errors are built only once a value has already failed, and a value that breaks both the schema and a custom keyword reports both.
On the schema from the schema-benchmarks product case, which carries fifteen
instanceof: 'Date' checks across nested objects and arrays, wrapping a
validator costs about 6 us at construction, and nothing is compiled until the
first call. Validating a value the schema accepts costs up to 25 ns more than
the bare validator, and validating one it rejects costs about 9 ns more.
Measured on an M-series Mac with Node 25.
const { withKeywords, CONSTRUCTORS } = require('@ata-project/keywords')
class MyClass {}
CONSTRUCTORS.MyClass = MyClass
const v = withKeywords(new Validator({
type: 'object',
properties: {
instance: { instanceof: 'MyClass' }
}
}))
v.validate({ instance: new MyClass() }) // validMIT