Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@cfworker/json-schema

Package Overview
Dependencies
Maintainers
1
Versions
43
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cfworker/json-schema - npm Package Compare versions

Comparing version 1.1.4 to 1.2.0

6

dist/dereference.js

@@ -89,2 +89,8 @@ import { encodePointer } from './pointer';

}
if (schema.__absolute_uri__ === undefined) {
Object.defineProperty(schema, '__absolute_uri__', {
enumerable: false,
value: schemaURI
});
}
if (schema.$ref && schema.__absolute_ref__ === undefined) {

@@ -91,0 +97,0 @@ const url = new URL(schema.$ref, baseURI);

@@ -44,2 +44,4 @@ export declare type SchemaDraft = '4' | '7' | '2019-09';

contains?: Schema | boolean;
minContains?: number;
maxContains?: number;
minItems?: number;

@@ -57,3 +59,14 @@ maxItems?: number;

__absolute_ref__?: string;
__absolute_uri__?: string;
[key: string]: any;
}
export interface OutputUnit {
keyword: string;
keywordLocation: string;
instanceLocation: string;
error: string;
}
export interface ValidationResult {
valid: boolean;
errors: OutputUnit[];
}

8

dist/validate.d.ts

@@ -1,7 +0,5 @@

import { Schema, SchemaDraft } from './types';
export declare function validate(instance: any, schema: Schema | boolean, draft?: SchemaDraft, lookup?: Record<string, boolean | Schema>, recursiveAnchor?: Schema | null, instancePointer?: string, evaluated?: {
import { Schema, SchemaDraft, ValidationResult } from './types';
export declare function validate(instance: any, schema: Schema | boolean, draft?: SchemaDraft, lookup?: Record<string, boolean | Schema>, recursiveAnchor?: Schema | null, instanceLocation?: string, schemaLocation?: string, evaluated?: {
properties?: Record<string, boolean>;
items?: number;
}): {
valid: boolean;
};
}): ValidationResult;

@@ -6,10 +6,18 @@ import { deepCompareStrict } from './deep-compare-strict';

import { ucs2length } from './ucs2-length';
const validResult = Object.freeze({ valid: true });
const invalidResult = Object.freeze({ valid: false });
export function validate(instance, schema, draft = '2019-09', lookup = dereference(schema), recursiveAnchor = null, instancePointer = '/#', evaluated) {
export function validate(instance, schema, draft = '2019-09', lookup = dereference(schema), recursiveAnchor = null, instanceLocation = '#', schemaLocation = '#', evaluated) {
if (schema === true) {
return validResult;
return { valid: true, errors: [] };
}
if (schema === false) {
return invalidResult;
return {
valid: false,
errors: [
{
instanceLocation,
keyword: 'false',
keywordLocation: instanceLocation,
error: 'False boolean schema.'
}
]
};
}

@@ -40,3 +48,4 @@ const rawInstanceType = typeof instance;

}
const { $ref, $recursiveRef, $recursiveAnchor, type: $type, const: $const, enum: $enum, required: $required, not: $not, anyOf: $anyOf, allOf: $allOf, oneOf: $oneOf, if: $if, then: $then, else: $else, format: $format, properties: $properties, patternProperties: $patternProperties, additionalProperties: $additionalProperties, unevaluatedProperties: $unevaluatedProperties, minProperties: $minProperties, maxProperties: $maxProperties, propertyNames: $propertyNames, dependentRequired: $dependentRequired, dependentSchemas: $dependentSchemas, dependencies: $dependencies, items: $items, additionalItems: $additionalItems, unevaluatedItems: $unevaluatedItems, contains: $contains, minItems: $minItems, maxItems: $maxItems, uniqueItems: $uniqueItems, minimum: $minimum, maximum: $maximum, exclusiveMinimum: $exclusiveMinimum, exclusiveMaximum: $exclusiveMaximum, multipleOf: $multipleOf, minLength: $minLength, maxLength: $maxLength, pattern: $pattern, __absolute_ref__ } = schema;
const { $ref, $recursiveRef, $recursiveAnchor, type: $type, const: $const, enum: $enum, required: $required, not: $not, anyOf: $anyOf, allOf: $allOf, oneOf: $oneOf, if: $if, then: $then, else: $else, format: $format, properties: $properties, patternProperties: $patternProperties, additionalProperties: $additionalProperties, unevaluatedProperties: $unevaluatedProperties, minProperties: $minProperties, maxProperties: $maxProperties, propertyNames: $propertyNames, dependentRequired: $dependentRequired, dependentSchemas: $dependentSchemas, dependencies: $dependencies, items: $items, additionalItems: $additionalItems, unevaluatedItems: $unevaluatedItems, contains: $contains, minContains: $minContains, maxContains: $maxContains, minItems: $minItems, maxItems: $maxItems, uniqueItems: $uniqueItems, minimum: $minimum, maximum: $maximum, exclusiveMinimum: $exclusiveMinimum, exclusiveMaximum: $exclusiveMaximum, multipleOf: $multipleOf, minLength: $minLength, maxLength: $maxLength, pattern: $pattern, __absolute_ref__ } = schema;
const errors = [];
if ($ref !== undefined) {

@@ -53,7 +62,14 @@ const uri = __absolute_ref__ || $ref;

}
if (!validate(instance, refSchema, draft, lookup, recursiveAnchor, instancePointer).valid) {
return invalidResult;
const keywordLocation = `${schemaLocation}/$ref`;
const result = validate(instance, refSchema, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation, evaluated);
if (!result.valid) {
errors.push({
instanceLocation,
keyword: '$ref',
keywordLocation,
error: 'A subschema had errors.'
}, ...result.errors);
}
if (draft === '4' || draft === '7') {
return validResult;
return { valid: errors.length === 0, errors };
}

@@ -64,5 +80,13 @@ }

}
if ($recursiveRef === '#' &&
!validate(instance, recursiveAnchor === null ? schema : recursiveAnchor, draft, lookup, recursiveAnchor, instancePointer).valid) {
return invalidResult;
if ($recursiveRef === '#') {
const keywordLocation = `${schemaLocation}/$recursiveRef`;
const result = validate(instance, recursiveAnchor === null ? schema : recursiveAnchor, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation, evaluated);
if (!result.valid) {
errors.push({
instanceLocation,
keyword: '$recursiveRef',
keywordLocation,
error: 'A subschema had errors.'
}, ...result.errors);
}
}

@@ -83,3 +107,8 @@ if (Array.isArray($type)) {

if (!valid) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'type',
keywordLocation: `${schemaLocation}/type`,
error: `Instance type "${instanceType}" is invalid. Expected "${$type.join('", "')}".`
});
}

@@ -89,7 +118,17 @@ }

if (instanceType !== 'number' || instance % 1 || instance !== instance) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'type',
keywordLocation: `${schemaLocation}/type`,
error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`
});
}
}
else if ($type !== undefined && instanceType !== $type) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'type',
keywordLocation: `${schemaLocation}/type`,
error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`
});
}

@@ -99,7 +138,17 @@ if ($const !== undefined) {

if (!deepCompareStrict(instance, $const)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'const',
keywordLocation: `${schemaLocation}/const`,
error: `Instance does not match ${JSON.stringify($const)}.`
});
}
}
else if (instance !== $const) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'const',
keywordLocation: `${schemaLocation}/const`,
error: `Instance does not match ${JSON.stringify($const)}.`
});
}

@@ -110,35 +159,121 @@ }

if (!$enum.some(value => deepCompareStrict(instance, value))) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'enum',
keywordLocation: `${schemaLocation}/enum`,
error: `Instance does not match any of ${JSON.stringify($enum)}.`
});
}
}
else if (!$enum.some(value => instance === value)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'enum',
keywordLocation: `${schemaLocation}/enum`,
error: `Instance does not match any of ${JSON.stringify($enum)}.`
});
}
}
if ($not !== undefined &&
validate(instance, $not, draft, lookup, recursiveAnchor, instancePointer, evaluated).valid) {
return invalidResult;
if ($not !== undefined) {
const keywordLocation = `${schemaLocation}/not`;
const result = validate(instance, $not, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation);
if (result.valid) {
errors.push({
instanceLocation,
keyword: 'not',
keywordLocation,
error: 'Instance matched "not" schema.'
});
}
}
if ($anyOf !== undefined &&
!$anyOf.some(subSchema => validate(instance, subSchema, draft, lookup, recursiveAnchor, instancePointer, evaluated).valid)) {
return invalidResult;
if ($anyOf !== undefined) {
const keywordLocation = `${schemaLocation}/anyOf`;
const errorsLength = errors.length;
let anyValid = false;
for (let i = 0; i < $anyOf.length; i++) {
const subSchema = $anyOf[i];
const result = validate(instance, subSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);
errors.push(...result.errors);
anyValid = anyValid || result.valid;
}
if (anyValid) {
errors.length = errorsLength;
}
else {
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'anyOf',
keywordLocation,
error: 'Instance does not match any subschemas.'
});
}
}
if ($allOf !== undefined &&
!$allOf.every(subSchema => validate(instance, subSchema, draft, lookup, recursiveAnchor, instancePointer, evaluated).valid)) {
return invalidResult;
if ($allOf !== undefined) {
const keywordLocation = `${schemaLocation}/allOf`;
const errorsLength = errors.length;
let allValid = true;
for (let i = 0; i < $allOf.length; i++) {
const subSchema = $allOf[i];
const result = validate(instance, subSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);
errors.push(...result.errors);
allValid = allValid && result.valid;
}
if (allValid) {
errors.length = errorsLength;
}
else {
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'allOf',
keywordLocation,
error: `Instance does not match every subschema.`
});
}
}
if ($oneOf !== undefined &&
$oneOf.filter(subSchema => validate(instance, subSchema, draft, lookup, recursiveAnchor, instancePointer, evaluated).valid).length !== 1) {
return invalidResult;
if ($oneOf !== undefined) {
const keywordLocation = `${schemaLocation}/oneOf`;
const errorsLength = errors.length;
const matches = $oneOf.filter((subSchema, i) => {
const result = validate(instance, subSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);
errors.push(...result.errors);
return result.valid;
}).length;
if (matches === 1) {
errors.length = errorsLength;
}
else {
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'oneOf',
keywordLocation,
error: `Instance does not match exactly one subschema (${matches} matches).`
});
}
}
if ($if !== undefined) {
if (validate(instance, $if, draft, lookup, recursiveAnchor, instancePointer, evaluated).valid) {
if ($then !== undefined &&
!validate(instance, $then, draft, lookup, recursiveAnchor, instancePointer, evaluated).valid) {
return invalidResult;
const keywordLocation = `${schemaLocation}/if`;
const conditionResult = validate(instance, $if, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation, evaluated).valid;
if (conditionResult) {
if ($then !== undefined) {
const thenResult = validate(instance, $then, draft, lookup, recursiveAnchor, instanceLocation, `${schemaLocation}/then`, evaluated);
if (!thenResult.valid) {
errors.push({
instanceLocation,
keyword: 'if',
keywordLocation,
error: `Instance does not match "then" schema.`
}, ...thenResult.errors);
}
}
}
else if ($else !== undefined &&
!validate(instance, $else, draft, lookup, recursiveAnchor, instancePointer, evaluated).valid) {
return invalidResult;
else if ($else !== undefined) {
const elseResult = validate(instance, $else, draft, lookup, recursiveAnchor, instanceLocation, `${schemaLocation}/else`, evaluated);
if (!elseResult.valid) {
errors.push({
instanceLocation,
keyword: 'if',
keywordLocation,
error: `Instance does not match "else" schema.`
}, ...elseResult.errors);
}
}

@@ -150,3 +285,8 @@ }

if (!(key in instance)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'required',
keywordLocation: `${schemaLocation}/required`,
error: `Instance does not have required property "${key}".`
});
}

@@ -157,11 +297,29 @@ }

if ($minProperties !== undefined && keys.length < $minProperties) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minProperties',
keywordLocation: `${schemaLocation}/minProperties`,
error: `Instance does not have at least ${$minProperties} properties.`
});
}
if ($maxProperties !== undefined && keys.length > $maxProperties) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maxProperties',
keywordLocation: `${schemaLocation}/maxProperties`,
error: `Instance does not have at least ${$maxProperties} properties.`
});
}
if ($propertyNames !== undefined) {
const keywordLocation = `${schemaLocation}/propertyNames`;
for (const key in instance) {
if (!validate(key, $propertyNames, draft, lookup, recursiveAnchor, instancePointer).valid) {
return invalidResult;
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(key, $propertyNames, draft, lookup, recursiveAnchor, subInstancePointer, keywordLocation);
if (!result.valid) {
errors.push({
instanceLocation,
keyword: 'propertyNames',
keywordLocation,
error: `Property name "${key}" does not match schema.`
}, ...result.errors);
}

@@ -171,8 +329,14 @@ }

if ($dependentRequired !== undefined) {
const keywordLocation = `${schemaLocation}/dependantRequired`;
for (const key in $dependentRequired) {
if (key in instance) {
const required = $dependentRequired[key];
for (const key of required) {
if (!(key in instance)) {
return invalidResult;
for (const dependantKey of required) {
if (!(dependantKey in instance)) {
errors.push({
instanceLocation,
keyword: 'dependentRequired',
keywordLocation,
error: `Instance has "${key}" but does not have "${dependantKey}".`
});
}

@@ -185,5 +349,12 @@ }

for (const key in $dependentSchemas) {
const keywordLocation = `${schemaLocation}/dependentSchemas`;
if (key in instance) {
if (!validate(instance, $dependentSchemas[key], draft, lookup, recursiveAnchor, instancePointer).valid) {
return invalidResult;
const result = validate(instance, $dependentSchemas[key], draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${encodePointer(key)}`, evaluated);
if (!result.valid) {
errors.push({
instanceLocation,
keyword: 'dependentSchemas',
keywordLocation,
error: `Instance has "${key}" but does not match dependant schema.`
}, ...result.errors);
}

@@ -194,2 +365,3 @@ }

if ($dependencies !== undefined) {
const keywordLocation = `${schemaLocation}/dependencies`;
for (const key in $dependencies) {

@@ -199,5 +371,10 @@ if (key in instance) {

if (Array.isArray(propsOrSchema)) {
for (const key of propsOrSchema) {
if (!(key in instance)) {
return invalidResult;
for (const dependantKey of propsOrSchema) {
if (!(dependantKey in instance)) {
errors.push({
instanceLocation,
keyword: 'dependencies',
keywordLocation,
error: `Instance has "${key}" but does not have "${dependantKey}".`
});
}

@@ -207,4 +384,10 @@ }

else {
if (!validate(instance, propsOrSchema, draft, lookup, recursiveAnchor, instancePointer).valid) {
return invalidResult;
const result = validate(instance, propsOrSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${encodePointer(key)}`);
if (!result.valid) {
errors.push({
instanceLocation,
keyword: 'dependencies',
keywordLocation,
error: `Instance has "${key}" but does not match dependant schema.`
}, ...result.errors);
}

@@ -219,3 +402,5 @@ }

}
let stop = false;
if ($properties !== undefined) {
const keywordLocation = `${schemaLocation}/properties`;
for (const key in $properties) {

@@ -225,12 +410,21 @@ if (!(key in instance)) {

}
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (validate(instance[key], $properties[key], draft, lookup, recursiveAnchor, subInstancePointer).valid) {
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(instance[key], $properties[key], draft, lookup, recursiveAnchor, subInstancePointer, `${keywordLocation}/${encodePointer(key)}`);
if (result.valid) {
evaluated.properties[key] = thisEvaluated[key] = true;
}
else {
return invalidResult;
stop = true;
errors.push({
instanceLocation,
keyword: 'properties',
keywordLocation,
error: `Property "${key}" does not match schema.`
}, ...result.errors);
break;
}
}
}
if ($patternProperties !== undefined) {
if (!stop && $patternProperties !== undefined) {
const keywordLocation = `${schemaLocation}/patternProperties`;
for (const pattern in $patternProperties) {

@@ -243,8 +437,15 @@ const regex = new RegExp(pattern);

}
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (validate(instance[key], subSchema, draft, lookup, recursiveAnchor, subInstancePointer).valid) {
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(instance[key], subSchema, draft, lookup, recursiveAnchor, subInstancePointer, `${keywordLocation}/${encodePointer(pattern)}`);
if (result.valid) {
evaluated.properties[key] = thisEvaluated[key] = true;
}
else {
return invalidResult;
stop = true;
errors.push({
instanceLocation,
keyword: 'patternProperties',
keywordLocation,
error: `Property "${key}" matches pattern "${pattern}" but does not match associated schema.`
}, ...result.errors);
}

@@ -254,3 +455,4 @@ }

}
if ($additionalProperties !== undefined) {
if (!stop && $additionalProperties !== undefined) {
const keywordLocation = `${schemaLocation}/additionalProperties`;
for (const key in instance) {

@@ -260,18 +462,35 @@ if (thisEvaluated[key]) {

}
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (validate(instance[key], $additionalProperties, draft, lookup, recursiveAnchor, subInstancePointer).valid) {
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(instance[key], $additionalProperties, draft, lookup, recursiveAnchor, subInstancePointer, keywordLocation);
if (result.valid) {
evaluated.properties[key] = true;
}
else {
return invalidResult;
stop = true;
errors.push({
instanceLocation,
keyword: 'additionalProperties',
keywordLocation,
error: `Property "${key}" does not match additional properties schema.`
}, ...result.errors);
}
}
}
else if ($unevaluatedProperties !== undefined) {
else if (!stop && $unevaluatedProperties !== undefined) {
const keywordLocation = `${schemaLocation}/unevaluatedProperties`;
for (const key in instance) {
if (!evaluated.properties[key]) {
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (!validate(instance[key], $unevaluatedProperties, draft, lookup, recursiveAnchor, subInstancePointer).valid) {
return invalidResult;
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(instance[key], $unevaluatedProperties, draft, lookup, recursiveAnchor, subInstancePointer, keywordLocation);
if (result.valid) {
evaluated.properties[key] = true;
}
else {
errors.push({
instanceLocation,
keyword: 'unevaluatedProperties',
keywordLocation,
error: `Property "${key}" does not match unevaluated properties schema.`
}, ...result.errors);
}
}

@@ -283,6 +502,16 @@ }

if ($maxItems !== undefined && instance.length > $maxItems) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maxItems',
keywordLocation: `${schemaLocation}/maxItems`,
error: `Array has too many items (${instance.length} > ${$maxItems}).`
});
}
if ($minItems !== undefined && instance.length < $minItems) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minItems',
keywordLocation: `${schemaLocation}/minItems`,
error: `Array has too few items (${instance.length} < ${$minItems}).`
});
}

@@ -294,8 +523,18 @@ if (!evaluated || evaluated.items === undefined) {

let i = 0;
let stop = false;
if ($items !== undefined) {
const keywordLocation = `${schemaLocation}/items`;
if (Array.isArray($items)) {
const length2 = Math.min($items.length, length);
for (; i < length2; i++) {
if (!validate(instance[i], $items[i], draft, lookup, recursiveAnchor, `${instancePointer}/${i}`).valid) {
return invalidResult;
const result = validate(instance[i], $items[i], draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, `${keywordLocation}/${i}`);
if (!result.valid) {
stop = true;
errors.push({
instanceLocation,
keyword: 'items',
keywordLocation,
error: `Items did not match schema.`
}, ...result.errors);
break;
}

@@ -306,4 +545,12 @@ }

for (; i < length; i++) {
if (!validate(instance[i], $items, draft, lookup, recursiveAnchor, `${instancePointer}/${i}`).valid) {
return invalidResult;
const result = validate(instance[i], $items, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
if (!result.valid) {
stop = true;
errors.push({
instanceLocation,
keyword: 'items',
keywordLocation,
error: `Items did not match schema.`
}, ...result.errors);
break;
}

@@ -313,6 +560,14 @@ }

evaluated.items = Math.max(i, evaluated.items);
if ($additionalItems !== undefined) {
if (!stop && $additionalItems !== undefined) {
const keywordLocation = `${schemaLocation}/additionalItems`;
for (; i < length; i++) {
if (!validate(instance[i], $additionalItems, draft, lookup, recursiveAnchor, `${instancePointer}/${i}`).valid) {
return invalidResult;
const result = validate(instance[i], $additionalItems, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
if (!result.valid) {
stop = true;
errors.push({
instanceLocation,
keyword: 'additionalItems',
keywordLocation,
error: `Items did not match additional items schema.`
}, ...result.errors);
}

@@ -323,23 +578,80 @@ }

}
if ($unevaluatedItems !== undefined) {
if (!stop && $unevaluatedItems !== undefined) {
const keywordLocation = `${schemaLocation}/unevaluatedItems`;
for (i = Math.max(evaluated.items, 0); i < length; i++) {
if (!validate(instance[i], $unevaluatedItems, draft, lookup, recursiveAnchor, `${instancePointer}/${i}`).valid) {
return invalidResult;
const result = validate(instance[i], $unevaluatedItems, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
if (!result.valid) {
errors.push({
instanceLocation,
keyword: 'unevaluatedItems',
keywordLocation,
error: `Items did not match unevaluated items schema.`
}, ...result.errors);
}
}
evaluated.items = Math.max(i, evaluated.items);
}
if ($contains !== undefined) {
if (length === 0) {
return invalidResult;
if (length === 0 && $minContains === undefined) {
errors.push({
instanceLocation,
keyword: 'contains',
keywordLocation: `${schemaLocation}/contains`,
error: `Array is empty. It must contain at least one item matching the schema.`
});
}
let contained = false;
for (let i = 0; i < length; i++) {
if (validate(instance[i], $contains, draft, lookup, recursiveAnchor, `${instancePointer}/${i}`).valid) {
contained = true;
break;
else if ($minContains !== undefined && length < $minContains) {
errors.push({
instanceLocation,
keyword: 'minContains',
keywordLocation: `${schemaLocation}/minContains`,
error: `Array has less items (${length}) than minContains (${$minContains}).`
});
}
else {
const keywordLocation = `${schemaLocation}/contains`;
const errorsLength = errors.length;
let contained = 0;
for (let i = 0; i < length; i++) {
const result = validate(instance[i], $contains, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
if (result.valid) {
contained++;
if ($minContains === undefined && $maxContains === undefined) {
break;
}
}
else {
errors.push(...result.errors);
}
}
if (contained >= ($minContains || 0)) {
errors.length = errorsLength;
}
if ($minContains === undefined &&
$maxContains === undefined &&
contained === 0) {
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'contains',
keywordLocation,
error: `Array does not contain item matching schema.`
});
}
else if ($minContains !== undefined && contained < $minContains) {
errors.push({
instanceLocation,
keyword: 'minContains',
keywordLocation: `${schemaLocation}/minContains`,
error: `Array must contain at least ${$minContains} items matching schema. Only ${contained} items were found.`
});
}
else if ($maxContains !== undefined && contained > $maxContains) {
errors.push({
instanceLocation,
keyword: 'maxContains',
keywordLocation: `${schemaLocation}/maxContains`,
error: `Array may contain at most ${$maxContains} items matching schema. ${contained} items were found.`
});
}
}
if (!contained) {
return invalidResult;
}
}

@@ -357,3 +669,10 @@ if ($uniqueItems) {

if (a === b || (ao && bo && deepCompareStrict(a, b))) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'uniqueItems',
keywordLocation: `${schemaLocation}/uniqueItems`,
error: `Duplicate items at indexes ${j} and ${k}.`
});
j = Number.MAX_SAFE_INTEGER;
k = Number.MAX_SAFE_INTEGER;
}

@@ -369,3 +688,8 @@ }

instance < $minimum)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minimum',
keywordLocation: `${schemaLocation}/minimum`,
error: `${instance} is less than ${$exclusiveMinimum ? 'or equal to ' : ''} ${$minimum}.`
});
}

@@ -375,3 +699,8 @@ if ($maximum !== undefined &&

instance > $maximum)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maximum',
keywordLocation: `${schemaLocation}/maximum`,
error: `${instance} is greater than ${$exclusiveMaximum ? 'or equal to ' : ''} ${$maximum}.`
});
}

@@ -381,12 +710,32 @@ }

if ($minimum !== undefined && instance < $minimum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minimum',
keywordLocation: `${schemaLocation}/minimum`,
error: `${instance} is less than ${$minimum}.`
});
}
if ($maximum !== undefined && instance > $maximum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maximum',
keywordLocation: `${schemaLocation}/maximum`,
error: `${instance} is greater than ${$maximum}.`
});
}
if ($exclusiveMinimum !== undefined && instance <= $exclusiveMinimum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'exclusiveMinimum',
keywordLocation: `${schemaLocation}/exclusiveMinimum`,
error: `${instance} is less than ${$exclusiveMinimum}.`
});
}
if ($exclusiveMaximum !== undefined && instance >= $exclusiveMaximum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'exclusiveMaximum',
keywordLocation: `${schemaLocation}/exclusiveMaximum`,
error: `${instance} is greater than or equal to ${$exclusiveMaximum}.`
});
}

@@ -397,3 +746,8 @@ }

if (division !== Math.floor(division)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'multipleOf',
keywordLocation: `${schemaLocation}/multipleOf`,
error: `${instance} is not a multiple of ${$multipleOf}.`
});
}

@@ -403,10 +757,28 @@ }

else if (instanceType === 'string') {
if ($minLength !== undefined && ucs2length(instance) < $minLength) {
return invalidResult;
const length = $minLength === undefined && $maxLength === undefined
? 0
: ucs2length(instance);
if ($minLength !== undefined && length < $minLength) {
errors.push({
instanceLocation,
keyword: 'minLength',
keywordLocation: `${schemaLocation}/minLength`,
error: `String is too short (${length} < ${$minLength}).`
});
}
if ($maxLength !== undefined && ucs2length(instance) > $maxLength) {
return invalidResult;
if ($maxLength !== undefined && length > $maxLength) {
errors.push({
instanceLocation,
keyword: 'maxLength',
keywordLocation: `${schemaLocation}/maxLength`,
error: `String is too long (${length} > ${$minLength}).`
});
}
if ($pattern !== undefined && !new RegExp($pattern).test(instance)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'pattern',
keywordLocation: `${schemaLocation}/pattern`,
error: `String does not match pattern.`
});
}

@@ -416,6 +788,11 @@ if ($format !== undefined &&

!fastFormat[$format](instance)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'format',
keywordLocation: `${schemaLocation}/format`,
error: `String does not match format "${$format}".`
});
}
}
return validResult;
return { valid: errors.length === 0, errors };
}

@@ -7,6 +7,4 @@ import { Schema, SchemaDraft } from './types';

constructor(schema: Schema | boolean, draft?: SchemaDraft);
validate(instance: any): {
valid: boolean;
};
validate(instance: any): import("./types").ValidationResult;
addSchema(schema: Schema, id?: string): void;
}
{
"name": "@cfworker/json-schema",
"type": "module",
"version": "1.1.4",
"version": "1.2.0",
"description": "A JSON schema validator that will run on Cloudflare workers. Supports drafts 4, 7, and 2019-09.",

@@ -41,11 +41,11 @@ "keywords": [

"devDependencies": {
"@cfworker/dev": "^1.1.4",
"@cfworker/dev": "^1.2.0",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"chai": "^4.2.0",
"json-schema-test-suite": "https://github.com/json-schema-org/JSON-Schema-Test-Suite",
"mocha": "^7.1.1",
"typescript": "^3.8.3"
"json-schema-test-suite": "git+https://github.com/json-schema-org/JSON-Schema-Test-Suite#ac63eb7",
"mocha": "^7.2.0",
"typescript": "^3.9.3"
},
"gitHead": "fd3e8bad07c4fc8987ab612568cea573af27183b"
"gitHead": "8728596ef4f1b597931a613b4e750f8686e66b62"
}

@@ -109,2 +109,10 @@ import { encodePointer } from './pointer';

// set the schema's absolute URI.
if (schema.__absolute_uri__ === undefined) {
Object.defineProperty(schema, '__absolute_uri__', {
enumerable: false,
value: schemaURI
});
}
// if a $ref is found, resolve it's absolute URI.

@@ -111,0 +119,0 @@ if (schema.$ref && schema.__absolute_ref__ === undefined) {

@@ -58,2 +58,4 @@ export type SchemaDraft = '4' | '7' | '2019-09';

contains?: Schema | boolean;
minContains?: number;
maxContains?: number;
minItems?: number;

@@ -74,4 +76,17 @@ maxItems?: number;

__absolute_ref__?: string;
__absolute_uri__?: string;
[key: string]: any;
}
export interface OutputUnit {
keyword: string;
keywordLocation: string;
instanceLocation: string;
error: string;
}
export interface ValidationResult {
valid: boolean;
errors: OutputUnit[];
}

@@ -5,8 +5,11 @@ import { deepCompareStrict } from './deep-compare-strict';

import { encodePointer } from './pointer';
import { InstanceType, Schema, SchemaDraft } from './types';
import {
InstanceType,
OutputUnit,
Schema,
SchemaDraft,
ValidationResult
} from './types';
import { ucs2length } from './ucs2-length';
const validResult = Object.freeze({ valid: true });
const invalidResult = Object.freeze({ valid: false });
export function validate(

@@ -18,11 +21,22 @@ instance: any,

recursiveAnchor: Schema | null = null,
instancePointer = '/#',
instanceLocation = '#',
schemaLocation = '#',
evaluated?: { properties?: Record<string, boolean>; items?: number }
): { valid: boolean } {
): ValidationResult {
if (schema === true) {
return validResult;
return { valid: true, errors: [] };
}
if (schema === false) {
return invalidResult;
return {
valid: false,
errors: [
{
instanceLocation,
keyword: 'false',
keywordLocation: instanceLocation,
error: 'False boolean schema.'
}
]
};
}

@@ -89,2 +103,4 @@

contains: $contains,
minContains: $minContains,
maxContains: $maxContains,
minItems: $minItems,

@@ -107,2 +123,4 @@ maxItems: $maxItems,

const errors: OutputUnit[] = [];
if ($ref !== undefined) {

@@ -119,16 +137,26 @@ const uri = __absolute_ref__ || $ref;

}
if (
!validate(
instance,
refSchema,
draft,
lookup,
recursiveAnchor,
instancePointer
).valid
) {
return invalidResult;
const keywordLocation = `${schemaLocation}/$ref`;
const result = validate(
instance,
refSchema,
draft,
lookup,
recursiveAnchor,
instanceLocation,
keywordLocation,
evaluated
);
if (!result.valid) {
errors.push(
{
instanceLocation,
keyword: '$ref',
keywordLocation,
error: 'A subschema had errors.'
},
...result.errors
);
}
if (draft === '4' || draft === '7') {
return validResult;
return { valid: errors.length === 0, errors };
}

@@ -141,5 +169,5 @@ }

if (
$recursiveRef === '#' &&
!validate(
if ($recursiveRef === '#') {
const keywordLocation = `${schemaLocation}/$recursiveRef`;
const result = validate(
instance,

@@ -150,6 +178,17 @@ recursiveAnchor === null ? schema : recursiveAnchor,

recursiveAnchor,
instancePointer
).valid
) {
return invalidResult;
instanceLocation,
keywordLocation,
evaluated
);
if (!result.valid) {
errors.push(
{
instanceLocation,
keyword: '$recursiveRef',
keywordLocation,
error: 'A subschema had errors.'
},
...result.errors
);
}
}

@@ -173,10 +212,27 @@

if (!valid) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'type',
keywordLocation: `${schemaLocation}/type`,
error: `Instance type "${instanceType}" is invalid. Expected "${$type.join(
'", "'
)}".`
});
}
} else if ($type === 'integer') {
if (instanceType !== 'number' || instance % 1 || instance !== instance) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'type',
keywordLocation: `${schemaLocation}/type`,
error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`
});
}
} else if ($type !== undefined && instanceType !== $type) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'type',
keywordLocation: `${schemaLocation}/type`,
error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`
});
}

@@ -187,6 +243,16 @@

if (!deepCompareStrict(instance, $const)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'const',
keywordLocation: `${schemaLocation}/const`,
error: `Instance does not match ${JSON.stringify($const)}.`
});
}
} else if (instance !== $const) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'const',
keywordLocation: `${schemaLocation}/const`,
error: `Instance does not match ${JSON.stringify($const)}.`
});
}

@@ -198,12 +264,22 @@ }

if (!$enum.some(value => deepCompareStrict(instance, value))) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'enum',
keywordLocation: `${schemaLocation}/enum`,
error: `Instance does not match any of ${JSON.stringify($enum)}.`
});
}
} else if (!$enum.some(value => instance === value)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'enum',
keywordLocation: `${schemaLocation}/enum`,
error: `Instance does not match any of ${JSON.stringify($enum)}.`
});
}
}
if (
$not !== undefined &&
validate(
if ($not !== undefined) {
const keywordLocation = `${schemaLocation}/not`;
const result = validate(
instance,

@@ -214,78 +290,122 @@ $not,

recursiveAnchor,
instancePointer,
evaluated
).valid
) {
return invalidResult;
instanceLocation,
keywordLocation /*,
evaluated*/
);
if (result.valid) {
errors.push({
instanceLocation,
keyword: 'not',
keywordLocation,
error: 'Instance matched "not" schema.'
});
}
}
if (
$anyOf !== undefined &&
!$anyOf.some(
subSchema =>
validate(
instance,
subSchema,
draft,
lookup,
recursiveAnchor,
instancePointer,
evaluated
).valid
)
) {
return invalidResult;
if ($anyOf !== undefined) {
const keywordLocation = `${schemaLocation}/anyOf`;
const errorsLength = errors.length;
let anyValid = false;
for (let i = 0; i < $anyOf.length; i++) {
const subSchema = $anyOf[i];
const result = validate(
instance,
subSchema,
draft,
lookup,
recursiveAnchor,
instanceLocation,
`${keywordLocation}/${i}`,
evaluated
);
errors.push(...result.errors);
anyValid = anyValid || result.valid;
}
if (anyValid) {
errors.length = errorsLength;
} else {
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'anyOf',
keywordLocation,
error: 'Instance does not match any subschemas.'
});
}
}
if (
$allOf !== undefined &&
!$allOf.every(
subSchema =>
validate(
instance,
subSchema,
draft,
lookup,
recursiveAnchor,
instancePointer,
evaluated
).valid
)
) {
return invalidResult;
if ($allOf !== undefined) {
const keywordLocation = `${schemaLocation}/allOf`;
const errorsLength = errors.length;
let allValid = true;
for (let i = 0; i < $allOf.length; i++) {
const subSchema = $allOf[i];
const result = validate(
instance,
subSchema,
draft,
lookup,
recursiveAnchor,
instanceLocation,
`${keywordLocation}/${i}`,
evaluated
);
errors.push(...result.errors);
allValid = allValid && result.valid;
}
if (allValid) {
errors.length = errorsLength;
} else {
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'allOf',
keywordLocation,
error: `Instance does not match every subschema.`
});
}
}
if (
$oneOf !== undefined &&
$oneOf.filter(
subSchema =>
validate(
instance,
subSchema,
draft,
lookup,
recursiveAnchor,
instancePointer,
evaluated
).valid
).length !== 1
) {
return invalidResult;
}
if ($if !== undefined) {
if (
validate(
if ($oneOf !== undefined) {
const keywordLocation = `${schemaLocation}/oneOf`;
const errorsLength = errors.length;
const matches = $oneOf.filter((subSchema, i) => {
const result = validate(
instance,
$if,
subSchema,
draft,
lookup,
recursiveAnchor,
instancePointer,
instanceLocation,
`${keywordLocation}/${i}`,
evaluated
).valid
) {
if (
$then !== undefined &&
!validate(
);
errors.push(...result.errors);
return result.valid;
}).length;
if (matches === 1) {
errors.length = errorsLength;
} else {
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'oneOf',
keywordLocation,
error: `Instance does not match exactly one subschema (${matches} matches).`
});
}
}
if ($if !== undefined) {
const keywordLocation = `${schemaLocation}/if`;
const conditionResult = validate(
instance,
$if,
draft,
lookup,
recursiveAnchor,
instanceLocation,
keywordLocation,
evaluated
).valid;
if (conditionResult) {
if ($then !== undefined) {
const thenResult = validate(
instance,

@@ -296,11 +416,20 @@ $then,

recursiveAnchor,
instancePointer,
instanceLocation,
`${schemaLocation}/then`,
evaluated
).valid
) {
return invalidResult;
);
if (!thenResult.valid) {
errors.push(
{
instanceLocation,
keyword: 'if',
keywordLocation,
error: `Instance does not match "then" schema.`
},
...thenResult.errors
);
}
}
} else if (
$else !== undefined &&
!validate(
} else if ($else !== undefined) {
const elseResult = validate(
instance,

@@ -311,7 +440,17 @@ $else,

recursiveAnchor,
instancePointer,
instanceLocation,
`${schemaLocation}/else`,
evaluated
).valid
) {
return invalidResult;
);
if (!elseResult.valid) {
errors.push(
{
instanceLocation,
keyword: 'if',
keywordLocation,
error: `Instance does not match "else" schema.`
},
...elseResult.errors
);
}
}

@@ -324,3 +463,8 @@ }

if (!(key in instance)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'required',
keywordLocation: `${schemaLocation}/required`,
error: `Instance does not have required property "${key}".`
});
}

@@ -333,22 +477,42 @@ }

if ($minProperties !== undefined && keys.length < $minProperties) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minProperties',
keywordLocation: `${schemaLocation}/minProperties`,
error: `Instance does not have at least ${$minProperties} properties.`
});
}
if ($maxProperties !== undefined && keys.length > $maxProperties) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maxProperties',
keywordLocation: `${schemaLocation}/maxProperties`,
error: `Instance does not have at least ${$maxProperties} properties.`
});
}
if ($propertyNames !== undefined) {
const keywordLocation = `${schemaLocation}/propertyNames`;
for (const key in instance) {
if (
!validate(
key,
$propertyNames,
draft,
lookup,
recursiveAnchor,
instancePointer
).valid
) {
return invalidResult;
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(
key,
$propertyNames,
draft,
lookup,
recursiveAnchor,
subInstancePointer,
keywordLocation
);
if (!result.valid) {
errors.push(
{
instanceLocation,
keyword: 'propertyNames',
keywordLocation,
error: `Property name "${key}" does not match schema.`
},
...result.errors
);
}

@@ -359,8 +523,14 @@ }

if ($dependentRequired !== undefined) {
const keywordLocation = `${schemaLocation}/dependantRequired`;
for (const key in $dependentRequired) {
if (key in instance) {
const required = $dependentRequired[key];
for (const key of required) {
if (!(key in instance)) {
return invalidResult;
for (const dependantKey of required) {
if (!(dependantKey in instance)) {
errors.push({
instanceLocation,
keyword: 'dependentRequired',
keywordLocation,
error: `Instance has "${key}" but does not have "${dependantKey}".`
});
}

@@ -374,14 +544,24 @@ }

for (const key in $dependentSchemas) {
const keywordLocation = `${schemaLocation}/dependentSchemas`;
if (key in instance) {
if (
!validate(
instance,
$dependentSchemas[key],
draft,
lookup,
recursiveAnchor,
instancePointer
).valid
) {
return invalidResult;
const result = validate(
instance,
$dependentSchemas[key],
draft,
lookup,
recursiveAnchor,
instanceLocation,
`${keywordLocation}/${encodePointer(key)}`,
evaluated
);
if (!result.valid) {
errors.push(
{
instanceLocation,
keyword: 'dependentSchemas',
keywordLocation,
error: `Instance has "${key}" but does not match dependant schema.`
},
...result.errors
);
}

@@ -393,2 +573,3 @@ }

if ($dependencies !== undefined) {
const keywordLocation = `${schemaLocation}/dependencies`;
for (const key in $dependencies) {

@@ -398,19 +579,32 @@ if (key in instance) {

if (Array.isArray(propsOrSchema)) {
for (const key of propsOrSchema) {
if (!(key in instance)) {
return invalidResult;
for (const dependantKey of propsOrSchema) {
if (!(dependantKey in instance)) {
errors.push({
instanceLocation,
keyword: 'dependencies',
keywordLocation,
error: `Instance has "${key}" but does not have "${dependantKey}".`
});
}
}
} else {
if (
!validate(
instance,
propsOrSchema,
draft,
lookup,
recursiveAnchor,
instancePointer
).valid
) {
return invalidResult;
const result = validate(
instance,
propsOrSchema,
draft,
lookup,
recursiveAnchor,
instanceLocation,
`${keywordLocation}/${encodePointer(key)}`
);
if (!result.valid) {
errors.push(
{
instanceLocation,
keyword: 'dependencies',
keywordLocation,
error: `Instance has "${key}" but does not match dependant schema.`
},
...result.errors
);
}

@@ -427,3 +621,6 @@ }

let stop = false;
if ($properties !== undefined) {
const keywordLocation = `${schemaLocation}/properties`;
for (const key in $properties) {

@@ -433,20 +630,32 @@ if (!(key in instance)) {

}
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (
validate(
instance[key],
$properties[key],
draft,
lookup,
recursiveAnchor,
subInstancePointer
).valid
) {
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(
instance[key],
$properties[key],
draft,
lookup,
recursiveAnchor,
subInstancePointer,
`${keywordLocation}/${encodePointer(key)}`
);
if (result.valid) {
evaluated.properties[key] = thisEvaluated[key] = true;
} else {
return invalidResult;
stop = true;
errors.push(
{
instanceLocation,
keyword: 'properties',
keywordLocation,
error: `Property "${key}" does not match schema.`
},
...result.errors
);
break;
}
}
}
if ($patternProperties !== undefined) {
if (!stop && $patternProperties !== undefined) {
const keywordLocation = `${schemaLocation}/patternProperties`;
for (const pattern in $patternProperties) {

@@ -459,16 +668,27 @@ const regex = new RegExp(pattern);

}
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (
validate(
instance[key],
subSchema,
draft,
lookup,
recursiveAnchor,
subInstancePointer
).valid
) {
const subInstancePointer = `${instanceLocation}/${encodePointer(
key
)}`;
const result = validate(
instance[key],
subSchema,
draft,
lookup,
recursiveAnchor,
subInstancePointer,
`${keywordLocation}/${encodePointer(pattern)}`
);
if (result.valid) {
evaluated.properties[key] = thisEvaluated[key] = true;
} else {
return invalidResult;
stop = true;
errors.push(
{
instanceLocation,
keyword: 'patternProperties',
keywordLocation,
error: `Property "${key}" matches pattern "${pattern}" but does not match associated schema.`
},
...result.errors
);
}

@@ -478,3 +698,5 @@ }

}
if ($additionalProperties !== undefined) {
if (!stop && $additionalProperties !== undefined) {
const keywordLocation = `${schemaLocation}/additionalProperties`;
for (const key in instance) {

@@ -484,33 +706,55 @@ if (thisEvaluated[key]) {

}
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (
validate(
instance[key],
$additionalProperties,
draft,
lookup,
recursiveAnchor,
subInstancePointer
).valid
) {
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;
const result = validate(
instance[key],
$additionalProperties,
draft,
lookup,
recursiveAnchor,
subInstancePointer,
keywordLocation
);
if (result.valid) {
evaluated.properties[key] = true;
} else {
return invalidResult;
stop = true;
errors.push(
{
instanceLocation,
keyword: 'additionalProperties',
keywordLocation,
error: `Property "${key}" does not match additional properties schema.`
},
...result.errors
);
}
}
} else if ($unevaluatedProperties !== undefined) {
} else if (!stop && $unevaluatedProperties !== undefined) {
const keywordLocation = `${schemaLocation}/unevaluatedProperties`;
for (const key in instance) {
if (!evaluated.properties[key]) {
const subInstancePointer = `${instancePointer}/${encodePointer(key)}`;
if (
!validate(
instance[key],
$unevaluatedProperties,
draft,
lookup,
recursiveAnchor,
subInstancePointer
).valid
) {
return invalidResult;
const subInstancePointer = `${instanceLocation}/${encodePointer(
key
)}`;
const result = validate(
instance[key],
$unevaluatedProperties,
draft,
lookup,
recursiveAnchor,
subInstancePointer,
keywordLocation
);
if (result.valid) {
evaluated.properties[key] = true;
} else {
errors.push(
{
instanceLocation,
keyword: 'unevaluatedProperties',
keywordLocation,
error: `Property "${key}" does not match unevaluated properties schema.`
},
...result.errors
);
}

@@ -522,7 +766,17 @@ }

if ($maxItems !== undefined && instance.length > $maxItems) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maxItems',
keywordLocation: `${schemaLocation}/maxItems`,
error: `Array has too many items (${instance.length} > ${$maxItems}).`
});
}
if ($minItems !== undefined && instance.length < $minItems) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minItems',
keywordLocation: `${schemaLocation}/minItems`,
error: `Array has too few items (${instance.length} < ${$minItems}).`
});
}

@@ -536,17 +790,29 @@

let i = 0;
let stop = false;
if ($items !== undefined) {
const keywordLocation = `${schemaLocation}/items`;
if (Array.isArray($items)) {
const length2 = Math.min($items.length, length);
for (; i < length2; i++) {
if (
!validate(
instance[i],
$items[i],
draft,
lookup,
recursiveAnchor,
`${instancePointer}/${i}`
).valid
) {
return invalidResult;
const result = validate(
instance[i],
$items[i],
draft,
lookup,
recursiveAnchor,
`${instanceLocation}/${i}`,
`${keywordLocation}/${i}`
);
if (!result.valid) {
stop = true;
errors.push(
{
instanceLocation,
keyword: 'items',
keywordLocation,
error: `Items did not match schema.`
},
...result.errors
);
break;
}

@@ -556,13 +822,23 @@ }

for (; i < length; i++) {
if (
!validate(
instance[i],
$items,
draft,
lookup,
recursiveAnchor,
`${instancePointer}/${i}`
).valid
) {
return invalidResult;
const result = validate(
instance[i],
$items,
draft,
lookup,
recursiveAnchor,
`${instanceLocation}/${i}`,
keywordLocation
);
if (!result.valid) {
stop = true;
errors.push(
{
instanceLocation,
keyword: 'items',
keywordLocation,
error: `Items did not match schema.`
},
...result.errors
);
break;
}

@@ -574,15 +850,25 @@ }

if ($additionalItems !== undefined) {
if (!stop && $additionalItems !== undefined) {
const keywordLocation = `${schemaLocation}/additionalItems`;
for (; i < length; i++) {
if (
!validate(
instance[i],
$additionalItems,
draft,
lookup,
recursiveAnchor,
`${instancePointer}/${i}`
).valid
) {
return invalidResult;
const result = validate(
instance[i],
$additionalItems,
draft,
lookup,
recursiveAnchor,
`${instanceLocation}/${i}`,
keywordLocation
);
if (!result.valid) {
stop = true;
errors.push(
{
instanceLocation,
keyword: 'additionalItems',
keywordLocation,
error: `Items did not match additional items schema.`
},
...result.errors
);
}

@@ -594,27 +880,50 @@ }

if ($unevaluatedItems !== undefined) {
if (!stop && $unevaluatedItems !== undefined) {
const keywordLocation = `${schemaLocation}/unevaluatedItems`;
for (i = Math.max(evaluated.items, 0); i < length; i++) {
if (
!validate(
instance[i],
$unevaluatedItems,
draft,
lookup,
recursiveAnchor,
`${instancePointer}/${i}`
).valid
) {
return invalidResult;
const result = validate(
instance[i],
$unevaluatedItems,
draft,
lookup,
recursiveAnchor,
`${instanceLocation}/${i}`,
keywordLocation
);
if (!result.valid) {
errors.push(
{
instanceLocation,
keyword: 'unevaluatedItems',
keywordLocation,
error: `Items did not match unevaluated items schema.`
},
...result.errors
);
}
}
evaluated.items = Math.max(i, evaluated.items);
}
if ($contains !== undefined) {
if (length === 0) {
return invalidResult;
}
let contained = false;
for (let i = 0; i < length; i++) {
if (
validate(
if (length === 0 && $minContains === undefined) {
errors.push({
instanceLocation,
keyword: 'contains',
keywordLocation: `${schemaLocation}/contains`,
error: `Array is empty. It must contain at least one item matching the schema.`
});
} else if ($minContains !== undefined && length < $minContains) {
errors.push({
instanceLocation,
keyword: 'minContains',
keywordLocation: `${schemaLocation}/minContains`,
error: `Array has less items (${length}) than minContains (${$minContains}).`
});
} else {
const keywordLocation = `${schemaLocation}/contains`;
const errorsLength = errors.length;
let contained = 0;
for (let i = 0; i < length; i++) {
const result = validate(
instance[i],

@@ -625,12 +934,46 @@ $contains,

recursiveAnchor,
`${instancePointer}/${i}`
).valid
`${instanceLocation}/${i}`,
keywordLocation
);
if (result.valid) {
contained++;
if ($minContains === undefined && $maxContains === undefined) {
break;
}
} else {
errors.push(...result.errors);
}
}
if (contained >= ($minContains || 0)) {
errors.length = errorsLength;
}
if (
$minContains === undefined &&
$maxContains === undefined &&
contained === 0
) {
contained = true;
break;
errors.splice(errorsLength, 0, {
instanceLocation,
keyword: 'contains',
keywordLocation,
error: `Array does not contain item matching schema.`
});
} else if ($minContains !== undefined && contained < $minContains) {
errors.push({
instanceLocation,
keyword: 'minContains',
keywordLocation: `${schemaLocation}/minContains`,
error: `Array must contain at least ${$minContains} items matching schema. Only ${contained} items were found.`
});
} else if ($maxContains !== undefined && contained > $maxContains) {
errors.push({
instanceLocation,
keyword: 'maxContains',
keywordLocation: `${schemaLocation}/maxContains`,
error: `Array may contain at most ${$maxContains} items matching schema. ${contained} items were found.`
});
}
}
if (!contained) {
return invalidResult;
}
}

@@ -649,3 +992,10 @@

if (a === b || (ao && bo && deepCompareStrict(a, b))) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'uniqueItems',
keywordLocation: `${schemaLocation}/uniqueItems`,
error: `Duplicate items at indexes ${j} and ${k}.`
});
j = Number.MAX_SAFE_INTEGER;
k = Number.MAX_SAFE_INTEGER;
}

@@ -662,3 +1012,10 @@ }

) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minimum',
keywordLocation: `${schemaLocation}/minimum`,
error: `${instance} is less than ${
$exclusiveMinimum ? 'or equal to ' : ''
} ${$minimum}.`
});
}

@@ -670,16 +1027,43 @@ if (

) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maximum',
keywordLocation: `${schemaLocation}/maximum`,
error: `${instance} is greater than ${
$exclusiveMaximum ? 'or equal to ' : ''
} ${$maximum}.`
});
}
} else {
if ($minimum !== undefined && instance < $minimum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'minimum',
keywordLocation: `${schemaLocation}/minimum`,
error: `${instance} is less than ${$minimum}.`
});
}
if ($maximum !== undefined && instance > $maximum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'maximum',
keywordLocation: `${schemaLocation}/maximum`,
error: `${instance} is greater than ${$maximum}.`
});
}
if ($exclusiveMinimum !== undefined && instance <= $exclusiveMinimum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'exclusiveMinimum',
keywordLocation: `${schemaLocation}/exclusiveMinimum`,
error: `${instance} is less than ${$exclusiveMinimum}.`
});
}
if ($exclusiveMaximum !== undefined && instance >= $exclusiveMaximum) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'exclusiveMaximum',
keywordLocation: `${schemaLocation}/exclusiveMaximum`,
error: `${instance} is greater than or equal to ${$exclusiveMaximum}.`
});
}

@@ -690,14 +1074,38 @@ }

if (division !== Math.floor(division)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'multipleOf',
keywordLocation: `${schemaLocation}/multipleOf`,
error: `${instance} is not a multiple of ${$multipleOf}.`
});
}
}
} else if (instanceType === 'string') {
if ($minLength !== undefined && ucs2length(instance) < $minLength) {
return invalidResult;
const length =
$minLength === undefined && $maxLength === undefined
? 0
: ucs2length(instance);
if ($minLength !== undefined && length < $minLength) {
errors.push({
instanceLocation,
keyword: 'minLength',
keywordLocation: `${schemaLocation}/minLength`,
error: `String is too short (${length} < ${$minLength}).`
});
}
if ($maxLength !== undefined && ucs2length(instance) > $maxLength) {
return invalidResult;
if ($maxLength !== undefined && length > $maxLength) {
errors.push({
instanceLocation,
keyword: 'maxLength',
keywordLocation: `${schemaLocation}/maxLength`,
error: `String is too long (${length} > ${$minLength}).`
});
}
if ($pattern !== undefined && !new RegExp($pattern).test(instance)) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'pattern',
keywordLocation: `${schemaLocation}/pattern`,
error: `String does not match pattern.`
});
}

@@ -709,7 +1117,12 @@ if (

) {
return invalidResult;
errors.push({
instanceLocation,
keyword: 'format',
keywordLocation: `${schemaLocation}/format`,
error: `String does not match format "${$format}".`
});
}
}
return validResult;
return { valid: errors.length === 0, errors };
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc