aboutsummaryrefslogtreecommitdiff
path: root/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation
diff options
context:
space:
mode:
authorDanijel Andjelkovic <adanijel99@gmail.com>2022-03-01 21:54:41 +0100
committerDanijel Andjelkovic <adanijel99@gmail.com>2022-03-01 21:54:41 +0100
commit6c8128f9fd5a5d0be115806c35a21b3d683df8d6 (patch)
treef46c2f6b3b9b294ff32bd75c08ccdc9e7a8cc4ef /sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation
parent2400b84e95913665da6279114168148444b8f9ab (diff)
parent7d3640f824f46490b47bd95f1c5a16644f712068 (diff)
Merge branch 'dev' of http://gitlab.pmf.kg.ac.rs/igrannonica/neuronstellar into logo
Diffstat (limited to 'sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation')
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/const.ts28
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts23
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/enum.ts52
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/index.ts49
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitContains.ts16
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitItems.ts26
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitLength.ts30
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts42
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts26
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts34
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/pattern.ts28
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/required.ts98
-rw-r--r--sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts79
13 files changed, 531 insertions, 0 deletions
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/const.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/const.ts
new file mode 100644
index 00000000..a3b94a5d
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/const.ts
@@ -0,0 +1,28 @@
+import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {_} from "../../compile/codegen"
+import {useFunc} from "../../compile/util"
+import equal from "../../runtime/equal"
+
+export type ConstError = ErrorObject<"const", {allowedValue: any}>
+
+const error: KeywordErrorDefinition = {
+ message: "must be equal to constant",
+ params: ({schemaCode}) => _`{allowedValue: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: "const",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {gen, data, $data, schemaCode, schema} = cxt
+ if ($data || (schema && typeof schema == "object")) {
+ cxt.fail$data(_`!${useFunc(gen, equal)}(${data}, ${schemaCode})`)
+ } else {
+ cxt.fail(_`${schema} !== ${data}`)
+ }
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts
new file mode 100644
index 00000000..4c616cfa
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts
@@ -0,0 +1,23 @@
+import type {CodeKeywordDefinition, ErrorObject} from "../../types"
+import {
+ validatePropertyDeps,
+ error,
+ DependenciesErrorParams,
+ PropertyDependencies,
+} from "../applicator/dependencies"
+
+export type DependentRequiredError = ErrorObject<
+ "dependentRequired",
+ DependenciesErrorParams,
+ PropertyDependencies
+>
+
+const def: CodeKeywordDefinition = {
+ keyword: "dependentRequired",
+ type: "object",
+ schemaType: "object",
+ error,
+ code: (cxt) => validatePropertyDeps(cxt),
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/enum.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/enum.ts
new file mode 100644
index 00000000..fa85373c
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/enum.ts
@@ -0,0 +1,52 @@
+import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {_, or, Name, Code} from "../../compile/codegen"
+import {useFunc} from "../../compile/util"
+import equal from "../../runtime/equal"
+
+export type EnumError = ErrorObject<"enum", {allowedValues: any[]}, any[] | {$data: string}>
+
+const error: KeywordErrorDefinition = {
+ message: "must be equal to one of the allowed values",
+ params: ({schemaCode}) => _`{allowedValues: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: "enum",
+ schemaType: "array",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {gen, data, $data, schema, schemaCode, it} = cxt
+ if (!$data && schema.length === 0) throw new Error("enum must have non-empty array")
+ const useLoop = schema.length >= it.opts.loopEnum
+ const eql = useFunc(gen, equal)
+ let valid: Code
+ if (useLoop || $data) {
+ valid = gen.let("valid")
+ cxt.block$data(valid, loopEnum)
+ } else {
+ /* istanbul ignore if */
+ if (!Array.isArray(schema)) throw new Error("ajv implementation error")
+ const vSchema = gen.const("vSchema", schemaCode)
+ valid = or(...schema.map((_x: unknown, i: number) => equalCode(vSchema, i)))
+ }
+ cxt.pass(valid)
+
+ function loopEnum(): void {
+ gen.assign(valid, false)
+ gen.forOf("v", schemaCode as Code, (v) =>
+ gen.if(_`${eql}(${data}, ${v})`, () => gen.assign(valid, true).break())
+ )
+ }
+
+ function equalCode(vSchema: Name, i: number): Code {
+ const sch = schema[i]
+ return typeof sch === "object" && sch !== null
+ ? _`${eql}(${data}, ${vSchema}[${i}])`
+ : _`${data} === ${sch}`
+ }
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/index.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/index.ts
new file mode 100644
index 00000000..3531b196
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/index.ts
@@ -0,0 +1,49 @@
+import type {ErrorObject, Vocabulary} from "../../types"
+import limitNumber, {LimitNumberError} from "./limitNumber"
+import multipleOf, {MultipleOfError} from "./multipleOf"
+import limitLength from "./limitLength"
+import pattern, {PatternError} from "./pattern"
+import limitProperties from "./limitProperties"
+import required, {RequiredError} from "./required"
+import limitItems from "./limitItems"
+import uniqueItems, {UniqueItemsError} from "./uniqueItems"
+import constKeyword, {ConstError} from "./const"
+import enumKeyword, {EnumError} from "./enum"
+
+const validation: Vocabulary = [
+ // number
+ limitNumber,
+ multipleOf,
+ // string
+ limitLength,
+ pattern,
+ // object
+ limitProperties,
+ required,
+ // array
+ limitItems,
+ uniqueItems,
+ // any
+ {keyword: "type", schemaType: ["string", "array"]},
+ {keyword: "nullable", schemaType: "boolean"},
+ constKeyword,
+ enumKeyword,
+]
+
+export default validation
+
+type LimitError = ErrorObject<
+ "maxItems" | "minItems" | "minProperties" | "maxProperties" | "minLength" | "maxLength",
+ {limit: number},
+ number | {$data: string}
+>
+
+export type ValidationKeywordError =
+ | LimitError
+ | LimitNumberError
+ | MultipleOfError
+ | PatternError
+ | RequiredError
+ | UniqueItemsError
+ | ConstError
+ | EnumError
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitContains.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitContains.ts
new file mode 100644
index 00000000..8bb43c1a
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitContains.ts
@@ -0,0 +1,16 @@
+import type {CodeKeywordDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {checkStrictMode} from "../../compile/util"
+
+const def: CodeKeywordDefinition = {
+ keyword: ["maxContains", "minContains"],
+ type: "array",
+ schemaType: "number",
+ code({keyword, parentSchema, it}: KeywordCxt) {
+ if (parentSchema.contains === undefined) {
+ checkStrictMode(it, `"${keyword}" without "contains" is ignored`)
+ }
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitItems.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitItems.ts
new file mode 100644
index 00000000..566de858
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitItems.ts
@@ -0,0 +1,26 @@
+import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {_, str, operators} from "../../compile/codegen"
+
+const error: KeywordErrorDefinition = {
+ message({keyword, schemaCode}) {
+ const comp = keyword === "maxItems" ? "more" : "fewer"
+ return str`must NOT have ${comp} than ${schemaCode} items`
+ },
+ params: ({schemaCode}) => _`{limit: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: ["maxItems", "minItems"],
+ type: "array",
+ schemaType: "number",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {keyword, data, schemaCode} = cxt
+ const op = keyword === "maxItems" ? operators.GT : operators.LT
+ cxt.fail$data(_`${data}.length ${op} ${schemaCode}`)
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitLength.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitLength.ts
new file mode 100644
index 00000000..f4f94725
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitLength.ts
@@ -0,0 +1,30 @@
+import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {_, str, operators} from "../../compile/codegen"
+import {useFunc} from "../../compile/util"
+import ucs2length from "../../runtime/ucs2length"
+
+const error: KeywordErrorDefinition = {
+ message({keyword, schemaCode}) {
+ const comp = keyword === "maxLength" ? "more" : "fewer"
+ return str`must NOT have ${comp} than ${schemaCode} characters`
+ },
+ params: ({schemaCode}) => _`{limit: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: ["maxLength", "minLength"],
+ type: "string",
+ schemaType: "number",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {keyword, data, schemaCode, it} = cxt
+ const op = keyword === "maxLength" ? operators.GT : operators.LT
+ const len =
+ it.opts.unicode === false ? _`${data}.length` : _`${useFunc(cxt.gen, ucs2length)}(${data})`
+ cxt.fail$data(_`${len} ${op} ${schemaCode}`)
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts
new file mode 100644
index 00000000..5499202e
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts
@@ -0,0 +1,42 @@
+import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {_, str, operators, Code} from "../../compile/codegen"
+
+const ops = operators
+
+type Kwd = "maximum" | "minimum" | "exclusiveMaximum" | "exclusiveMinimum"
+
+type Comparison = "<=" | ">=" | "<" | ">"
+
+const KWDs: {[K in Kwd]: {okStr: Comparison; ok: Code; fail: Code}} = {
+ maximum: {okStr: "<=", ok: ops.LTE, fail: ops.GT},
+ minimum: {okStr: ">=", ok: ops.GTE, fail: ops.LT},
+ exclusiveMaximum: {okStr: "<", ok: ops.LT, fail: ops.GTE},
+ exclusiveMinimum: {okStr: ">", ok: ops.GT, fail: ops.LTE},
+}
+
+export type LimitNumberError = ErrorObject<
+ Kwd,
+ {limit: number; comparison: Comparison},
+ number | {$data: string}
+>
+
+const error: KeywordErrorDefinition = {
+ message: ({keyword, schemaCode}) => str`must be ${KWDs[keyword as Kwd].okStr} ${schemaCode}`,
+ params: ({keyword, schemaCode}) =>
+ _`{comparison: ${KWDs[keyword as Kwd].okStr}, limit: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: Object.keys(KWDs),
+ type: "number",
+ schemaType: "number",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {keyword, data, schemaCode} = cxt
+ cxt.fail$data(_`${data} ${KWDs[keyword as Kwd].fail} ${schemaCode} || isNaN(${data})`)
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts
new file mode 100644
index 00000000..e72124a7
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts
@@ -0,0 +1,26 @@
+import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {_, str, operators} from "../../compile/codegen"
+
+const error: KeywordErrorDefinition = {
+ message({keyword, schemaCode}) {
+ const comp = keyword === "maxProperties" ? "more" : "fewer"
+ return str`must NOT have ${comp} than ${schemaCode} items`
+ },
+ params: ({schemaCode}) => _`{limit: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: ["maxProperties", "minProperties"],
+ type: "object",
+ schemaType: "number",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {keyword, data, schemaCode} = cxt
+ const op = keyword === "maxProperties" ? operators.GT : operators.LT
+ cxt.fail$data(_`Object.keys(${data}).length ${op} ${schemaCode}`)
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts
new file mode 100644
index 00000000..1fd79abb
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts
@@ -0,0 +1,34 @@
+import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {_, str} from "../../compile/codegen"
+
+export type MultipleOfError = ErrorObject<
+ "multipleOf",
+ {multipleOf: number},
+ number | {$data: string}
+>
+
+const error: KeywordErrorDefinition = {
+ message: ({schemaCode}) => str`must be multiple of ${schemaCode}`,
+ params: ({schemaCode}) => _`{multipleOf: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: "multipleOf",
+ type: "number",
+ schemaType: "number",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {gen, data, schemaCode, it} = cxt
+ // const bdt = bad$DataType(schemaCode, <string>def.schemaType, $data)
+ const prec = it.opts.multipleOfPrecision
+ const res = gen.let("res")
+ const invalid = prec
+ ? _`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}`
+ : _`${res} !== parseInt(${res})`
+ cxt.fail$data(_`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`)
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/pattern.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/pattern.ts
new file mode 100644
index 00000000..7b27b7d3
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/pattern.ts
@@ -0,0 +1,28 @@
+import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {usePattern} from "../code"
+import {_, str} from "../../compile/codegen"
+
+export type PatternError = ErrorObject<"pattern", {pattern: string}, string | {$data: string}>
+
+const error: KeywordErrorDefinition = {
+ message: ({schemaCode}) => str`must match pattern "${schemaCode}"`,
+ params: ({schemaCode}) => _`{pattern: ${schemaCode}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: "pattern",
+ type: "string",
+ schemaType: "string",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {data, $data, schema, schemaCode, it} = cxt
+ // TODO regexp should be wrapped in try/catchs
+ const u = it.opts.unicodeRegExp ? "u" : ""
+ const regExp = $data ? _`(new RegExp(${schemaCode}, ${u}))` : usePattern(cxt, schema)
+ cxt.fail$data(_`!${regExp}.test(${data})`)
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/required.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/required.ts
new file mode 100644
index 00000000..fea7367e
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/required.ts
@@ -0,0 +1,98 @@
+import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {
+ checkReportMissingProp,
+ checkMissingProp,
+ reportMissingProp,
+ propertyInData,
+ noPropertyInData,
+} from "../code"
+import {_, str, nil, not, Name, Code} from "../../compile/codegen"
+import {checkStrictMode} from "../../compile/util"
+
+export type RequiredError = ErrorObject<
+ "required",
+ {missingProperty: string},
+ string[] | {$data: string}
+>
+
+const error: KeywordErrorDefinition = {
+ message: ({params: {missingProperty}}) => str`must have required property '${missingProperty}'`,
+ params: ({params: {missingProperty}}) => _`{missingProperty: ${missingProperty}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: "required",
+ type: "object",
+ schemaType: "array",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {gen, schema, schemaCode, data, $data, it} = cxt
+ const {opts} = it
+ if (!$data && schema.length === 0) return
+ const useLoop = schema.length >= opts.loopRequired
+ if (it.allErrors) allErrorsMode()
+ else exitOnErrorMode()
+
+ if (opts.strictRequired) {
+ const props = cxt.parentSchema.properties
+ const {definedProperties} = cxt.it
+ for (const requiredKey of schema) {
+ if (props?.[requiredKey] === undefined && !definedProperties.has(requiredKey)) {
+ const schemaPath = it.schemaEnv.baseId + it.errSchemaPath
+ const msg = `required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)`
+ checkStrictMode(it, msg, it.opts.strictRequired)
+ }
+ }
+ }
+
+ function allErrorsMode(): void {
+ if (useLoop || $data) {
+ cxt.block$data(nil, loopAllRequired)
+ } else {
+ for (const prop of schema) {
+ checkReportMissingProp(cxt, prop)
+ }
+ }
+ }
+
+ function exitOnErrorMode(): void {
+ const missing = gen.let("missing")
+ if (useLoop || $data) {
+ const valid = gen.let("valid", true)
+ cxt.block$data(valid, () => loopUntilMissing(missing, valid))
+ cxt.ok(valid)
+ } else {
+ gen.if(checkMissingProp(cxt, schema, missing))
+ reportMissingProp(cxt, missing)
+ gen.else()
+ }
+ }
+
+ function loopAllRequired(): void {
+ gen.forOf("prop", schemaCode as Code, (prop) => {
+ cxt.setParams({missingProperty: prop})
+ gen.if(noPropertyInData(gen, data, prop, opts.ownProperties), () => cxt.error())
+ })
+ }
+
+ function loopUntilMissing(missing: Name, valid: Name): void {
+ cxt.setParams({missingProperty: missing})
+ gen.forOf(
+ missing,
+ schemaCode as Code,
+ () => {
+ gen.assign(valid, propertyInData(gen, data, missing, opts.ownProperties))
+ gen.if(not(valid), () => {
+ cxt.error()
+ gen.break()
+ })
+ },
+ nil
+ )
+ }
+ },
+}
+
+export default def
diff --git a/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts
new file mode 100644
index 00000000..765c4d04
--- /dev/null
+++ b/sandbox/testAppNevena/Front/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts
@@ -0,0 +1,79 @@
+import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
+import type {KeywordCxt} from "../../compile/validate"
+import {checkDataTypes, getSchemaTypes, DataType} from "../../compile/validate/dataType"
+import {_, str, Name} from "../../compile/codegen"
+import {useFunc} from "../../compile/util"
+import equal from "../../runtime/equal"
+
+export type UniqueItemsError = ErrorObject<
+ "uniqueItems",
+ {i: number; j: number},
+ boolean | {$data: string}
+>
+
+const error: KeywordErrorDefinition = {
+ message: ({params: {i, j}}) =>
+ str`must NOT have duplicate items (items ## ${j} and ${i} are identical)`,
+ params: ({params: {i, j}}) => _`{i: ${i}, j: ${j}}`,
+}
+
+const def: CodeKeywordDefinition = {
+ keyword: "uniqueItems",
+ type: "array",
+ schemaType: "boolean",
+ $data: true,
+ error,
+ code(cxt: KeywordCxt) {
+ const {gen, data, $data, schema, parentSchema, schemaCode, it} = cxt
+ if (!$data && !schema) return
+ const valid = gen.let("valid")
+ const itemTypes = parentSchema.items ? getSchemaTypes(parentSchema.items) : []
+ cxt.block$data(valid, validateUniqueItems, _`${schemaCode} === false`)
+ cxt.ok(valid)
+
+ function validateUniqueItems(): void {
+ const i = gen.let("i", _`${data}.length`)
+ const j = gen.let("j")
+ cxt.setParams({i, j})
+ gen.assign(valid, true)
+ gen.if(_`${i} > 1`, () => (canOptimize() ? loopN : loopN2)(i, j))
+ }
+
+ function canOptimize(): boolean {
+ return itemTypes.length > 0 && !itemTypes.some((t) => t === "object" || t === "array")
+ }
+
+ function loopN(i: Name, j: Name): void {
+ const item = gen.name("item")
+ const wrongType = checkDataTypes(itemTypes, item, it.opts.strictNumbers, DataType.Wrong)
+ const indices = gen.const("indices", _`{}`)
+ gen.for(_`;${i}--;`, () => {
+ gen.let(item, _`${data}[${i}]`)
+ gen.if(wrongType, _`continue`)
+ if (itemTypes.length > 1) gen.if(_`typeof ${item} == "string"`, _`${item} += "_"`)
+ gen
+ .if(_`typeof ${indices}[${item}] == "number"`, () => {
+ gen.assign(j, _`${indices}[${item}]`)
+ cxt.error()
+ gen.assign(valid, false).break()
+ })
+ .code(_`${indices}[${item}] = ${i}`)
+ })
+ }
+
+ function loopN2(i: Name, j: Name): void {
+ const eql = useFunc(gen, equal)
+ const outer = gen.name("outer")
+ gen.label(outer).for(_`;${i}--;`, () =>
+ gen.for(_`${j} = ${i}; ${j}--;`, () =>
+ gen.if(_`${eql}(${data}[${i}], ${data}[${j}])`, () => {
+ cxt.error()
+ gen.assign(valid, false).break(outer)
+ })
+ )
+ )
+ }
+ },
+}
+
+export default def