.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / table / node_modules / ajv / lib / compile / index.ts
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/table/node_modules/ajv/lib/compile/index.ts b/.config/coc/extensions/node_modules/coc-prettier/node_modules/table/node_modules/ajv/lib/compile/index.ts
new file mode 100644 (file)
index 0000000..d612109
--- /dev/null
@@ -0,0 +1,315 @@
+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 root?: SchemaEnv
+  readonly baseId?: string
+  readonly schemaPath?: string
+  readonly localRefs?: LocalRefs
+  readonly meta?: boolean
+}
+
+export class SchemaEnv implements SchemaEnvArgs {
+  readonly schema: AnySchema
+  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.root = env.root || this
+    this.baseId = env.baseId ?? normalizeId(schema?.$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(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(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
+    if (schema) _sch = new SchemaEnv({schema, 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 = URI.parse(ref)
+  const refPath = _getFullPath(p)
+  let baseId = getFullPath(root.baseId)
+  // 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
+    if (schema.$id) baseId = resolveUrl(baseId, schema.$id)
+    return new SchemaEnv({schema, 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
+    schema = schema[unescapeFragment(part)]
+    if (schema === undefined) return
+    // TODO PREVENT_SCOPE_CHANGE could be defined in keyword def?
+    if (!PREVENT_SCOPE_CHANGE.has(part) && typeof schema == "object" && schema.$id) {
+      baseId = resolveUrl(baseId, schema.$id)
+    }
+  }
+  let env: SchemaEnv | undefined
+  if (typeof schema != "boolean" && schema.$ref && !schemaHasRulesButRef(schema, this.RULES)) {
+    const $ref = resolveUrl(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.
+  env = env || new SchemaEnv({schema, root, baseId})
+  if (env.schema !== env.root.schema) return env
+  return undefined
+}