export { Format , FormatDefinition , AsyncFormatDefinition , KeywordDefinition , KeywordErrorDefinition , CodeKeywordDefinition , MacroKeywordDefinition , FuncKeywordDefinition , Vocabulary , Schema , SchemaObject , AnySchemaObject , AsyncSchema , AnySchema , ValidateFunction , AsyncValidateFunction , AnyValidateFunction , ErrorObject , ErrorNoParams , } from "./types" ; export { SchemaCxt , SchemaObjCxt } from "./compile" ; export interface Plugin < Opts > { ( ajv , options ? ) : Ajv ; [ prop ] : any ; } export { KeywordCxt } from "./compile/validate" ; export { DefinedError } from "./vocabularies/errors" ; export { JSONType } from "./compile/rules" ; export { JSONSchemaType } from "./types/json-schema" ; export { JTDSchemaType , SomeJTDSchemaType , JTDDataType } from "./types/jtd-schema" ; export { _ , str , stringify , nil , Name , Code , CodeGen , CodeGenOptions } from "./compile/codegen" ; import type { Schema , AnySchema , AnySchemaObject , SchemaObject , AsyncSchema , Vocabulary , KeywordDefinition , AddedKeywordDefinition , AnyValidateFunction , ValidateFunction , AsyncValidateFunction , ErrorObject , Format , AddedFormat , RegExpEngine , } from "./types" ; import type { JSONSchemaType } from "./types/json-schema" ; import type { JTDSchemaType , SomeJTDSchemaType , JTDDataType } from "./types/jtd-schema" ; import ValidationError from "./runtime/validation_error" ; import MissingRefError from "./compile/ref_error" ; import { getRules , ValidationRules , Rule , RuleGroup , JSONType } from "./compile/rules" ; import { SchemaEnv , compileSchema , resolveSchema } from "./compile" ; import { Code , ValueScope } from "./compile/codegen" ; import { normalizeId , getSchemaRefs } from "./compile/resolve" ; import { getJSONTypes } from "./compile/validate/dataType" ; import { eachItem } from "./compile/util" ; import * as $dataRefSchema from "./refs/data.json" ; const defaultRegExp = ( str , flags ) => new RegExp ( str , flags ) ; defaultRegExp . code = "new RegExp" ; const META_IGNORE_OPTIONS = [ "removeAdditional" , "useDefaults" , "coerceTypes" ] ; const EXT_SCOPE_NAMES = new Set ( [ "validate" , "serialize" , "parse" , "wrapper" , "root" , "schema" , "keyword" , "pattern" , "formats" , "validate$data" , "func" , "obj" , "Error" , ] ) ; export type Options = CurrentOptions & DeprecatedOptions ; export interface CurrentOptions { strict ? : boolean | "log" ; strictSchema ? : boolean | "log" ; strictNumbers ? : boolean | "log" ; strictTypes ? : boolean | "log" ; strictTuples ? : boolean | "log" ; strictRequired ? : boolean | "log" ; allowMatchingProperties ? : boolean ; allowUnionTypes ? : boolean ; validateFormats ? : boolean ; $data ? : boolean ; allErrors ? : boolean ; verbose ? : boolean ; discriminator ? : boolean ; unicodeRegExp ? : boolean ; timestamp ? : "string" | "date" ; parseDate ? : boolean ; allowDate ? : boolean ; $comment ? : true | ( ( comment : string , schemaPath ? : string , rootSchema ? : AnySchemaObject ) => unknown ) ; formats ? : { [ Name in string ] ? : Format ; } ; keywords ? : Vocabulary ; schemas ? : AnySchema [ ] | { [ Key in string ] ? : AnySchema ; } ; logger ? : Logger | false ; loadSchema ? : ( uri : string ) => Promise < AnySchemaObject > ; removeAdditional ? : boolean | "all" | "failing" ; useDefaults ? : boolean | "empty" ; coerceTypes ? : boolean | "array" ; next ? : boolean ; unevaluated ? : boolean ; dynamicRef ? : boolean ; schemaId ? : "id" | "$id" ; jtd ? : boolean ; meta ? : SchemaObject | boolean ; defaultMeta ? : string | AnySchemaObject ; validateSchema ? : boolean | "log" ; addUsedSchema ? : boolean ; inlineRefs ? : boolean | number ; passContext ? : boolean ; loopRequired ? : number ; loopEnum ? : number ; ownProperties ? : boolean ; multipleOfPrecision ? : number ; int32range ? : boolean ; messages ? : boolean ; code ? : CodeOptions ; } export interface CodeOptions { es5 ? : boolean ; esm ? : boolean ; lines ? : boolean ; optimize ? : boolean | number ; formats ? : Code ; source ? : boolean ; process ? : ( code : string , schema ? : SchemaEnv ) => string ; regExp ? : RegExpEngine ; } interface InstanceCodeOptions extends CodeOptions { regExp : RegExpEngine ; optimize : number ; } interface DeprecatedOptions { deprecated ignoreKeywordsWithRef ? : boolean ; deprecated jsPropertySyntax ? : boolean ; deprecated unicode ? : boolean ; } interface RemovedOptions { format ? : boolean ; errorDataPath ? : "object" | "property" ; nullable ? : boolean ; jsonPointers ? : boolean ; extendRefs ? : true | "ignore" | "fail" ; missingRefs ? : true | "ignore" | "fail" ; processCode ? : ( code : string , schema ? : SchemaEnv ) => string ; sourceCode ? : boolean ; strictDefaults ? : boolean ; strictKeywords ? : boolean ; uniqueItems ? : boolean ; unknownFormats ? : true | string [ ] | "ignore" ; cache ? : any ; serialize ? : ( schema : AnySchema ) => unknown ; ajvErrors ? : boolean ; } type OptionsInfo < T extends RemovedOptions | DeprecatedOptions > = { [ K in keyof T ] - ? : string | undefined ; } ; const removedOptions = { errorDataPath : "" , format : "`validateFormats: false` can be used instead." , nullable : '"nullable" keyword is supported by default.' , jsonPointers : "Deprecated jsPropertySyntax can be used instead." , extendRefs : "Deprecated ignoreKeywordsWithRef can be used instead." , missingRefs : "Pass empty schema with $id that should be ignored to ajv.addSchema." , processCode : "Use option `code: {process: (code, schemaEnv: object) => string}`" , sourceCode : "Use option `code: {source: true}`" , strictDefaults : "It is default now, see option `strict`." , strictKeywords : "It is default now, see option `strict`." , uniqueItems : '"uniqueItems" keyword is always validated.' , unknownFormats : "Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option)." , cache : "Map is used as cache, schema object as key." , serialize : "Map is used as cache, schema object as key." , ajvErrors : "It is default now." , } ; const deprecatedOptions = { ignoreKeywordsWithRef : "" , jsPropertySyntax : "" , unicode : '"minLength"/"maxLength" account for unicode characters by default.' , } ; type RequiredInstanceOptions = { [ K in "strictSchema" | "strictNumbers" | "strictTypes" | "strictTuples" | "strictRequired" | "inlineRefs" | "loopRequired" | "loopEnum" | "meta" | "messages" | "schemaId" | "addUsedSchema" | "validateSchema" | "validateFormats" | "int32range" | "unicodeRegExp" ] : NonNullable < Options [ K ] > ; } & { code : InstanceCodeOptions ; } ; export type InstanceOptions = Options & RequiredInstanceOptions ; const MAX_EXPRESSION = 200 ; function requiredOptions ( o ) { const s = o . strict ; const _optz = o . code ?. optimize ; const optimize = _optz === true || _optz === undefined ? 1 : _optz || 0 ; const regExp = o . code ?. regExp ?? defaultRegExp ; return { strictSchema : o . strictSchema ?? s ?? true , strictNumbers : o . strictNumbers ?? s ?? true , strictTypes : o . strictTypes ?? s ?? "log" , strictTuples : o . strictTuples ?? s ?? "log" , strictRequired : o . strictRequired ?? s ?? false , code : o . code ? { ... o . code , optimize , regExp } : { optimize , regExp } , loopRequired : o . loopRequired ?? MAX_EXPRESSION , loopEnum : o . loopEnum ?? MAX_EXPRESSION , meta : o . meta ?? true , messages : o . messages ?? true , inlineRefs : o . inlineRefs ?? true , schemaId : o . schemaId ?? "$id" , addUsedSchema : o . addUsedSchema ?? true , validateSchema : o . validateSchema ?? true , validateFormats : o . validateFormats ?? true , unicodeRegExp : o . unicodeRegExp ?? true , int32range : o . int32range ?? true , } ; } export interface Logger { log ( ... args ) : unknown ; warn ( ... args ) : unknown ; error ( ... args ) : unknown ; } export default class Ajv { opts : InstanceOptions ; errors ? : ErrorObject [ ] | null ; logger : Logger ; readonly scope : ValueScope ; readonly schemas : { [ Key in string ] ? : SchemaEnv ; } = { } ; readonly refs : { [ Ref in string ] ? : SchemaEnv | string ; } = { } ; readonly formats : { [ Name in string ] ? : AddedFormat ; } = { } ; readonly RULES : ValidationRules ; readonly _compilations : Set < SchemaEnv > = new Set ( ) ; private readonly _loading : { [ Ref in string ] ? : Promise < AnySchemaObject > ; } = { } ; private readonly _cache : Map < AnySchema , SchemaEnv > = new Map ( ) ; private readonly _metaOpts : InstanceOptions ; static ValidationError = ValidationError ; static MissingRefError = MissingRefError ; constructor ( opts = { } ) { opts = this . opts = { ... opts , ... requiredOptions ( opts ) } ; const { es5 , lines } = this . opts . code ; this . scope = new ValueScope ( { scope : { } , prefixes : EXT_SCOPE_NAMES , es5 , lines } ) ; this . logger = getLogger ( opts . logger ) ; const formatOpt = opts . validateFormats ; opts . validateFormats = false ; this . RULES = getRules ( ) ; checkOptions . call ( this , removedOptions , opts , "NOT SUPPORTED" ) ; checkOptions . call ( this , deprecatedOptions , opts , "DEPRECATED" , "warn" ) ; this . _metaOpts = getMetaSchemaOptions . call ( this ) ; if ( opts . formats ) addInitialFormats . call ( this ) ; this . _addVocabularies ( ) ; this . _addDefaultMetaSchema ( ) ; if ( opts . keywords ) addInitialKeywords . call ( this , opts . keywords ) ; if ( typeof opts . meta == "object" ) this . addMetaSchema ( opts . meta ) ; addInitialSchemas . call ( this ) ; opts . validateFormats = formatOpt ; } _addVocabularies ( ) { this . addKeyword ( "$async" ) ; } _addDefaultMetaSchema ( ) { const { $data , meta , schemaId } = this . opts ; let _dataRefSchema = $dataRefSchema ; if ( schemaId === "id" ) { _dataRefSchema = { ... $dataRefSchema } ; _dataRefSchema . id = _dataRefSchema . $id ; delete _dataRefSchema . $id ; } if ( meta && $data ) this . addMetaSchema ( _dataRefSchema , _dataRefSchema [ schemaId ] , false ) ; } defaultMeta ( ) { const { meta , schemaId } = this . opts ; return ( this . opts . defaultMeta = typeof meta == "object" ? meta [ schemaId ] || meta : undefined ) ; } validate ( schema , data ) ; validate ( schemaKeyRef , data ) ; validate < T > ( schema , data ) ; validate < T > ( schema , data ) ; validate < N extends never , T extends SomeJTDSchemaType > ( schema , data ) ; validate < T > ( schema , data ) ; validate < T > ( schemaKeyRef , data ) ; validate < T > ( schemaKeyRef , data ) { let v ; if ( typeof schemaKeyRef == "string" ) { v = this . getSchema < T > ( schemaKeyRef ) ; if ( ! v ) throw new Error ( `no schema with key or ref "${ schemaKeyRef }"` ) ; } else { v = this . compile < T > ( schemaKeyRef ) ; } const valid = v ( data ) ; if ( ! ( "$async" in v ) ) this . errors = v . errors ; return valid ; } compile < T = unknown > ( schema , _meta ? ) ; compile < T = unknown > ( schema , _meta ? ) ; compile < N extends never , T extends SomeJTDSchemaType > ( schema , _meta ? ) ; compile < T = unknown > ( schema , _meta ? ) ; compile < T = unknown > ( schema , _meta ? ) ; compile < T = unknown > ( schema , _meta ? ) { const sch = this . _addSchema ( schema , _meta ) ; return ( sch . validate || this . _compileSchemaEnv ( sch ) ) as AnyValidateFunction < T > ; } compileAsync < T = unknown > ( schema , _meta ? ) ; compileAsync < T = unknown > ( schema , _meta ? ) ; compileAsync < T = unknown > ( schema , meta ? ) ; compileAsync < T = unknown > ( schema , meta ? ) ; compileAsync < T = unknown > ( schema , meta ? ) { if ( typeof this . opts . loadSchema != "function" ) { throw new Error ( "options.loadSchema should be a function" ) ; } const { loadSchema } = this . opts ; return runCompileAsync . call ( this , schema , meta ) ; async function runCompileAsync ( this , _schema , _meta ? ) { await loadMetaSchema . call ( this , _schema . $schema ) ; const sch = this . _addSchema ( _schema , _meta ) ; return sch . validate || _compileAsync . call ( this , sch ) ; } async function loadMetaSchema ( this , $ref ? ) { if ( $ref && ! this . getSchema ( $ref ) ) { await runCompileAsync . call ( this , { $ref } , true ) ; } } async function _compileAsync ( this , sch ) { try { return this . _compileSchemaEnv ( sch ) ; } catch ( e ) { if ( ! ( e instanceof MissingRefError ) ) throw e ; checkLoaded . call ( this , e ) ; await loadMissingSchema . call ( this , e . missingSchema ) ; return _compileAsync . call ( this , sch ) ; } } function checkLoaded ( this , { missingSchema : ref , missingRef } ) { if ( this . refs [ ref ] ) { throw new Error ( `AnySchema ${ ref } is loaded but ${ missingRef } cannot be resolved` ) ; } } async function loadMissingSchema ( this , ref ) { const _schema = await _loadSchema . call ( this , ref ) ; if ( ! this . refs [ ref ] ) await loadMetaSchema . call ( this , _schema . $schema ) ; if ( ! this . refs [ ref ] ) this . addSchema ( _schema , ref , meta ) ; } async function _loadSchema ( this , ref ) { const p = this . _loading [ ref ] ; if ( p ) return p ; try { return await ( this . _loading [ ref ] = loadSchema ( ref ) ) ; } finally { delete this . _loading [ ref ] ; } } } addSchema ( schema , key ? , _meta ? , _validateSchema = this . opts . validateSchema ) { if ( Array . isArray ( schema ) ) { for ( const sch of schema ) this . addSchema ( sch , undefined , _meta , _validateSchema ) ; return this ; } let id ; if ( typeof schema === "object" ) { const { schemaId } = this . opts ; id = schema [ schemaId ] ; if ( id !== undefined && typeof id != "string" ) { throw new Error ( `schema ${ schemaId } must be string` ) ; } } key = normalizeId ( key || id ) ; this . _checkUnique ( key ) ; this . schemas [ key ] = this . _addSchema ( schema , _meta , key , _validateSchema , true ) ; return this ; } addMetaSchema ( schema , key ? , _validateSchema = this . opts . validateSchema ) { this . addSchema ( schema , key , true , _validateSchema ) ; return this ; } validateSchema ( schema , throwOrLogError ? ) { if ( typeof schema == "boolean" ) return true ; let $schema ; $schema = schema . $schema ; if ( $schema !== undefined && typeof $schema != "string" ) { throw new Error ( "$schema must be a string" ) ; } $schema = $schema || this . opts . defaultMeta || this . defaultMeta ( ) ; if ( ! $schema ) { this . logger . warn ( "meta-schema not available" ) ; this . errors = null ; return true ; } const valid = this . validate ( $schema , schema ) ; if ( ! valid && throwOrLogError ) { const message = "schema is invalid: " + this . errorsText ( ) ; if ( this . opts . validateSchema === "log" ) this . logger . error ( message ) ; else throw new Error ( message ) ; } return valid ; } getSchema < T = unknown > ( keyRef ) { let sch ; while ( typeof ( sch = getSchEnv . call ( this , keyRef ) ) == "string" ) keyRef = sch ; if ( sch === undefined ) { const { schemaId } = this . opts ; const root = new SchemaEnv ( { schema : { } , schemaId } ) ; sch = resolveSchema . call ( this , root , keyRef ) ; if ( ! sch ) return ; this . refs [ keyRef ] = sch ; } return ( sch . validate || this . _compileSchemaEnv ( sch ) ) as AnyValidateFunction < T > | undefined ; } removeSchema ( schemaKeyRef ? ) { if ( schemaKeyRef instanceof RegExp ) { this . _removeAllSchemas ( this . schemas , schemaKeyRef ) ; this . _removeAllSchemas ( this . refs , schemaKeyRef ) ; return this ; } switch ( typeof schemaKeyRef ) { case "undefined" : this . _removeAllSchemas ( this . schemas ) ; this . _removeAllSchemas ( this . refs ) ; this . _cache . clear ( ) ; return this ; case "string" : { const sch = getSchEnv . call ( this , schemaKeyRef ) ; if ( typeof sch == "object" ) this . _cache . delete ( sch . schema ) ; delete this . schemas [ schemaKeyRef ] ; delete this . refs [ schemaKeyRef ] ; return this ; } case "object" : { const cacheKey = schemaKeyRef ; this . _cache . delete ( cacheKey ) ; let id = schemaKeyRef [ this . opts . schemaId ] ; if ( id ) { id = normalizeId ( id ) ; delete this . schemas [ id ] ; delete this . refs [ id ] ; } return this ; } default : throw new Error ( "ajv.removeSchema: invalid parameter" ) ; } } addVocabulary ( definitions ) { for ( const def of definitions ) this . addKeyword ( def ) ; return this ; } addKeyword ( kwdOrDef , def ? ) { let keyword ; if ( typeof kwdOrDef == "string" ) { keyword = kwdOrDef ; if ( typeof def == "object" ) { this . logger . warn ( "these parameters are deprecated, see docs for addKeyword" ) ; def . keyword = keyword ; } } else if ( typeof kwdOrDef == "object" && def === undefined ) { def = kwdOrDef ; keyword = def . keyword ; if ( Array . isArray ( keyword ) && ! keyword . length ) { throw new Error ( "addKeywords: keyword must be string or non-empty array" ) ; } } else { throw new Error ( "invalid addKeywords parameters" ) ; } checkKeyword . call ( this , keyword , def ) ; if ( ! def ) { eachItem ( keyword , ( kwd ) => addRule . call ( this , kwd ) ) ; return this ; } keywordMetaschema . call ( this , def ) ; const definition = { ... def , type : getJSONTypes ( def . type ) , schemaType : getJSONTypes ( def . schemaType ) , } ; eachItem ( keyword , definition . type . length === 0 ? ( k ) => addRule . call ( this , k , definition ) : ( k ) => definition . type . forEach ( ( t ) => addRule . call ( this , k , definition , t ) ) ) ; return this ; } getKeyword ( keyword ) { const rule = this . RULES . all [ keyword ] ; return typeof rule == "object" ? rule . definition : ! ! rule ; } removeKeyword ( keyword ) { const { RULES } = this ; delete RULES . keywords [ keyword ] ; delete RULES . all [ keyword ] ; for ( const group of RULES . rules ) { const i = group . rules . findIndex ( ( rule ) => rule . keyword === keyword ) ; if ( i >= 0 ) group . rules . splice ( i , 1 ) ; } return this ; } addFormat ( name , format ) { if ( typeof format == "string" ) format = new RegExp ( format ) ; this . formats [ name ] = format ; return this ; } errorsText ( errors = this . errors , { separator = ", " , dataVar = "data" } = { } ) { if ( ! errors || errors . length === 0 ) return "No errors" ; return errors . map ( ( e ) => `${ dataVar }${ e . instancePath } ${ e . message }` ) . reduce ( ( text , msg ) => text + separator + msg ) ; } $dataMetaSchema ( metaSchema , keywordsJsonPointers ) { const rules = this . RULES . all ; metaSchema = JSON . parse ( JSON . stringify ( metaSchema ) ) ; for ( const jsonPointer of keywordsJsonPointers ) { const segments = jsonPointer . split ( "/" ) . slice ( 1 ) ; let keywords = metaSchema ; for ( const seg of segments ) keywords = keywords [ seg ] as AnySchemaObject ; for ( const key in rules ) { const rule = rules [ key ] ; if ( typeof rule != "object" ) continue ; const { $data } = rule . definition ; const schema = keywords [ key ] as AnySchemaObject | undefined ; if ( $data && schema ) keywords [ key ] = schemaOrData ( schema ) ; } } return metaSchema ; } private _removeAllSchemas ( schemas , regex ? ) { for ( const keyRef in schemas ) { const sch = schemas [ keyRef ] ; if ( ! regex || regex . test ( keyRef ) ) { if ( typeof sch == "string" ) { delete schemas [ keyRef ] ; } else if ( sch && ! sch . meta ) { this . _cache . delete ( sch . schema ) ; delete schemas [ keyRef ] ; } } } } _addSchema ( schema , meta ? , baseId ? , validateSchema = this . opts . validateSchema , addSchema = this . opts . addUsedSchema ) { let id ; const { schemaId } = this . opts ; if ( typeof schema == "object" ) { id = schema [ schemaId ] ; } else { if ( this . opts . jtd ) throw new Error ( "schema must be object" ) ; else if ( typeof schema != "boolean" ) throw new Error ( "schema must be object or boolean" ) ; } let sch = this . _cache . get ( schema ) ; if ( sch !== undefined ) return sch ; baseId = normalizeId ( id || baseId ) ; const localRefs = getSchemaRefs . call ( this , schema , baseId ) ; sch = new SchemaEnv ( { schema , schemaId , meta , baseId , localRefs } ) ; this . _cache . set ( sch . schema , sch ) ; if ( addSchema && ! baseId . startsWith ( "#" ) ) { if ( baseId ) this . _checkUnique ( baseId ) ; this . refs [ baseId ] = sch ; } if ( validateSchema ) this . validateSchema ( schema , true ) ; return sch ; } private _checkUnique ( id ) { if ( this . schemas [ id ] || this . refs [ id ] ) { throw new Error ( `schema with key or id "${ id }" already exists` ) ; } } private _compileSchemaEnv ( sch ) { if ( sch . meta ) this . _compileMetaSchema ( sch ) ; else compileSchema . call ( this , sch ) ; if ( ! sch . validate ) throw new Error ( "ajv implementation error" ) ; return sch . validate ; } private _compileMetaSchema ( sch ) { const currentOpts = this . opts ; this . opts = this . _metaOpts ; try { compileSchema . call ( this , sch ) ; } finally { this . opts = currentOpts ; } } } export interface ErrorsTextOptions { separator ? : string ; dataVar ? : string ; } function checkOptions ( this , checkOpts , options , msg , log = "error" ) { for ( const key in checkOpts ) { const opt = key as keyof typeof checkOpts ; if ( opt in options ) this . logger [ log ] ( `${ msg }: option ${ key }. ${ checkOpts [ opt ] }` ) ; } } function getSchEnv ( this , keyRef ) { keyRef = normalizeId ( keyRef ) ; return this . schemas [ keyRef ] || this . refs [ keyRef ] ; } function addInitialSchemas ( this ) { const optsSchemas = this . opts . schemas ; if ( ! optsSchemas ) return ; if ( Array . isArray ( optsSchemas ) ) this . addSchema ( optsSchemas ) ; else for ( const key in optsSchemas ) this . addSchema ( optsSchemas [ key ] as AnySchema , key ) ; } function addInitialFormats ( this ) { for ( const name in this . opts . formats ) { const format = this . opts . formats [ name ] ; if ( format ) this . addFormat ( name , format ) ; } } function addInitialKeywords ( this , defs ) { if ( Array . isArray ( defs ) ) { this . addVocabulary ( defs ) ; return ; } this . logger . warn ( "keywords option as map is deprecated, pass array" ) ; for ( const keyword in defs ) { const def = defs [ keyword ] as KeywordDefinition ; if ( ! def . keyword ) def . keyword = keyword ; this . addKeyword ( def ) ; } } function getMetaSchemaOptions ( this ) { const metaOpts = { ... this . opts } ; for ( const opt of META_IGNORE_OPTIONS ) delete metaOpts [ opt ] ; return metaOpts ; } const noLogs = { log ( ) { } , warn ( ) { } , error ( ) { } } ; function getLogger ( logger ? ) { if ( logger === false ) return noLogs ; if ( logger === undefined ) return console ; if ( logger . log && logger . warn && logger . error ) return logger as Logger ; throw new Error ( "logger must implement log, warn and error methods" ) ; } const KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i ; function checkKeyword ( this , keyword , def ? ) { const { RULES } = this ; eachItem ( keyword , ( kwd ) => { if ( RULES . keywords [ kwd ] ) throw new Error ( `Keyword ${ kwd } is already defined` ) ; if ( ! KEYWORD_NAME . test ( kwd ) ) throw new Error ( `Keyword ${ kwd } has invalid name` ) ; } ) ; if ( ! def ) return ; if ( def . $data && ! ( "code" in def || "validate" in def ) ) { throw new Error ( '$data keyword must have "code" or "validate" function' ) ; } } function addRule ( this , keyword , definition ? , dataType ? ) { const post = definition ?. post ; if ( dataType && post ) throw new Error ( 'keyword with "post" flag cannot have "type"' ) ; const { RULES } = this ; let ruleGroup = post ? RULES . post : RULES . rules . find ( ( { type : t } ) => t === dataType ) ; if ( ! ruleGroup ) { ruleGroup = { type : dataType , rules : [ ] } ; RULES . rules . push ( ruleGroup ) ; } RULES . keywords [ keyword ] = true ; if ( ! definition ) return ; const rule = { keyword , definition : { ... definition , type : getJSONTypes ( definition . type ) , schemaType : getJSONTypes ( definition . schemaType ) , } , } ; if ( definition . before ) addBeforeRule . call ( this , ruleGroup , rule , definition . before ) ; else ruleGroup . rules . push ( rule ) ; RULES . all [ keyword ] = rule ; definition . implements ?. forEach ( ( kwd ) => this . addKeyword ( kwd ) ) ; } function addBeforeRule ( this , ruleGroup , rule , before ) { const i = ruleGroup . rules . findIndex ( ( _rule ) => _rule . keyword === before ) ; if ( i >= 0 ) { ruleGroup . rules . splice ( i , 0 , rule ) ; } else { ruleGroup . rules . push ( rule ) ; this . logger . warn ( `rule ${ before } is not defined` ) ; } } function keywordMetaschema ( this , def ) { let { metaSchema } = def ; if ( metaSchema === undefined ) return ; if ( def . $data && this . opts . $data ) metaSchema = schemaOrData ( metaSchema ) ; def . validateSchema = this . compile ( metaSchema , true ) ; } const $dataRef = { $ref : "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#" , } ; function schemaOrData ( schema ) { return { anyOf : [ schema , $dataRef ] } ; }