You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					325 lines
				
				12 KiB
			
		
		
			
		
	
	
					325 lines
				
				12 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								import type {
							 | 
						||
| 
								 | 
							
								  AnySchema,
							 | 
						||
| 
								 | 
							
								  AnySchemaObject,
							 | 
						||
| 
								 | 
							
								  AnyValidateFunction,
							 | 
						||
| 
								 | 
							
								  AsyncValidateFunction,
							 | 
						||
| 
								 | 
							
								  EvaluatedProperties,
							 | 
						||
| 
								 | 
							
								  EvaluatedItems,
							 | 
						||
| 
								 | 
							
								} from "../types"
							 | 
						||
| 
								 | 
							
								import type Ajv from "../core"
							 | 
						||
| 
								 | 
							
								import type {InstanceOptions} from "../core"
							 | 
						||
| 
								 | 
							
								import {CodeGen, _, nil, stringify, Name, Code, ValueScopeName} from "./codegen"
							 | 
						||
| 
								 | 
							
								import ValidationError from "../runtime/validation_error"
							 | 
						||
| 
								 | 
							
								import N from "./names"
							 | 
						||
| 
								 | 
							
								import {LocalRefs, getFullPath, _getFullPath, inlineRef, normalizeId, resolveUrl} from "./resolve"
							 | 
						||
| 
								 | 
							
								import {schemaHasRulesButRef, unescapeFragment} from "./util"
							 | 
						||
| 
								 | 
							
								import {validateFunctionCode} from "./validate"
							 | 
						||
| 
								 | 
							
								import * as URI from "uri-js"
							 | 
						||
| 
								 | 
							
								import {JSONType} from "./rules"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export type SchemaRefs = {
							 | 
						||
| 
								 | 
							
								  [Ref in string]?: SchemaEnv | AnySchema
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export interface SchemaCxt {
							 | 
						||
| 
								 | 
							
								  readonly gen: CodeGen
							 | 
						||
| 
								 | 
							
								  readonly allErrors?: boolean // validation mode - whether to collect all errors or break on error
							 | 
						||
| 
								 | 
							
								  readonly data: Name // Name with reference to the current part of data instance
							 | 
						||
| 
								 | 
							
								  readonly parentData: Name // should be used in keywords modifying data
							 | 
						||
| 
								 | 
							
								  readonly parentDataProperty: Code | number // should be used in keywords modifying data
							 | 
						||
| 
								 | 
							
								  readonly dataNames: Name[]
							 | 
						||
| 
								 | 
							
								  readonly dataPathArr: (Code | number)[]
							 | 
						||
| 
								 | 
							
								  readonly dataLevel: number // the level of the currently validated data,
							 | 
						||
| 
								 | 
							
								  // it can be used to access both the property names and the data on all levels from the top.
							 | 
						||
| 
								 | 
							
								  dataTypes: JSONType[] // data types applied to the current part of data instance
							 | 
						||
| 
								 | 
							
								  definedProperties: Set<string> // set of properties to keep track of for required checks
							 | 
						||
| 
								 | 
							
								  readonly topSchemaRef: Code
							 | 
						||
| 
								 | 
							
								  readonly validateName: Name
							 | 
						||
| 
								 | 
							
								  evaluated?: Name
							 | 
						||
| 
								 | 
							
								  readonly ValidationError?: Name
							 | 
						||
| 
								 | 
							
								  readonly schema: AnySchema // current schema object - equal to parentSchema passed via KeywordCxt
							 | 
						||
| 
								 | 
							
								  readonly schemaEnv: SchemaEnv
							 | 
						||
| 
								 | 
							
								  readonly rootId: string
							 | 
						||
| 
								 | 
							
								  baseId: string // the current schema base URI that should be used as the base for resolving URIs in references (\$ref)
							 | 
						||
| 
								 | 
							
								  readonly schemaPath: Code // the run-time expression that evaluates to the property name of the current schema
							 | 
						||
| 
								 | 
							
								  readonly errSchemaPath: string // this is actual string, should not be changed to Code
							 | 
						||
| 
								 | 
							
								  readonly errorPath: Code
							 | 
						||
| 
								 | 
							
								  readonly propertyName?: Name
							 | 
						||
| 
								 | 
							
								  readonly compositeRule?: boolean // true indicates that the current schema is inside the compound keyword,
							 | 
						||
| 
								 | 
							
								  // where failing some rule doesn't mean validation failure (`anyOf`, `oneOf`, `not`, `if`).
							 | 
						||
| 
								 | 
							
								  // This flag is used to determine whether you can return validation result immediately after any error in case the option `allErrors` is not `true.
							 | 
						||
| 
								 | 
							
								  // You only need to use it if you have many steps in your keywords and potentially can define multiple errors.
							 | 
						||
| 
								 | 
							
								  props?: EvaluatedProperties | Name // properties evaluated by this schema - used by parent schema or assigned to validation function
							 | 
						||
| 
								 | 
							
								  items?: EvaluatedItems | Name // last item evaluated by this schema - used by parent schema or assigned to validation function
							 | 
						||
| 
								 | 
							
								  jtdDiscriminator?: string
							 | 
						||
| 
								 | 
							
								  jtdMetadata?: boolean
							 | 
						||
| 
								 | 
							
								  readonly createErrors?: boolean
							 | 
						||
| 
								 | 
							
								  readonly opts: InstanceOptions // Ajv instance option.
							 | 
						||
| 
								 | 
							
								  readonly self: Ajv // current Ajv instance
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export interface SchemaObjCxt extends SchemaCxt {
							 | 
						||
| 
								 | 
							
								  readonly schema: AnySchemaObject
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								interface SchemaEnvArgs {
							 | 
						||
| 
								 | 
							
								  readonly schema: AnySchema
							 | 
						||
| 
								 | 
							
								  readonly schemaId?: "$id" | "id"
							 | 
						||
| 
								 | 
							
								  readonly root?: SchemaEnv
							 | 
						||
| 
								 | 
							
								  readonly baseId?: string
							 | 
						||
| 
								 | 
							
								  readonly schemaPath?: string
							 | 
						||
| 
								 | 
							
								  readonly localRefs?: LocalRefs
							 | 
						||
| 
								 | 
							
								  readonly meta?: boolean
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export class SchemaEnv implements SchemaEnvArgs {
							 | 
						||
| 
								 | 
							
								  readonly schema: AnySchema
							 | 
						||
| 
								 | 
							
								  readonly schemaId?: "$id" | "id"
							 | 
						||
| 
								 | 
							
								  readonly root: SchemaEnv
							 | 
						||
| 
								 | 
							
								  baseId: string // TODO possibly, it should be readonly
							 | 
						||
| 
								 | 
							
								  schemaPath?: string
							 | 
						||
| 
								 | 
							
								  localRefs?: LocalRefs
							 | 
						||
| 
								 | 
							
								  readonly meta?: boolean
							 | 
						||
| 
								 | 
							
								  readonly $async?: boolean // true if the current schema is asynchronous.
							 | 
						||
| 
								 | 
							
								  readonly refs: SchemaRefs = {}
							 | 
						||
| 
								 | 
							
								  readonly dynamicAnchors: {[Ref in string]?: true} = {}
							 | 
						||
| 
								 | 
							
								  validate?: AnyValidateFunction
							 | 
						||
| 
								 | 
							
								  validateName?: ValueScopeName
							 | 
						||
| 
								 | 
							
								  serialize?: (data: unknown) => string
							 | 
						||
| 
								 | 
							
								  serializeName?: ValueScopeName
							 | 
						||
| 
								 | 
							
								  parse?: (data: string) => unknown
							 | 
						||
| 
								 | 
							
								  parseName?: ValueScopeName
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  constructor(env: SchemaEnvArgs) {
							 | 
						||
| 
								 | 
							
								    let schema: AnySchemaObject | undefined
							 | 
						||
| 
								 | 
							
								    if (typeof env.schema == "object") schema = env.schema
							 | 
						||
| 
								 | 
							
								    this.schema = env.schema
							 | 
						||
| 
								 | 
							
								    this.schemaId = env.schemaId
							 | 
						||
| 
								 | 
							
								    this.root = env.root || this
							 | 
						||
| 
								 | 
							
								    this.baseId = env.baseId ?? normalizeId(schema?.[env.schemaId || "$id"])
							 | 
						||
| 
								 | 
							
								    this.schemaPath = env.schemaPath
							 | 
						||
| 
								 | 
							
								    this.localRefs = env.localRefs
							 | 
						||
| 
								 | 
							
								    this.meta = env.meta
							 | 
						||
| 
								 | 
							
								    this.$async = schema?.$async
							 | 
						||
| 
								 | 
							
								    this.refs = {}
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// let codeSize = 0
							 | 
						||
| 
								 | 
							
								// let nodeCount = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Compiles schema in SchemaEnv
							 | 
						||
| 
								 | 
							
								export function compileSchema(this: Ajv, sch: SchemaEnv): SchemaEnv {
							 | 
						||
| 
								 | 
							
								  // TODO refactor - remove compilations
							 | 
						||
| 
								 | 
							
								  const _sch = getCompilingSchema.call(this, sch)
							 | 
						||
| 
								 | 
							
								  if (_sch) return _sch
							 | 
						||
| 
								 | 
							
								  const rootId = getFullPath(this.opts.uriResolver, sch.root.baseId) // TODO if getFullPath removed 1 tests fails
							 | 
						||
| 
								 | 
							
								  const {es5, lines} = this.opts.code
							 | 
						||
| 
								 | 
							
								  const {ownProperties} = this.opts
							 | 
						||
| 
								 | 
							
								  const gen = new CodeGen(this.scope, {es5, lines, ownProperties})
							 | 
						||
| 
								 | 
							
								  let _ValidationError
							 | 
						||
| 
								 | 
							
								  if (sch.$async) {
							 | 
						||
| 
								 | 
							
								    _ValidationError = gen.scopeValue("Error", {
							 | 
						||
| 
								 | 
							
								      ref: ValidationError,
							 | 
						||
| 
								 | 
							
								      code: _`require("ajv/dist/runtime/validation_error").default`,
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const validateName = gen.scopeName("validate")
							 | 
						||
| 
								 | 
							
								  sch.validateName = validateName
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const schemaCxt: SchemaCxt = {
							 | 
						||
| 
								 | 
							
								    gen,
							 | 
						||
| 
								 | 
							
								    allErrors: this.opts.allErrors,
							 | 
						||
| 
								 | 
							
								    data: N.data,
							 | 
						||
| 
								 | 
							
								    parentData: N.parentData,
							 | 
						||
| 
								 | 
							
								    parentDataProperty: N.parentDataProperty,
							 | 
						||
| 
								 | 
							
								    dataNames: [N.data],
							 | 
						||
| 
								 | 
							
								    dataPathArr: [nil], // TODO can its length be used as dataLevel if nil is removed?
							 | 
						||
| 
								 | 
							
								    dataLevel: 0,
							 | 
						||
| 
								 | 
							
								    dataTypes: [],
							 | 
						||
| 
								 | 
							
								    definedProperties: new Set<string>(),
							 | 
						||
| 
								 | 
							
								    topSchemaRef: gen.scopeValue(
							 | 
						||
| 
								 | 
							
								      "schema",
							 | 
						||
| 
								 | 
							
								      this.opts.code.source === true
							 | 
						||
| 
								 | 
							
								        ? {ref: sch.schema, code: stringify(sch.schema)}
							 | 
						||
| 
								 | 
							
								        : {ref: sch.schema}
							 | 
						||
| 
								 | 
							
								    ),
							 | 
						||
| 
								 | 
							
								    validateName,
							 | 
						||
| 
								 | 
							
								    ValidationError: _ValidationError,
							 | 
						||
| 
								 | 
							
								    schema: sch.schema,
							 | 
						||
| 
								 | 
							
								    schemaEnv: sch,
							 | 
						||
| 
								 | 
							
								    rootId,
							 | 
						||
| 
								 | 
							
								    baseId: sch.baseId || rootId,
							 | 
						||
| 
								 | 
							
								    schemaPath: nil,
							 | 
						||
| 
								 | 
							
								    errSchemaPath: sch.schemaPath || (this.opts.jtd ? "" : "#"),
							 | 
						||
| 
								 | 
							
								    errorPath: _`""`,
							 | 
						||
| 
								 | 
							
								    opts: this.opts,
							 | 
						||
| 
								 | 
							
								    self: this,
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let sourceCode: string | undefined
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    this._compilations.add(sch)
							 | 
						||
| 
								 | 
							
								    validateFunctionCode(schemaCxt)
							 | 
						||
| 
								 | 
							
								    gen.optimize(this.opts.code.optimize)
							 | 
						||
| 
								 | 
							
								    // gen.optimize(1)
							 | 
						||
| 
								 | 
							
								    const validateCode = gen.toString()
							 | 
						||
| 
								 | 
							
								    sourceCode = `${gen.scopeRefs(N.scope)}return ${validateCode}`
							 | 
						||
| 
								 | 
							
								    // console.log((codeSize += sourceCode.length), (nodeCount += gen.nodeCount))
							 | 
						||
| 
								 | 
							
								    if (this.opts.code.process) sourceCode = this.opts.code.process(sourceCode, sch)
							 | 
						||
| 
								 | 
							
								    // console.log("\n\n\n *** \n", sourceCode)
							 | 
						||
| 
								 | 
							
								    const makeValidate = new Function(`${N.self}`, `${N.scope}`, sourceCode)
							 | 
						||
| 
								 | 
							
								    const validate: AnyValidateFunction = makeValidate(this, this.scope.get())
							 | 
						||
| 
								 | 
							
								    this.scope.value(validateName, {ref: validate})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    validate.errors = null
							 | 
						||
| 
								 | 
							
								    validate.schema = sch.schema
							 | 
						||
| 
								 | 
							
								    validate.schemaEnv = sch
							 | 
						||
| 
								 | 
							
								    if (sch.$async) (validate as AsyncValidateFunction).$async = true
							 | 
						||
| 
								 | 
							
								    if (this.opts.code.source === true) {
							 | 
						||
| 
								 | 
							
								      validate.source = {validateName, validateCode, scopeValues: gen._values}
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (this.opts.unevaluated) {
							 | 
						||
| 
								 | 
							
								      const {props, items} = schemaCxt
							 | 
						||
| 
								 | 
							
								      validate.evaluated = {
							 | 
						||
| 
								 | 
							
								        props: props instanceof Name ? undefined : props,
							 | 
						||
| 
								 | 
							
								        items: items instanceof Name ? undefined : items,
							 | 
						||
| 
								 | 
							
								        dynamicProps: props instanceof Name,
							 | 
						||
| 
								 | 
							
								        dynamicItems: items instanceof Name,
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (validate.source) validate.source.evaluated = stringify(validate.evaluated)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    sch.validate = validate
							 | 
						||
| 
								 | 
							
								    return sch
							 | 
						||
| 
								 | 
							
								  } catch (e) {
							 | 
						||
| 
								 | 
							
								    delete sch.validate
							 | 
						||
| 
								 | 
							
								    delete sch.validateName
							 | 
						||
| 
								 | 
							
								    if (sourceCode) this.logger.error("Error compiling schema, function code:", sourceCode)
							 | 
						||
| 
								 | 
							
								    // console.log("\n\n\n *** \n", sourceCode, this.opts)
							 | 
						||
| 
								 | 
							
								    throw e
							 | 
						||
| 
								 | 
							
								  } finally {
							 | 
						||
| 
								 | 
							
								    this._compilations.delete(sch)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function resolveRef(
							 | 
						||
| 
								 | 
							
								  this: Ajv,
							 | 
						||
| 
								 | 
							
								  root: SchemaEnv,
							 | 
						||
| 
								 | 
							
								  baseId: string,
							 | 
						||
| 
								 | 
							
								  ref: string
							 | 
						||
| 
								 | 
							
								): AnySchema | SchemaEnv | undefined {
							 | 
						||
| 
								 | 
							
								  ref = resolveUrl(this.opts.uriResolver, baseId, ref)
							 | 
						||
| 
								 | 
							
								  const schOrFunc = root.refs[ref]
							 | 
						||
| 
								 | 
							
								  if (schOrFunc) return schOrFunc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let _sch = resolve.call(this, root, ref)
							 | 
						||
| 
								 | 
							
								  if (_sch === undefined) {
							 | 
						||
| 
								 | 
							
								    const schema = root.localRefs?.[ref] // TODO maybe localRefs should hold SchemaEnv
							 | 
						||
| 
								 | 
							
								    const {schemaId} = this.opts
							 | 
						||
| 
								 | 
							
								    if (schema) _sch = new SchemaEnv({schema, schemaId, root, baseId})
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (_sch === undefined) return
							 | 
						||
| 
								 | 
							
								  return (root.refs[ref] = inlineOrCompile.call(this, _sch))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function inlineOrCompile(this: Ajv, sch: SchemaEnv): AnySchema | SchemaEnv {
							 | 
						||
| 
								 | 
							
								  if (inlineRef(sch.schema, this.opts.inlineRefs)) return sch.schema
							 | 
						||
| 
								 | 
							
								  return sch.validate ? sch : compileSchema.call(this, sch)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Index of schema compilation in the currently compiled list
							 | 
						||
| 
								 | 
							
								export function getCompilingSchema(this: Ajv, schEnv: SchemaEnv): SchemaEnv | void {
							 | 
						||
| 
								 | 
							
								  for (const sch of this._compilations) {
							 | 
						||
| 
								 | 
							
								    if (sameSchemaEnv(sch, schEnv)) return sch
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function sameSchemaEnv(s1: SchemaEnv, s2: SchemaEnv): boolean {
							 | 
						||
| 
								 | 
							
								  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// resolve and compile the references ($ref)
							 | 
						||
| 
								 | 
							
								// TODO returns AnySchemaObject (if the schema can be inlined) or validation function
							 | 
						||
| 
								 | 
							
								function resolve(
							 | 
						||
| 
								 | 
							
								  this: Ajv,
							 | 
						||
| 
								 | 
							
								  root: SchemaEnv, // information about the root schema for the current schema
							 | 
						||
| 
								 | 
							
								  ref: string // reference to resolve
							 | 
						||
| 
								 | 
							
								): SchemaEnv | undefined {
							 | 
						||
| 
								 | 
							
								  let sch
							 | 
						||
| 
								 | 
							
								  while (typeof (sch = this.refs[ref]) == "string") ref = sch
							 | 
						||
| 
								 | 
							
								  return sch || this.schemas[ref] || resolveSchema.call(this, root, ref)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Resolve schema, its root and baseId
							 | 
						||
| 
								 | 
							
								export function resolveSchema(
							 | 
						||
| 
								 | 
							
								  this: Ajv,
							 | 
						||
| 
								 | 
							
								  root: SchemaEnv, // root object with properties schema, refs TODO below SchemaEnv is assigned to it
							 | 
						||
| 
								 | 
							
								  ref: string // reference to resolve
							 | 
						||
| 
								 | 
							
								): SchemaEnv | undefined {
							 | 
						||
| 
								 | 
							
								  const p = this.opts.uriResolver.parse(ref)
							 | 
						||
| 
								 | 
							
								  const refPath = _getFullPath(this.opts.uriResolver, p)
							 | 
						||
| 
								 | 
							
								  let baseId = getFullPath(this.opts.uriResolver, root.baseId, undefined)
							 | 
						||
| 
								 | 
							
								  // TODO `Object.keys(root.schema).length > 0` should not be needed - but removing breaks 2 tests
							 | 
						||
| 
								 | 
							
								  if (Object.keys(root.schema).length > 0 && refPath === baseId) {
							 | 
						||
| 
								 | 
							
								    return getJsonPointer.call(this, p, root)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const id = normalizeId(refPath)
							 | 
						||
| 
								 | 
							
								  const schOrRef = this.refs[id] || this.schemas[id]
							 | 
						||
| 
								 | 
							
								  if (typeof schOrRef == "string") {
							 | 
						||
| 
								 | 
							
								    const sch = resolveSchema.call(this, root, schOrRef)
							 | 
						||
| 
								 | 
							
								    if (typeof sch?.schema !== "object") return
							 | 
						||
| 
								 | 
							
								    return getJsonPointer.call(this, p, sch)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof schOrRef?.schema !== "object") return
							 | 
						||
| 
								 | 
							
								  if (!schOrRef.validate) compileSchema.call(this, schOrRef)
							 | 
						||
| 
								 | 
							
								  if (id === normalizeId(ref)) {
							 | 
						||
| 
								 | 
							
								    const {schema} = schOrRef
							 | 
						||
| 
								 | 
							
								    const {schemaId} = this.opts
							 | 
						||
| 
								 | 
							
								    const schId = schema[schemaId]
							 | 
						||
| 
								 | 
							
								    if (schId) baseId = resolveUrl(this.opts.uriResolver, baseId, schId)
							 | 
						||
| 
								 | 
							
								    return new SchemaEnv({schema, schemaId, root, baseId})
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return getJsonPointer.call(this, p, schOrRef)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const PREVENT_SCOPE_CHANGE = new Set([
							 | 
						||
| 
								 | 
							
								  "properties",
							 | 
						||
| 
								 | 
							
								  "patternProperties",
							 | 
						||
| 
								 | 
							
								  "enum",
							 | 
						||
| 
								 | 
							
								  "dependencies",
							 | 
						||
| 
								 | 
							
								  "definitions",
							 | 
						||
| 
								 | 
							
								])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getJsonPointer(
							 | 
						||
| 
								 | 
							
								  this: Ajv,
							 | 
						||
| 
								 | 
							
								  parsedRef: URI.URIComponents,
							 | 
						||
| 
								 | 
							
								  {baseId, schema, root}: SchemaEnv
							 | 
						||
| 
								 | 
							
								): SchemaEnv | undefined {
							 | 
						||
| 
								 | 
							
								  if (parsedRef.fragment?.[0] !== "/") return
							 | 
						||
| 
								 | 
							
								  for (const part of parsedRef.fragment.slice(1).split("/")) {
							 | 
						||
| 
								 | 
							
								    if (typeof schema === "boolean") return
							 | 
						||
| 
								 | 
							
								    const partSchema = schema[unescapeFragment(part)]
							 | 
						||
| 
								 | 
							
								    if (partSchema === undefined) return
							 | 
						||
| 
								 | 
							
								    schema = partSchema
							 | 
						||
| 
								 | 
							
								    // TODO PREVENT_SCOPE_CHANGE could be defined in keyword def?
							 | 
						||
| 
								 | 
							
								    const schId = typeof schema === "object" && schema[this.opts.schemaId]
							 | 
						||
| 
								 | 
							
								    if (!PREVENT_SCOPE_CHANGE.has(part) && schId) {
							 | 
						||
| 
								 | 
							
								      baseId = resolveUrl(this.opts.uriResolver, baseId, schId)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  let env: SchemaEnv | undefined
							 | 
						||
| 
								 | 
							
								  if (typeof schema != "boolean" && schema.$ref && !schemaHasRulesButRef(schema, this.RULES)) {
							 | 
						||
| 
								 | 
							
								    const $ref = resolveUrl(this.opts.uriResolver, baseId, schema.$ref)
							 | 
						||
| 
								 | 
							
								    env = resolveSchema.call(this, root, $ref)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  // even though resolution failed we need to return SchemaEnv to throw exception
							 | 
						||
| 
								 | 
							
								  // so that compileAsync loads missing schema.
							 | 
						||
| 
								 | 
							
								  const {schemaId} = this.opts
							 | 
						||
| 
								 | 
							
								  env = env || new SchemaEnv({schema, schemaId, root, baseId})
							 | 
						||
| 
								 | 
							
								  if (env.schema !== env.root.schema) return env
							 | 
						||
| 
								 | 
							
								  return undefined
							 | 
						||
| 
								 | 
							
								}
							 |