json-schema-library
Advanced tools
Comparing version 5.3.0 to 6.0.0
import Interface from "./lib/cores/CoreInterface"; | ||
import Draft04 from "./lib/cores/Draft04"; | ||
import Draft06 from "./lib/cores/Draft06"; | ||
import Draft07 from "./lib/cores/Draft07"; | ||
import JsonEditor from "./lib/cores/JsonEditor"; | ||
@@ -27,11 +29,24 @@ import addSchema from "./lib/addSchema"; | ||
AllOfError: string; | ||
ConstError: string; | ||
ContainsError: string; | ||
ContainsArrayError: string; | ||
containsAnyError: string; | ||
EnumError: string; | ||
FormatDateError: string; | ||
FormatDateTimeError: string; | ||
FormatEmailError: string; | ||
FormatUrlError: string; | ||
FormatUriError: string; | ||
FormatURLError: string; | ||
FormatURIError: string; | ||
FormatURIReferenceError: string; | ||
FormatURITemplateError: string; | ||
FormatHostnameError: string; | ||
FormatIPV4Error: string; | ||
FormatIPV4LeadingZeroError: string; | ||
FormatIPV6Error: string; | ||
FormatIPV6LeadingZeroError: string; | ||
FormatJSONPointerError: string; | ||
FormatRegExError: string; | ||
FormatTimeError: string; | ||
InvalidDataError: string; | ||
InvalidPropertyNameError: string; | ||
MaximumError: string; | ||
@@ -64,2 +79,4 @@ MaxItemsError: string; | ||
export { config, Interface, Draft04, // core implementing draft04 specs | ||
Draft06, // core implementing draft06 specs | ||
Draft07, // core implementing draft07 specs | ||
JsonEditor, // adjusted core of draft04 to better support the json-editor | ||
@@ -66,0 +83,0 @@ addSchema, // add a schema to be references via $ref |
import { JSONSchema } from "../types"; | ||
/** | ||
* compiles the input root schema for $ref resolution and returns it again | ||
* @attention this modifies input schema but maintains object-structure | ||
* | ||
* for a compiled json-schema you can call getRef on any contained schema (location of type). | ||
* this resolves a $ref target to a valid schema (for a valid $ref) | ||
* | ||
* @param rootSchema root json-schema ($id, defs, ... ) to compile | ||
* @param [force] = false force compile json-schema | ||
* @return compiled json-schema | ||
*/ | ||
export default function compile(rootSchema: JSONSchema, force?: boolean): JSONSchema; |
@@ -6,11 +6,24 @@ declare const _default: { | ||
AllOfError: string; | ||
ConstError: string; | ||
ContainsError: string; | ||
ContainsArrayError: string; | ||
containsAnyError: string; | ||
EnumError: string; | ||
FormatDateError: string; | ||
FormatDateTimeError: string; | ||
FormatEmailError: string; | ||
FormatUrlError: string; | ||
FormatUriError: string; | ||
FormatURLError: string; | ||
FormatURIError: string; | ||
FormatURIReferenceError: string; | ||
FormatURITemplateError: string; | ||
FormatHostnameError: string; | ||
FormatIPV4Error: string; | ||
FormatIPV4LeadingZeroError: string; | ||
FormatIPV6Error: string; | ||
FormatIPV6LeadingZeroError: string; | ||
FormatJSONPointerError: string; | ||
FormatRegExError: string; | ||
FormatTimeError: string; | ||
InvalidDataError: string; | ||
InvalidPropertyNameError: string; | ||
MaximumError: string; | ||
@@ -17,0 +30,0 @@ MaxItemsError: string; |
@@ -22,3 +22,3 @@ import { JSONSchema, JSONPointer, JSONError } from "../types"; | ||
setSchema(schema: JSONSchema): void; | ||
step(key: string, schema: JSONSchema, data: any, pointer?: JSONPointer): JSONSchema; | ||
step(key: string | number, schema: JSONSchema, data: any, pointer?: JSONPointer): JSONSchema; | ||
} |
@@ -9,2 +9,2 @@ import { JSONSchema } from "../types"; | ||
*/ | ||
export default function getTypeId(schema: JSONSchema): string | undefined; | ||
export default function getTypeId(schema: JSONSchema): string | string[] | undefined; |
declare const _default: { | ||
date: (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
"date-time": (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
@@ -7,6 +8,11 @@ email: (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
ipv6: (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
"json-pointer": (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
"relative-json-pointer": (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
regex: (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
time: (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
uri: (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
"uri-reference": (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
"uri-template": (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
url: (core: any, schema: any, value: any, pointer: any) => import("../types").JSONError; | ||
}; | ||
export default _default; |
@@ -1,28 +0,3 @@ | ||
declare const KeywordValidation: { | ||
additionalProperties: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
allOf: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
anyOf: (core: any, schema: any, value: any, pointer: any) => any; | ||
dependencies: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
enum: (core: any, schema: any, value: any, pointer: any) => any; | ||
format: (core: any, schema: any, value: any, pointer: any) => any; | ||
items: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
maximum: (core: any, schema: any, value: any, pointer: any) => any; | ||
maxItems: (core: any, schema: any, value: any, pointer: any) => any; | ||
maxLength: (core: any, schema: any, value: any, pointer: any) => any; | ||
maxProperties: (core: any, schema: any, value: any, pointer: any) => any; | ||
minLength: (core: any, schema: any, value: any, pointer: any) => any; | ||
minimum: (core: any, schema: any, value: any, pointer: any) => any; | ||
minItems: (core: any, schema: any, value: any, pointer: any) => any; | ||
minProperties: (core: any, schema: any, value: any, pointer: any) => any; | ||
multipleOf: (core: any, schema: any, value: any, pointer: any) => any; | ||
not: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
oneOf: (core: any, schema: any, value: any, pointer: any) => any; | ||
pattern: (core: any, schema: any, value: any, pointer: any) => any; | ||
patternProperties: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
properties: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
propertiesRequired: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
required: (core: any, schema: any, value: any, pointer: any) => any; | ||
requiredNotEmpty: (core: any, schema: any, value: any, pointer: any) => any; | ||
uniqueItems: (core: any, schema: any, value: any, pointer: any) => any[]; | ||
}; | ||
import { JSONValidator } from "../types"; | ||
declare const KeywordValidation: Record<string, JSONValidator>; | ||
export default KeywordValidation; |
import strings from "./lib/config/strings"; | ||
import Interface from "./lib/cores/CoreInterface"; | ||
import Draft04 from "./lib/cores/Draft04"; | ||
import Draft06 from "./lib/cores/Draft06"; | ||
import Draft07 from "./lib/cores/Draft07"; | ||
import JsonEditor from "./lib/cores/JsonEditor"; | ||
@@ -24,2 +26,4 @@ import addSchema from "./lib/addSchema"; | ||
export { config, Interface, Draft04, // core implementing draft04 specs | ||
Draft06, // core implementing draft06 specs | ||
Draft07, // core implementing draft07 specs | ||
JsonEditor, // adjusted core of draft04 to better support the json-editor | ||
@@ -26,0 +30,0 @@ addSchema, // add a schema to be references via $ref |
@@ -11,2 +11,13 @@ /* eslint max-statements-per-line: ["error", { "max": 2 }] */ | ||
const suffixes = /(#|\/)+$/g; | ||
/** | ||
* compiles the input root schema for $ref resolution and returns it again | ||
* @attention this modifies input schema but maintains object-structure | ||
* | ||
* for a compiled json-schema you can call getRef on any contained schema (location of type). | ||
* this resolves a $ref target to a valid schema (for a valid $ref) | ||
* | ||
* @param rootSchema root json-schema ($id, defs, ... ) to compile | ||
* @param [force] = false force compile json-schema | ||
* @return compiled json-schema | ||
*/ | ||
export default function compile(rootSchema, force = false) { | ||
@@ -13,0 +24,0 @@ if (rootSchema[COMPILED] !== undefined) { |
@@ -8,11 +8,24 @@ /* eslint max-len: 0 */ | ||
AllOfError: "Value `{{value}}` at `{{pointer}}` does not match schema of `{{allOf}}`", | ||
ConstError: "Expected value at `{{pointer}}` to be `{{expected}}`, but value given is `{{value}}`", | ||
ContainsError: "The array at `{{pointer}}` must contain an element that matches `{{schema}}`", | ||
ContainsArrayError: "The property at `{{pointer}}` must not be an array", | ||
containsAnyError: "The array at `{{pointer}}` must contain at least one item", | ||
EnumError: "Expected given value `{{value}}` in `{{pointer}}` to be one of `{{values}}`", | ||
FormatDateError: "Value `{{value}}` at `{{pointer}}` is not a valid date", | ||
FormatDateTimeError: "Value `{{value}}` at `{{pointer}}` is not a valid date-time", | ||
FormatEmailError: "Value `{{value}}` at `{{pointer}}` is not a valid email", | ||
FormatUrlError: "Value `{{value}}` at `{{pointer}}` is not a valid url", | ||
FormatUriError: "Value `{{value}}` at `{{pointer}}` is not a valid uri", | ||
FormatURLError: "Value `{{value}}` at `{{pointer}}` is not a valid url", | ||
FormatURIError: "Value `{{value}}` at `{{pointer}}` is not a valid uri", | ||
FormatURIReferenceError: "Value `{{value}}` at `{{pointer}}` is not a valid uri-reference", | ||
FormatURITemplateError: "Value `{{value}}` at `{{pointer}}` is not a valid uri-template", | ||
FormatHostnameError: "Value `{{value}}` at `{{pointer}}` is not a valid hostname", | ||
FormatIPV4Error: "Value `{{value}}` at `{{pointer}}` is not a valid IPv4 address", | ||
FormatIPV4LeadingZeroError: "IPv4 addresses starting with zero are invalid, since they are interpreted as octals", | ||
FormatIPV6Error: "Value `{{value}}` at `{{pointer}}` is not a valid IPv6 address", | ||
FormatIPV6LeadingZeroError: "IPv6 addresses starting with zero are invalid, since they are interpreted as octals", | ||
FormatJSONPointerError: "Value `{{value}}` at `{{pointer}}` is not a valid json-pointer", | ||
FormatRegExError: "Value `{{value}}` at `{{pointer}}` is not a valid regular expression", | ||
FormatTimeError: "Value `{{value}}` at `{{pointer}}` is not a valid time", | ||
InvalidDataError: "No value may be specified in `{{pointer}}`", | ||
InvalidPropertyNameError: "Invalid property name `{{property}}` at `{{pointer}}`", | ||
MaximumError: "Value in `{{pointer}}` is `{{length}}`, but should be `{{maximum}}` at maximum", | ||
@@ -19,0 +32,0 @@ MaxItemsError: "Too many items in `{{pointer}}`, should be `{{maximum}}` at most, but got `{{length}}`", |
@@ -15,7 +15,7 @@ import gp from "gson-pointer"; | ||
.forEach(defId => { | ||
if (!isObject(schema.definitions[defId])) { | ||
console.log(`Invalid schema in ${pointer}/definitions/${defId}`); | ||
if (schema.definitions[defId] === false || isObject(schema.definitions[defId])) { | ||
walk.nextTypeDefs(schema.definitions[defId], gp.join(pointer, "definitions", defId, false)); | ||
return; | ||
} | ||
walk.nextTypeDefs(schema.definitions[defId], gp.join(pointer, "definitions", defId, false)); | ||
console.log(`Invalid schema in ${pointer}/definitions/${defId}`); | ||
}); | ||
@@ -22,0 +22,0 @@ } |
@@ -5,4 +5,12 @@ export default function resolveRef(schema, rootSchema) { | ||
} | ||
if (schema.getRoot) { | ||
// we actually always need to resolve the schema like this, since returned subschemas | ||
// must resolve relative from their schema | ||
const resolvedSchema = schema.getRoot().getRef(schema); | ||
// console.log(schema.$ref, "=>", resolvedSchema); | ||
return resolvedSchema; | ||
} | ||
// tryout - this should never be called, except we missed something | ||
const resolvedSchema = rootSchema.getRef(schema); | ||
return resolvedSchema; | ||
} |
@@ -17,3 +17,14 @@ import gp from "gson-pointer"; | ||
} | ||
const type = types[id]; | ||
let type; | ||
if (Array.isArray(id)) { | ||
// since types can also be declared as a set of types, merge the definitions | ||
// maybe this will require a more sophisticated approach | ||
type = {}; | ||
for (let i = 0, l = id.length; i < l; i += 1) { | ||
Object.assign(type, types[id[i]]); | ||
} | ||
} | ||
else { | ||
type = types[id]; | ||
} | ||
if (type.definitions == null) { | ||
@@ -20,0 +31,0 @@ return defs; |
@@ -18,3 +18,3 @@ import types from "./types"; | ||
} | ||
if (types[schema.type]) { | ||
if (types[schema.type] || Array.isArray(schema.type)) { | ||
return schema.type; | ||
@@ -21,0 +21,0 @@ } |
@@ -26,2 +26,10 @@ import getTypeOf from "./getTypeOf"; | ||
if (itemsType === "array") { | ||
// @draft >= 7 bool schema, items:[true, false] | ||
if (schema.items[key] === true) { | ||
return createSchemaOf(data[key]); | ||
} | ||
// @draft >= 7 bool schema, items:[true, false] | ||
if (schema.items[key] === false) { | ||
return errors.invalidDataError({ key, value: data[key], pointer }); | ||
} | ||
if (schema.items[key]) { | ||
@@ -130,2 +138,10 @@ return core.resolveRef(schema.items[key]); | ||
export default function step(core, key, schema, data, pointer = "#") { | ||
// @draft >= 4 ? | ||
if (Array.isArray(schema.type)) { | ||
const dataType = getTypeOf(data); | ||
if (schema.type.includes(dataType)) { | ||
return stepType[dataType](core, key, schema, data, pointer); | ||
} | ||
return core.errors.typeError({ value: data, pointer, expected: schema.type, received: dataType }); | ||
} | ||
const expectedType = schema.type || getTypeOf(data); | ||
@@ -132,0 +148,0 @@ if (stepType[expectedType]) { |
import getTypeOf from "./getTypeOf"; | ||
import { errorOrPromise } from "./utils/filter"; | ||
import flattenArray from "./utils/flattenArray"; | ||
import equal from "fast-deep-equal"; | ||
function getJsonSchemaType(value, expectedType) { | ||
@@ -22,6 +23,23 @@ let jsType = getTypeOf(value); | ||
export default function validate(core, value, schema = core.rootSchema, pointer = "#") { | ||
schema = core.resolveRef(schema); | ||
// @todo this is a high level v7 schema validation | ||
// @ts-ignore | ||
if (schema === true) { | ||
return []; | ||
} | ||
// @todo this is a high level v7 schema validation | ||
// @ts-ignore | ||
if (schema === false) { | ||
return [core.errors.invalidDataError({ value, pointer })]; | ||
} | ||
if (schema.type === "error") { | ||
return [schema]; | ||
} | ||
schema = core.resolveRef(schema); | ||
// @draft >= 6 const | ||
if (schema.const !== undefined) { | ||
if (equal(schema.const, value)) { | ||
return []; | ||
} | ||
return [core.errors.constError({ value, expected: schema.const, pointer })]; | ||
} | ||
const receivedType = getJsonSchemaType(value, schema.type); | ||
@@ -28,0 +46,0 @@ const expectedType = schema.type || receivedType; |
@@ -8,5 +8,12 @@ /* eslint no-invalid-this: 0 */ | ||
allOfError: createCustomError("AllOfError"), | ||
constError: createCustomError("ConstError"), | ||
containsError: createCustomError("ContainsError"), | ||
containsArrayError: createCustomError("ContainsArrayError"), | ||
containsAnyError: createCustomError("ContainsAnyError"), | ||
enumError: createCustomError("EnumError"), | ||
formatUrlError: createCustomError("FormatUrlError"), | ||
formatUriError: createCustomError("FormatUriError"), | ||
formatURLError: createCustomError("FormatURLError"), | ||
formatURIError: createCustomError("FormatURIError"), | ||
formatURIReferenceError: createCustomError("FormatURIReferenceError"), | ||
formatURITemplateError: createCustomError("FormatURITemplateError"), | ||
formatDateError: createCustomError("FormatDateaError"), | ||
formatDateTimeError: createCustomError("FormatDateTimeError"), | ||
@@ -16,6 +23,12 @@ formatEmailError: createCustomError("FormatEmailError"), | ||
formatIPV4Error: createCustomError("FormatIPV4Error"), | ||
formatIPV4LeadingZeroError: createCustomError("FormatIPV4LeadingZeroError"), | ||
formatIPV6Error: createCustomError("FormatIPV6Error"), | ||
formatIPV6LeadingZeroError: createCustomError("FormatIPV6LeadingZeroError"), | ||
formatJSONPointerError: createCustomError("FormatJSONPointerError"), | ||
formatRegExError: createCustomError("FormatRegExError"), | ||
formatTimeError: createCustomError("FormatTimeError"), | ||
invalidSchemaError: createCustomError("InvalidSchemaError"), | ||
invalidDataError: createCustomError("InvalidDataError"), | ||
invalidTypeError: createCustomError("InvalidTypeError"), | ||
invalidPropertyNameError: createCustomError("InvalidPropertyNameError"), | ||
maximumError: createCustomError("MaximumError"), | ||
@@ -22,0 +35,0 @@ maxItemsError: createCustomError("MaxItemsError"), |
@@ -1,4 +0,6 @@ | ||
/* eslint-disable max-len */ | ||
/* eslint-disable max-len, no-control-regex */ | ||
import errors from "./errors"; | ||
import validUrl from "valid-url"; | ||
// referenced | ||
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts | ||
// https://gist.github.com/marcelotmelo/b67f58a08bee6c2468f8 | ||
@@ -9,4 +11,32 @@ const isValidDateTime = new RegExp("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([Zz])|([\\+|\\-]([01][0-9]|2[0-3]):[0-5][0-9]))$"); | ||
const isValidHostname = /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/; | ||
const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; | ||
const matchTime = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i; | ||
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; | ||
const isValidJSONPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/; | ||
const isValidRelativeJSONPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/; | ||
const isValidURIRef = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; | ||
// uri-template: https://tools.ietf.org/html/rfc6570 | ||
const isValidURITemplate = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i; | ||
// Default JSON-Schema formats: date-time, email, hostname, ipv4, ipv6, uri, uriref | ||
export default { | ||
date: (core, schema, value, pointer) => { | ||
if (typeof value !== "string") { | ||
return undefined; | ||
} | ||
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts | ||
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6 | ||
const matches = value.match(matchDate); | ||
if (!matches) { | ||
return errors.formatDateTimeError({ value, pointer }); | ||
} | ||
const year = +matches[1]; | ||
const month = +matches[2]; | ||
const day = +matches[3]; | ||
// https://tools.ietf.org/html/rfc3339#appendix-C | ||
const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); | ||
if (month >= 1 && month <= 12 && day >= 1 && day <= (month == 2 && isLeapYear ? 29 : DAYS[month])) { | ||
return undefined; | ||
} | ||
return errors.formatDateError({ value, pointer }); | ||
}, | ||
"date-time": (core, schema, value, pointer) => { | ||
@@ -60,2 +90,6 @@ if (typeof value !== "string") { | ||
} | ||
if (value && value[0] === "0") { | ||
// leading zeroes should be rejected, as they are treated as octals | ||
return errors.formatIPV4LeadingZeroError({ value, pointer }); | ||
} | ||
if (value.length <= 15 && isValidIPV4.test(value)) { | ||
@@ -70,2 +104,6 @@ return undefined; | ||
} | ||
if (value && value[0] === "0") { | ||
// leading zeroes should be rejected, as they are treated as octals | ||
return errors.formatIPV6LeadingZeroError({ value, pointer }); | ||
} | ||
if (value.length <= 45 && isValidIPV6.test(value)) { | ||
@@ -76,4 +114,31 @@ return undefined; | ||
}, | ||
"json-pointer": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidJSONPointer.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatJSONPointerError({ value, pointer }); | ||
}, | ||
"relative-json-pointer": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidRelativeJSONPointer.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatJSONPointerError({ value, pointer }); | ||
}, | ||
regex: (core, schema, value, pointer) => { | ||
if (typeof value === "string" && /\\Z$/.test(value) === false) { | ||
try { | ||
new RegExp(value); | ||
return undefined; | ||
} | ||
catch (e) { } // eslint-disable-line no-empty | ||
return errors.formatRegExError({ value, pointer }); | ||
} | ||
// v7 tests, ignore non-regex values | ||
if (typeof value === "object" || typeof value === "number" || Array.isArray(value)) { | ||
return undefined; | ||
@@ -83,11 +148,47 @@ } | ||
}, | ||
uri: (core, schema, value, pointer) => { | ||
time: (core, schema, value, pointer) => { | ||
if (typeof value !== "string") { | ||
return undefined; | ||
} | ||
if (value === "" || validUrl.isUri(value)) { | ||
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts | ||
const matches = value.match(matchTime); | ||
if (!matches) { | ||
return errors.formatDateTimeError({ value, pointer }); | ||
} | ||
const hour = +matches[1]; | ||
const minute = +matches[2]; | ||
const second = +matches[3]; | ||
const timeZone = !!matches[5]; | ||
if (((hour <= 23 && minute <= 59 && second <= 59) || (hour == 23 && minute == 59 && second == 60)) && timeZone) { | ||
return undefined; | ||
} | ||
return errors.formatUriError({ value, pointer }); | ||
return errors.formatTimeError({ value, pointer }); | ||
}, | ||
uri: (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (validUrl.isUri(value)) { | ||
return undefined; | ||
} | ||
return errors.formatURIError({ value, pointer }); | ||
}, | ||
"uri-reference": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidURIRef.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatURIReferenceError({ value, pointer }); | ||
}, | ||
"uri-template": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidURITemplate.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatURITemplateError({ value, pointer }); | ||
}, | ||
url: (core, schema, value, pointer) => { | ||
@@ -94,0 +195,0 @@ if (value === "" || validUrl.isWebUri(value)) { |
@@ -6,2 +6,5 @@ import getTypeOf from "../getTypeOf"; | ||
const FPP = settings.floatingPointPrecision; | ||
function isError(o) { | ||
return (o === null || o === void 0 ? void 0 : o.type) === "error"; | ||
} | ||
// list of validation keywords: http://json-schema.org/latest/json-schema-validation.html#rfc.section.5 | ||
@@ -104,2 +107,10 @@ const KeywordValidation = { | ||
} | ||
// @draft >= 6 boolean schema | ||
if (schema.dependencies[property] === true) { | ||
return; | ||
} | ||
if (schema.dependencies[property] === false) { | ||
errors.push(core.errors.missingDependencyError({ pointer })); | ||
return; | ||
} | ||
let dependencyErrors; | ||
@@ -146,2 +157,9 @@ const type = getTypeOf(schema.dependencies[property]); | ||
items: (core, schema, value, pointer) => { | ||
// @draft >= 7 bool schema | ||
if (schema.items === false) { | ||
if (Array.isArray(value) && value.length === 0) { | ||
return undefined; | ||
} | ||
return core.errors.invalidDataError({ pointer, value }); | ||
} | ||
const errors = []; | ||
@@ -255,2 +273,3 @@ for (let i = 0; i < value.length; i += 1) { | ||
} | ||
// also check https://stackoverflow.com/questions/1815367/catch-and-compute-overflow-during-multiplication-of-two-large-integers | ||
return undefined; | ||
@@ -270,3 +289,3 @@ }, | ||
schema = core.resolveOneOf(value, schema, pointer); | ||
if (schema && schema.type === "error") { | ||
if (isError(schema)) { | ||
return schema; | ||
@@ -273,0 +292,0 @@ } |
export default { | ||
"http://json-schema.org/draft-04/schema": require("./draft04.json"), | ||
reset() { | ||
@@ -4,0 +3,0 @@ Object.keys(this).forEach(key => { |
declare const _default: { | ||
"http://json-schema.org/draft-04/schema": any; | ||
reset(): void; | ||
}; | ||
export default _default; |
import strings from "./lib/config/strings"; | ||
import Interface from "./lib/cores/CoreInterface"; | ||
import Draft04 from "./lib/cores/Draft04"; | ||
import Draft06 from "./lib/cores/Draft06"; | ||
import Draft07 from "./lib/cores/Draft07"; | ||
import JsonEditor from "./lib/cores/JsonEditor"; | ||
@@ -29,2 +31,4 @@ import addSchema from "./lib/addSchema"; | ||
Draft04, // core implementing draft04 specs | ||
Draft06, // core implementing draft06 specs | ||
Draft07, // core implementing draft07 specs | ||
JsonEditor, // adjusted core of draft04 to better support the json-editor | ||
@@ -31,0 +35,0 @@ addSchema, // add a schema to be references via $ref |
@@ -14,3 +14,13 @@ /* eslint max-statements-per-line: ["error", { "max": 2 }] */ | ||
/** | ||
* compiles the input root schema for $ref resolution and returns it again | ||
* @attention this modifies input schema but maintains object-structure | ||
* | ||
* for a compiled json-schema you can call getRef on any contained schema (location of type). | ||
* this resolves a $ref target to a valid schema (for a valid $ref) | ||
* | ||
* @param rootSchema root json-schema ($id, defs, ... ) to compile | ||
* @param [force] = false force compile json-schema | ||
* @return compiled json-schema | ||
*/ | ||
export default function compile(rootSchema: JSONSchema, force = false): JSONSchema { | ||
@@ -17,0 +27,0 @@ if (rootSchema[COMPILED] !== undefined) { return rootSchema; } // eslint-disable-line |
@@ -8,11 +8,24 @@ /* eslint max-len: 0 */ | ||
AllOfError: "Value `{{value}}` at `{{pointer}}` does not match schema of `{{allOf}}`", | ||
ConstError: "Expected value at `{{pointer}}` to be `{{expected}}`, but value given is `{{value}}`", | ||
ContainsError: "The array at `{{pointer}}` must contain an element that matches `{{schema}}`", | ||
ContainsArrayError: "The property at `{{pointer}}` must not be an array", | ||
containsAnyError: "The array at `{{pointer}}` must contain at least one item", | ||
EnumError: "Expected given value `{{value}}` in `{{pointer}}` to be one of `{{values}}`", | ||
FormatDateError: "Value `{{value}}` at `{{pointer}}` is not a valid date", | ||
FormatDateTimeError: "Value `{{value}}` at `{{pointer}}` is not a valid date-time", | ||
FormatEmailError: "Value `{{value}}` at `{{pointer}}` is not a valid email", | ||
FormatUrlError: "Value `{{value}}` at `{{pointer}}` is not a valid url", | ||
FormatUriError: "Value `{{value}}` at `{{pointer}}` is not a valid uri", | ||
FormatURLError: "Value `{{value}}` at `{{pointer}}` is not a valid url", | ||
FormatURIError: "Value `{{value}}` at `{{pointer}}` is not a valid uri", | ||
FormatURIReferenceError: "Value `{{value}}` at `{{pointer}}` is not a valid uri-reference", | ||
FormatURITemplateError: "Value `{{value}}` at `{{pointer}}` is not a valid uri-template", | ||
FormatHostnameError: "Value `{{value}}` at `{{pointer}}` is not a valid hostname", | ||
FormatIPV4Error: "Value `{{value}}` at `{{pointer}}` is not a valid IPv4 address", | ||
FormatIPV4LeadingZeroError: "IPv4 addresses starting with zero are invalid, since they are interpreted as octals", | ||
FormatIPV6Error: "Value `{{value}}` at `{{pointer}}` is not a valid IPv6 address", | ||
FormatIPV6LeadingZeroError: "IPv6 addresses starting with zero are invalid, since they are interpreted as octals", | ||
FormatJSONPointerError: "Value `{{value}}` at `{{pointer}}` is not a valid json-pointer", | ||
FormatRegExError: "Value `{{value}}` at `{{pointer}}` is not a valid regular expression", | ||
FormatTimeError: "Value `{{value}}` at `{{pointer}}` is not a valid time", | ||
InvalidDataError: "No value may be specified in `{{pointer}}`", | ||
InvalidPropertyNameError: "Invalid property name `{{property}}` at `{{pointer}}`", | ||
MaximumError: "Value in `{{pointer}}` is `{{length}}`, but should be `{{maximum}}` at maximum", | ||
@@ -19,0 +32,0 @@ MaxItemsError: "Too many items in `{{pointer}}`, should be `{{maximum}}` at most, but got `{{length}}`", |
@@ -73,5 +73,5 @@ /* eslint @typescript-eslint/no-unused-vars: "off" */ | ||
step(key: string, schema: JSONSchema, data: any, pointer: JSONPointer = "#"): JSONSchema { | ||
step(key: string|number, schema: JSONSchema, data: any, pointer: JSONPointer = "#"): JSONSchema { | ||
throw new Error("function 'step' is not implemented"); | ||
} | ||
} |
@@ -33,7 +33,7 @@ import gp from "gson-pointer"; | ||
.forEach(defId => { | ||
if (!isObject(schema.definitions[defId])) { | ||
console.log(`Invalid schema in ${pointer}/definitions/${defId}`); | ||
if (schema.definitions[defId] === false || isObject(schema.definitions[defId])) { | ||
walk.nextTypeDefs(schema.definitions[defId], gp.join(pointer, "definitions", defId, false)); | ||
return; | ||
} | ||
walk.nextTypeDefs(schema.definitions[defId], gp.join(pointer, "definitions", defId, false)); | ||
console.log(`Invalid schema in ${pointer}/definitions/${defId}`); | ||
}); | ||
@@ -40,0 +40,0 @@ } |
@@ -9,4 +9,13 @@ import { JSONSchema } from "./types"; | ||
if (schema.getRoot) { | ||
// we actually always need to resolve the schema like this, since returned subschemas | ||
// must resolve relative from their schema | ||
const resolvedSchema = schema.getRoot().getRef(schema); | ||
// console.log(schema.$ref, "=>", resolvedSchema); | ||
return resolvedSchema; | ||
} | ||
// tryout - this should never be called, except we missed something | ||
const resolvedSchema = rootSchema.getRef(schema); | ||
return resolvedSchema; | ||
} |
@@ -22,3 +22,16 @@ import gp from "gson-pointer"; | ||
} | ||
const type = types[id]; | ||
let type; | ||
if (Array.isArray(id)) { | ||
// since types can also be declared as a set of types, merge the definitions | ||
// maybe this will require a more sophisticated approach | ||
type = {}; | ||
for (let i = 0, l = id.length; i < l; i += 1) { | ||
Object.assign(type, types[id[i]]); | ||
} | ||
} else { | ||
type = types[id]; | ||
} | ||
if (type.definitions == null) { | ||
@@ -25,0 +38,0 @@ return defs; |
@@ -15,3 +15,3 @@ import types from "./types"; | ||
*/ | ||
export default function getTypeId(schema: JSONSchema): string|undefined { | ||
export default function getTypeId(schema: JSONSchema): string|string[]|undefined { | ||
if (isObject(schema) === false) { | ||
@@ -25,3 +25,3 @@ return undefined; | ||
if (types[schema.type]) { | ||
if (types[schema.type] || Array.isArray(schema.type)) { | ||
return schema.type; | ||
@@ -28,0 +28,0 @@ } |
@@ -36,2 +36,11 @@ import getTypeOf from "./getTypeOf"; | ||
if (itemsType === "array") { | ||
// @draft >= 7 bool schema, items:[true, false] | ||
if (schema.items[key] === true) { | ||
return createSchemaOf(data[key]); | ||
} | ||
// @draft >= 7 bool schema, items:[true, false] | ||
if (schema.items[key] === false) { | ||
return errors.invalidDataError({ key, value: data[key], pointer }); | ||
} | ||
if (schema.items[key]) { | ||
@@ -162,2 +171,12 @@ return core.resolveRef(schema.items[key]); | ||
JSONSchema|JSONError { | ||
// @draft >= 4 ? | ||
if (Array.isArray(schema.type)) { | ||
const dataType = getTypeOf(data); | ||
if (schema.type.includes(dataType)) { | ||
return stepType[dataType](core, key, schema, data, pointer); | ||
} | ||
return core.errors.typeError({ value: data, pointer, expected: schema.type, received: dataType }); | ||
} | ||
const expectedType = schema.type || getTypeOf(data); | ||
@@ -164,0 +183,0 @@ if (stepType[expectedType]) { |
@@ -6,2 +6,3 @@ import getTypeOf from "./getTypeOf"; | ||
import Core from "./cores/CoreInterface"; | ||
import equal from "fast-deep-equal"; | ||
@@ -32,2 +33,15 @@ | ||
export default function validate(core: Core, value: any, schema: JSONSchema = core.rootSchema, pointer: JSONPointer = "#"): Array<JSONError> { | ||
schema = core.resolveRef(schema); | ||
// @todo this is a high level v7 schema validation | ||
// @ts-ignore | ||
if (schema === true) { | ||
return []; | ||
} | ||
// @todo this is a high level v7 schema validation | ||
// @ts-ignore | ||
if (schema === false) { | ||
return [core.errors.invalidDataError({ value, pointer })]; | ||
} | ||
if (schema.type === "error") { | ||
@@ -37,3 +51,9 @@ return [schema as JSONError]; | ||
schema = core.resolveRef(schema); | ||
// @draft >= 6 const | ||
if (schema.const !== undefined) { | ||
if (equal(schema.const, value)) { | ||
return []; | ||
} | ||
return [core.errors.constError({ value, expected: schema.const, pointer })]; | ||
} | ||
@@ -40,0 +60,0 @@ const receivedType = getJsonSchemaType(value, schema.type); |
@@ -10,5 +10,12 @@ /* eslint no-invalid-this: 0 */ | ||
allOfError: createCustomError("AllOfError"), | ||
constError: createCustomError("ConstError"), | ||
containsError: createCustomError("ContainsError"), | ||
containsArrayError: createCustomError("ContainsArrayError"), | ||
containsAnyError: createCustomError("ContainsAnyError"), | ||
enumError: createCustomError("EnumError"), | ||
formatUrlError: createCustomError("FormatUrlError"), | ||
formatUriError: createCustomError("FormatUriError"), | ||
formatURLError: createCustomError("FormatURLError"), | ||
formatURIError: createCustomError("FormatURIError"), | ||
formatURIReferenceError: createCustomError("FormatURIReferenceError"), | ||
formatURITemplateError: createCustomError("FormatURITemplateError"), | ||
formatDateError: createCustomError("FormatDateaError"), | ||
formatDateTimeError: createCustomError("FormatDateTimeError"), | ||
@@ -18,6 +25,12 @@ formatEmailError: createCustomError("FormatEmailError"), | ||
formatIPV4Error: createCustomError("FormatIPV4Error"), | ||
formatIPV4LeadingZeroError: createCustomError("FormatIPV4LeadingZeroError"), | ||
formatIPV6Error: createCustomError("FormatIPV6Error"), | ||
formatIPV6LeadingZeroError: createCustomError("FormatIPV6LeadingZeroError"), | ||
formatJSONPointerError: createCustomError("FormatJSONPointerError"), | ||
formatRegExError: createCustomError("FormatRegExError"), | ||
formatTimeError: createCustomError("FormatTimeError"), | ||
invalidSchemaError: createCustomError("InvalidSchemaError"), | ||
invalidDataError: createCustomError("InvalidDataError"), | ||
invalidTypeError: createCustomError("InvalidTypeError"), | ||
invalidPropertyNameError: createCustomError("InvalidPropertyNameError"), | ||
maximumError: createCustomError("MaximumError"), | ||
@@ -24,0 +37,0 @@ maxItemsError: createCustomError("MaxItemsError"), |
@@ -1,5 +0,8 @@ | ||
/* eslint-disable max-len */ | ||
/* eslint-disable max-len, no-control-regex */ | ||
import errors from "./errors"; | ||
import validUrl from "valid-url"; | ||
// referenced | ||
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts | ||
// https://gist.github.com/marcelotmelo/b67f58a08bee6c2468f8 | ||
@@ -10,7 +13,37 @@ const isValidDateTime = new RegExp("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([Zz])|([\\+|\\-]([01][0-9]|2[0-3]):[0-5][0-9]))$"); | ||
const isValidHostname = /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/; | ||
const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; | ||
const matchTime = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i; | ||
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; | ||
const isValidJSONPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/; | ||
const isValidRelativeJSONPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/; | ||
const isValidURIRef = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; | ||
// uri-template: https://tools.ietf.org/html/rfc6570 | ||
const isValidURITemplate = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i; | ||
// Default JSON-Schema formats: date-time, email, hostname, ipv4, ipv6, uri, uriref | ||
export default { | ||
date: (core, schema, value, pointer) => { | ||
if (typeof value !== "string") { | ||
return undefined; | ||
} | ||
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts | ||
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6 | ||
const matches = value.match(matchDate); | ||
if (!matches) { | ||
return errors.formatDateTimeError({ value, pointer }); | ||
} | ||
const year = +matches[1]; | ||
const month = +matches[2]; | ||
const day = +matches[3]; | ||
// https://tools.ietf.org/html/rfc3339#appendix-C | ||
const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); | ||
if (month >= 1 && month <= 12 && day >= 1 && day <= (month == 2 && isLeapYear ? 29 : DAYS[month])) { | ||
return undefined; | ||
} | ||
return errors.formatDateError({ value, pointer }); | ||
}, | ||
"date-time": (core, schema, value, pointer) => { | ||
@@ -67,2 +100,6 @@ if (typeof value !== "string") { | ||
} | ||
if (value && value[0] === "0") { | ||
// leading zeroes should be rejected, as they are treated as octals | ||
return errors.formatIPV4LeadingZeroError({ value, pointer }); | ||
} | ||
if (value.length <= 15 && isValidIPV4.test(value)) { | ||
@@ -78,2 +115,6 @@ return undefined; | ||
} | ||
if (value && value[0] === "0") { | ||
// leading zeroes should be rejected, as they are treated as octals | ||
return errors.formatIPV6LeadingZeroError({ value, pointer }); | ||
} | ||
if (value.length <= 45 && isValidIPV6.test(value)) { | ||
@@ -85,4 +126,33 @@ return undefined; | ||
"json-pointer": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidJSONPointer.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatJSONPointerError({ value, pointer }); | ||
}, | ||
"relative-json-pointer": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidRelativeJSONPointer.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatJSONPointerError({ value, pointer }); | ||
}, | ||
regex: (core, schema, value, pointer) => { | ||
if (typeof value === "string" && /\\Z$/.test(value) === false) { | ||
try { | ||
new RegExp(value); | ||
return undefined; | ||
} catch (e) {} // eslint-disable-line no-empty | ||
return errors.formatRegExError({ value, pointer }); | ||
} | ||
// v7 tests, ignore non-regex values | ||
if (typeof value === "object" || typeof value === "number" || Array.isArray(value)) { | ||
return undefined; | ||
@@ -93,12 +163,51 @@ } | ||
uri: (core, schema, value, pointer) => { | ||
time: (core, schema, value, pointer) => { | ||
if (typeof value !== "string") { | ||
return undefined; | ||
} | ||
if (value === "" || validUrl.isUri(value)) { | ||
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts | ||
const matches = value.match(matchTime); | ||
if (!matches) { | ||
return errors.formatDateTimeError({ value, pointer }); | ||
} | ||
const hour = +matches[1]; | ||
const minute = +matches[2]; | ||
const second = +matches[3]; | ||
const timeZone = !!matches[5]; | ||
if (((hour <= 23 && minute <= 59 && second <= 59) || (hour == 23 && minute == 59 && second == 60)) && timeZone) { | ||
return undefined | ||
} | ||
return errors.formatTimeError({ value, pointer }); | ||
}, | ||
uri: (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
return errors.formatUriError({ value, pointer }); | ||
if (validUrl.isUri(value)) { | ||
return undefined; | ||
} | ||
return errors.formatURIError({ value, pointer }); | ||
}, | ||
"uri-reference": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidURIRef.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatURIReferenceError({ value, pointer }); | ||
}, | ||
"uri-template": (core, schema, value, pointer) => { | ||
if (typeof value !== "string" || value === "") { | ||
return undefined; | ||
} | ||
if (isValidURITemplate.test(value)) { | ||
return undefined; | ||
} | ||
return errors.formatURITemplateError({ value, pointer }); | ||
}, | ||
url: (core, schema, value, pointer) => { | ||
@@ -105,0 +214,0 @@ if (value === "" || validUrl.isWebUri(value)) { |
@@ -5,7 +5,12 @@ import getTypeOf from "../getTypeOf"; | ||
import ucs2decode from "../utils/punycode.ucs2decode"; | ||
import { JSONValidator } from "../types"; | ||
const FPP = settings.floatingPointPrecision; | ||
import { JSONError } from "../types"; | ||
function isError(o): o is JSONError { | ||
return o?.type === "error"; | ||
} | ||
// list of validation keywords: http://json-schema.org/latest/json-schema-validation.html#rfc.section.5 | ||
const KeywordValidation = { | ||
const KeywordValidation: Record<string, JSONValidator> = { | ||
@@ -126,2 +131,11 @@ additionalProperties: (core, schema, value, pointer) => { | ||
// @draft >= 6 boolean schema | ||
if (schema.dependencies[property] === true) { | ||
return; | ||
} | ||
if (schema.dependencies[property] === false) { | ||
errors.push(core.errors.missingDependencyError({ pointer })); | ||
return; | ||
} | ||
let dependencyErrors; | ||
@@ -135,3 +149,2 @@ const type = getTypeOf(schema.dependencies[property]); | ||
dependencyErrors = core.validate(value, schema.dependencies[property]); | ||
} else { | ||
@@ -170,2 +183,10 @@ throw new Error(`Invalid dependency definition for ${pointer}/${property}. Must be list or schema`); | ||
items: (core, schema, value, pointer) => { | ||
// @draft >= 7 bool schema | ||
if (schema.items === false) { | ||
if (Array.isArray(value) && value.length === 0) { | ||
return undefined; | ||
} | ||
return core.errors.invalidDataError({ pointer, value }); | ||
} | ||
const errors = []; | ||
@@ -176,3 +197,2 @@ for (let i = 0; i < value.length; i += 1) { | ||
const itemSchema = core.step(i, schema, value, pointer); | ||
if (itemSchema && itemSchema.type === "error") { | ||
@@ -283,2 +303,3 @@ return [itemSchema]; | ||
} | ||
// also check https://stackoverflow.com/questions/1815367/catch-and-compute-overflow-during-multiplication-of-two-large-integers | ||
return undefined; | ||
@@ -299,3 +320,3 @@ }, | ||
schema = core.resolveOneOf(value, schema, pointer); | ||
if (schema && schema.type === "error") { | ||
if (isError(schema)) { | ||
return schema; | ||
@@ -302,0 +323,0 @@ } |
{ | ||
"name": "json-schema-library", | ||
"version": "5.3.0", | ||
"version": "6.0.0", | ||
"description": "Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation", | ||
@@ -16,6 +16,11 @@ "module": "dist/module/index.js", | ||
"test:spec": "TS_NODE_PROJECT=./test/tsconfig.json mocha -r ts-node/register -R spec 'test/spec/**/*.test.ts'", | ||
"test:4": "TS_NODE_PROJECT=./test/tsconfig.json mocha -r ts-node/register -R spec 'test/spec/v4/*.test.ts'", | ||
"test:6": "TS_NODE_PROJECT=./test/tsconfig.json mocha -r ts-node/register -R spec 'test/spec/v6/*.test.ts'", | ||
"test:7": "TS_NODE_PROJECT=./test/tsconfig.json mocha -r ts-node/register -R spec 'test/spec/v7/*.test.ts'", | ||
"test:2019": "TS_NODE_PROJECT=./test/tsconfig.json mocha -r ts-node/register -R spec 'test/spec/v2019-09/*.test.ts'", | ||
"tdd": "watch \"npm run test:unit\" lib/ test/", | ||
"coverage": "nyc npm run test --reporter=lcov", | ||
"analyze": "NODE_ENV=production webpack --json | webpack-bundle-size-analyzer", | ||
"lint": "eslint lib" | ||
"lint": "eslint lib", | ||
"prepublishOnly": "yarn dist" | ||
}, | ||
@@ -33,2 +38,3 @@ "repository": { | ||
"devDependencies": { | ||
"@json-schema-org/tests": "^2.0.0", | ||
"@types/chai": "^4.2.14", | ||
@@ -47,3 +53,2 @@ "@types/mocha": "^8.0.4", | ||
"glob": "^7.1.2", | ||
"json-schema-test-suite": "json-schema-org/JSON-Schema-Test-Suite.git#master", | ||
"mocha": "^3.1.2", | ||
@@ -61,2 +66,3 @@ "nyc": "^10.0.0", | ||
"deepmerge": "^4.2.2", | ||
"fast-deep-equal": "^3.1.3", | ||
"gson-pointer": "^4.1.1", | ||
@@ -66,2 +72,5 @@ "gson-query": "^5.1.0", | ||
}, | ||
"resolutions": { | ||
"json-schema-test-suite": "json-schema-org/JSON-Schema-Test-Suite.git#bcf1dc81ae099ade2a9642c672c06ee1af1bb489" | ||
}, | ||
"publishConfig": { | ||
@@ -68,0 +77,0 @@ "registry": "http://registry.npmjs.org" |
@@ -26,6 +26,7 @@ <h1 align="center"><img src="./docs/json-schema-library.png" width="256" alt="json-schema-library"></h1> | ||
1. with version `v5.0.0` the api has changed to es6 modules, where there is no default epxport, only named exports. Additionally all code has been rewritten to typescript. When directly accessing files, switch to `dist/module/*.js`-files for plain js-modules. | ||
2. with version `v4.0.0` the api has changed in order to use the defined (root) schema in core as default where | ||
1. with version `v6.0.0` supported json schema drafts are exported directly as `Draft04`, `Draft06`, `Draft07`, e.g. `import { Draft07 } from "json-schema-library"` | ||
1. with version `v5.0.0` the api has changed to es6 modules, where there is no default export, only named exports. Additionally all code has been rewritten to typescript. When directly accessing files, switch to `dist/module/*.js`-files for plain js-modules. | ||
1. with version `v4.0.0` the api has changed in order to use the defined (root) schema in core as default where | ||
possible. This means, most methods have a changed signature, where `data` is passed before an optional `schema` argument. Check the [Core Overview](#core) for the current signature | ||
3. additionally `iterateSchema` has been renamed to `eachSchema` for consistency | ||
1. additionally `iterateSchema` has been renamed to `eachSchema` for consistency | ||
@@ -32,0 +33,0 @@ |
export default { | ||
"http://json-schema.org/draft-04/schema": require("./draft04.json"), | ||
reset() { | ||
@@ -5,0 +3,0 @@ Object.keys(this).forEach(key => { |
Sorry, the diff of this file is too big to display
405321
189
7558
277
5
+ Addedfast-deep-equal@^3.1.3
+ Addedfast-deep-equal@3.1.3(transitive)