diff --git a/jest.config.js b/jest.config.js index 5cab2d86d..6dbae1caa 100644 --- a/jest.config.js +++ b/jest.config.js @@ -38,7 +38,6 @@ const common = { 'SDKErrors.ts', 'Did.rpc.ts', // third party code copied to this repo - 'packages/utils/src/json-schema/', 'jsonabc.ts', ], } diff --git a/packages/utils/package.json b/packages/utils/package.json index 1c7f4bf50..72e55d92a 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -35,6 +35,7 @@ "typescript": "^4.8.3" }, "dependencies": { + "@cfworker/json-schema": "^4.0.2", "@kiltprotocol/eddsa-jcs-2022": "^0.1.0", "@kiltprotocol/es256k-jcs-2023": "^0.1.0", "@kiltprotocol/jcs-data-integrity-proofs-common": "^0.1.0", diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index ef52f484e..4409c99ff 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -15,7 +15,7 @@ export * as Crypto from './Crypto.js' export * as UUID from './UUID.js' export * as DataUtils from './DataUtils.js' export * as SDKErrors from './SDKErrors.js' -export * as JsonSchema from './json-schema/index.js' +export * as JsonSchema from '@cfworker/json-schema' export * as Signers from './Signers.js' export * as Multikey from './Multikey.js' export { Caip19, Caip2 } from './CAIP/index.js' diff --git a/packages/utils/src/json-schema/README.md b/packages/utils/src/json-schema/README.md deleted file mode 100644 index c86201bc5..000000000 --- a/packages/utils/src/json-schema/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Copied from https://github.com/cfworker/cfworker/tree/v1.9.0/packages/json-schema in v1.9.0, -because this codebase does not support importing pure es6 modules, yet. diff --git a/packages/utils/src/json-schema/deep-compare-strict.ts b/packages/utils/src/json-schema/deep-compare-strict.ts deleted file mode 100644 index da1f0359f..000000000 --- a/packages/utils/src/json-schema/deep-compare-strict.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* eslint-disable */ - -export function deepCompareStrict(a: any, b: any): boolean { - const typeofA = typeof a - if (typeofA !== typeof b) { - return false - } - if (Array.isArray(a)) { - if (!Array.isArray(b)) { - return false - } - const length = a.length - if (length !== b.length) { - return false - } - for (let i = 0; i < length; i++) { - if (!deepCompareStrict(a[i], b[i])) { - return false - } - } - return true - } - if (typeofA === 'object') { - if (!a || !b) { - return a === b - } - const aKeys = Object.keys(a) - const bKeys = Object.keys(b) - const length = aKeys.length - if (length !== bKeys.length) { - return false - } - for (const k of aKeys) { - if (!deepCompareStrict(a[k], b[k])) { - return false - } - } - return true - } - return a === b -} diff --git a/packages/utils/src/json-schema/dereference.ts b/packages/utils/src/json-schema/dereference.ts deleted file mode 100644 index 18f2db946..000000000 --- a/packages/utils/src/json-schema/dereference.ts +++ /dev/null @@ -1,186 +0,0 @@ -/* eslint-disable */ -// @ts-nocheck -import { encodePointer } from './pointer.js' -import { Schema } from './types.js' - -export const schemaKeyword: Record = { - additionalItems: true, - unevaluatedItems: true, - items: true, - contains: true, - additionalProperties: true, - unevaluatedProperties: true, - propertyNames: true, - not: true, - if: true, - then: true, - else: true, -} - -export const schemaArrayKeyword: Record = { - prefixItems: true, - items: true, - allOf: true, - anyOf: true, - oneOf: true, -} - -export const schemaMapKeyword: Record = { - $defs: true, - definitions: true, - properties: true, - patternProperties: true, - dependentSchemas: true, -} - -export const ignoredKeyword: Record = { - id: true, - $id: true, - $ref: true, - $schema: true, - $anchor: true, - $vocabulary: true, - $comment: true, - default: true, - enum: true, - const: true, - required: true, - type: true, - maximum: true, - minimum: true, - exclusiveMaximum: true, - exclusiveMinimum: true, - multipleOf: true, - maxLength: true, - minLength: true, - pattern: true, - format: true, - maxItems: true, - minItems: true, - uniqueItems: true, - maxProperties: true, - minProperties: true, -} - -/** - * Default base URI for schemas without an $id. - * https://json-schema.org/draft/2019-09/json-schema-core.html#initial-base - * https://tools.ietf.org/html/rfc3986#section-5.1 - */ -export let initialBaseURI = - typeof self !== 'undefined' && self.location - ? new URL(self.location.origin + self.location.pathname + location.search) - : new URL('https://github.com/cfworker') - -export function dereference( - schema: Schema | boolean, - lookup: Record = Object.create(null), - baseURI = initialBaseURI, - basePointer = '' -) { - if (schema && typeof schema === 'object' && !Array.isArray(schema)) { - const id = schema.$id || schema.id - if (id) { - const url = new URL(id, baseURI) - if (url.hash.length > 1) { - lookup[url.href] = schema - } else { - url.hash = '' // normalize hash https://url.spec.whatwg.org/#dom-url-hash - if (basePointer === '') { - baseURI = url - } else { - dereference(schema, lookup, baseURI) - } - } - } - } else if (schema !== true && schema !== false) { - return lookup - } - - // compute the schema's URI and add it to the mapping. - const schemaURI = baseURI.href + (basePointer ? '#' + basePointer : '') - if (lookup[schemaURI] !== undefined) { - throw new Error(`Duplicate schema URI "${schemaURI}"`) - } - lookup[schemaURI] = schema - - // exit early if this is a boolean schema. - if (schema === true || schema === false) { - return lookup - } - - // 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 its absolute URI. - if (schema.$ref && schema.__absolute_ref__ === undefined) { - const url = new URL(schema.$ref, baseURI) - url.hash = url.hash // normalize hash https://url.spec.whatwg.org/#dom-url-hash - Object.defineProperty(schema, '__absolute_ref__', { - enumerable: false, - value: url.href, - }) - } - - // if a $recursiveRef is found, resolve its absolute URI. - if (schema.$recursiveRef && schema.__absolute_recursive_ref__ === undefined) { - const url = new URL(schema.$recursiveRef, baseURI) - url.hash = url.hash // normalize hash https://url.spec.whatwg.org/#dom-url-hash - Object.defineProperty(schema, '__absolute_recursive_ref__', { - enumerable: false, - value: url.href, - }) - } - - // if an $anchor is found, compute its URI and add it to the mapping. - if (schema.$anchor) { - const url = new URL('#' + schema.$anchor, baseURI) - lookup[url.href] = schema - } - - // process subschemas. - for (let key in schema) { - if (ignoredKeyword[key]) { - continue - } - const keyBase = `${basePointer}/${encodePointer(key)}` - const subSchema = schema[key] - if (Array.isArray(subSchema)) { - if (schemaArrayKeyword[key]) { - const length = subSchema.length - for (let i = 0; i < length; i++) { - dereference(subSchema[i], lookup, baseURI, `${keyBase}/${i}`) - } - } - } else if (schemaMapKeyword[key]) { - for (let subKey in subSchema) { - dereference( - subSchema[subKey], - lookup, - baseURI, - `${keyBase}/${encodePointer(subKey)}` - ) - } - } else { - dereference(subSchema, lookup, baseURI, keyBase) - } - } - - return lookup -} - -// schema identification examples -// https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.appendix.A -// $ref delegation -// https://github.com/json-schema-org/json-schema-spec/issues/514 -// output format -// https://json-schema.org/draft/2019-09/json-schema-core.html#output -// JSON pointer -// https://tools.ietf.org/html/rfc6901 -// JSON relative pointer -// https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01 diff --git a/packages/utils/src/json-schema/format.ts b/packages/utils/src/json-schema/format.ts deleted file mode 100644 index 2d6bb2492..000000000 --- a/packages/utils/src/json-schema/format.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* eslint-disable */ -// based on https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js - -const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ -const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] -const TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i -const HOSTNAME = - /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i -// const URI = /^(?:[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; -const URIREF = - /^(?:[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 URITEMPLATE = - /^(?:(?:[^\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 -// For the source: https://gist.github.com/dperini/729294 -// For test cases: https://mathiasbynens.be/demo/url-regex -const URL_ = - /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu -const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i -const JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$/ -const JSON_POINTER_URI_FRAGMENT = - /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i -const RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/ - -// date: http://tools.ietf.org/html/rfc3339#section-5.6 -const FASTDATE = /^\d\d\d\d-[0-1]\d-[0-3]\d$/ -// date-time: http://tools.ietf.org/html/rfc3339#section-5.6 -const FASTTIME = - /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i -const FASTDATETIME = - /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i -// uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js -// const FASTURI = /^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i; -const FASTURIREFERENCE = - /^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i - -// https://github.com/ExodusMovement/schemasafe/blob/master/src/formats.js -function EMAIL(input: string): boolean { - if (input[0] === '"') return false - const [name, host, ...rest] = input.split('@') - if ( - !name || - !host || - rest.length !== 0 || - name.length > 64 || - host.length > 253 - ) - return false - if (name[0] === '.' || name.endsWith('.') || name.includes('..')) return false - if ( - !/^[a-z0-9.-]+$/i.test(host) || - !/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+$/i.test(name) - ) - return false - return host - .split('.') - .every((part) => /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/i.test(part)) -} - -// optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html -const IPV4 = - /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/ -// optimized http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses -const IPV6 = - /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i - -// https://github.com/ExodusMovement/schemasafe/blob/master/src/formats.js -function DURATION(input: string): boolean { - return ( - input.length > 1 && - input.length < 80 && - (/^P\d+([.,]\d+)?W$/.test(input) || - (/^P[\dYMDTHS]*(\d[.,]\d+)?[YMDHS]$/.test(input) && - /^P([.,\d]+Y)?([.,\d]+M)?([.,\d]+D)?(T([.,\d]+H)?([.,\d]+M)?([.,\d]+S)?)?$/.test( - input - ))) - ) -} - -function bind(r: RegExp) { - return r.test.bind(r) -} - -export const fullFormat: Record boolean> = { - date, - time: time.bind(undefined, false), - 'date-time': date_time, - duration: DURATION, - uri, - 'uri-reference': bind(URIREF), - 'uri-template': bind(URITEMPLATE), - url: bind(URL_), - email: EMAIL, - hostname: bind(HOSTNAME), - ipv4: bind(IPV4), - ipv6: bind(IPV6), - regex: regex, - uuid: bind(UUID), - 'json-pointer': bind(JSON_POINTER), - 'json-pointer-uri-fragment': bind(JSON_POINTER_URI_FRAGMENT), - 'relative-json-pointer': bind(RELATIVE_JSON_POINTER), -} - -export const fastFormat: Record boolean> = { - ...fullFormat, - date: bind(FASTDATE), - time: bind(FASTTIME), - 'date-time': bind(FASTDATETIME), - 'uri-reference': bind(FASTURIREFERENCE), -} - -function isLeapYear(year: number) { - // https://tools.ietf.org/html/rfc3339#appendix-C - return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) -} - -function date(str: string) { - // full-date from http://tools.ietf.org/html/rfc3339#section-5.6 - const matches = str.match(DATE) - if (!matches) return false - - const year = +matches[1] - const month = +matches[2] - const day = +matches[3] - - return ( - month >= 1 && - month <= 12 && - day >= 1 && - day <= (month == 2 && isLeapYear(year) ? 29 : DAYS[month]) - ) -} - -function time(full: boolean, str: string) { - const matches = str.match(TIME) - if (!matches) return false - - const hour = +matches[1] - const minute = +matches[2] - const second = +matches[3] - const timeZone = !!matches[5] - return ( - ((hour <= 23 && minute <= 59 && second <= 59) || - (hour == 23 && minute == 59 && second == 60)) && - (!full || timeZone) - ) -} - -const DATE_TIME_SEPARATOR = /t|\s/i -function date_time(str: string) { - // http://tools.ietf.org/html/rfc3339#section-5.6 - const dateTime = str.split(DATE_TIME_SEPARATOR) - return dateTime.length == 2 && date(dateTime[0]) && time(true, dateTime[1]) -} - -const NOT_URI_FRAGMENT = /\/|:/ -const URI_PATTERN = - /^(?:[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 - -function uri(str: string): boolean { - // http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "." - return NOT_URI_FRAGMENT.test(str) && URI_PATTERN.test(str) -} - -const Z_ANCHOR = /[^\\]\\Z/ -function regex(str: string) { - if (Z_ANCHOR.test(str)) return false - try { - new RegExp(str) - return true - } catch (e) { - return false - } -} diff --git a/packages/utils/src/json-schema/index.ts b/packages/utils/src/json-schema/index.ts deleted file mode 100644 index b779c382a..000000000 --- a/packages/utils/src/json-schema/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable */ -export * from './deep-compare-strict.js' -export * from './dereference.js' -export * from './format.js' -export * from './pointer.js' -export * from './types.js' -export * from './ucs2-length.js' -export * from './validate.js' -export * from './validator.js' diff --git a/packages/utils/src/json-schema/pointer.ts b/packages/utils/src/json-schema/pointer.ts deleted file mode 100644 index cee7bbe5e..000000000 --- a/packages/utils/src/json-schema/pointer.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-disable */ -export function encodePointer(p: string): string { - return encodeURI(escapePointer(p)) -} - -export function escapePointer(p: string): string { - return p.replace(/~/g, '~0').replace(/\//g, '~1') -} diff --git a/packages/utils/src/json-schema/types.ts b/packages/utils/src/json-schema/types.ts deleted file mode 100644 index aa2bd78e2..000000000 --- a/packages/utils/src/json-schema/types.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* eslint-disable */ -export type SchemaDraft = '4' | '7' | '2019-09' | '2020-12' - -export const enum OutputFormat { - Flag = 1 << 0, - Basic = 1 << 1, - Detailed = 1 << 2, -} - -export type InstanceType = - | 'array' - | 'boolean' - | 'integer' - | 'null' - | 'number' - | 'object' - | 'string' - -export interface Schema { - $id?: string - $anchor?: string - $recursiveAnchor?: boolean - $ref?: string - $recursiveRef?: '#' - $schema?: string - $comment?: string - $defs?: any - $vocabulary?: Record - - type?: InstanceType | InstanceType[] - const?: any - enum?: any[] - required?: string[] - not?: Schema - anyOf?: Schema[] - allOf?: Schema[] - oneOf?: Schema[] - if?: Schema - then?: Schema - else?: Schema - - format?: string - - properties?: Record - patternProperties?: Record - additionalProperties?: Schema | boolean - unevaluatedProperties?: Schema | boolean - minProperties?: number - maxProperties?: number - propertyNames?: Schema - dependentRequired?: Record - dependentSchemas?: Record - dependencies?: Record - - prefixItems?: Array[] - items?: Schema | boolean | Array - additionalItems?: Schema | boolean - unevaluatedItems?: Schema | boolean - contains?: Schema | boolean - minContains?: number - maxContains?: number - minItems?: number - maxItems?: number - uniqueItems?: boolean - - minimum?: number - maximum?: number - exclusiveMinimum?: number | boolean - exclusiveMaximum?: number | boolean - multipleOf?: number - - minLength?: number - maxLength?: number - pattern?: string - - __absolute_ref__?: string - __absolute_recursive_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[] -} diff --git a/packages/utils/src/json-schema/ucs2-length.ts b/packages/utils/src/json-schema/ucs2-length.ts deleted file mode 100644 index a23509723..000000000 --- a/packages/utils/src/json-schema/ucs2-length.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable */ -/** - * Get UCS-2 length of a string - * https://mathiasbynens.be/notes/javascript-encoding - * https://github.com/bestiejs/punycode.js - punycode.ucs2.decode - */ -export function ucs2length(s: string) { - let result = 0 - let length = s.length - let index = 0 - let charCode: number - while (index < length) { - result++ - charCode = s.charCodeAt(index++) - if (charCode >= 0xd800 && charCode <= 0xdbff && index < length) { - // high surrogate, and there is a next character - charCode = s.charCodeAt(index) - if ((charCode & 0xfc00) == 0xdc00) { - // low surrogate - index++ - } - } - } - return result -} diff --git a/packages/utils/src/json-schema/validate.ts b/packages/utils/src/json-schema/validate.ts deleted file mode 100644 index e6a41fa59..000000000 --- a/packages/utils/src/json-schema/validate.ts +++ /dev/null @@ -1,1165 +0,0 @@ -/* eslint-disable */ -import { deepCompareStrict } from './deep-compare-strict.js' -import { dereference } from './dereference.js' -import { fastFormat } from './format.js' -import { encodePointer } from './pointer.js' -import { - InstanceType, - OutputUnit, - Schema, - SchemaDraft, - ValidationResult, -} from './types.js' -import { ucs2length } from './ucs2-length.js' - -export type Evaluated = Record - -export function validate( - instance: any, - schema: Schema | boolean, - draft: SchemaDraft = '2019-09', - lookup = dereference(schema), - shortCircuit = true, - recursiveAnchor: Schema | null = null, - instanceLocation = '#', - schemaLocation = '#', - evaluated: Evaluated = Object.create(null) -): ValidationResult { - if (schema === true) { - return { valid: true, errors: [] } - } - - if (schema === false) { - return { - valid: false, - errors: [ - { - instanceLocation, - keyword: 'false', - keywordLocation: instanceLocation, - error: 'False boolean schema.', - }, - ], - } - } - - const rawInstanceType = typeof instance - let instanceType: Exclude - switch (rawInstanceType) { - case 'boolean': - case 'number': - case 'string': - instanceType = rawInstanceType - break - case 'object': - if (instance === null) { - instanceType = 'null' - } else if (Array.isArray(instance)) { - instanceType = 'array' - } else { - instanceType = 'object' - } - break - default: - // undefined, bigint, function, symbol - throw new Error( - `Instances of "${rawInstanceType}" type are not supported.` - ) - } - - 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, - - prefixItems: $prefixItems, - 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__, - __absolute_recursive_ref__, - } = schema - - const errors: OutputUnit[] = [] - - if ($recursiveAnchor === true && recursiveAnchor === null) { - recursiveAnchor = schema - } - - if ($recursiveRef === '#') { - const refSchema = - recursiveAnchor === null - ? (lookup[__absolute_recursive_ref__!] as Schema) - : recursiveAnchor - const keywordLocation = `${schemaLocation}/$recursiveRef` - const result = validate( - instance, - recursiveAnchor === null ? schema : recursiveAnchor, - draft, - lookup, - shortCircuit, - refSchema, - instanceLocation, - keywordLocation, - evaluated - ) - if (!result.valid) { - errors.push( - { - instanceLocation, - keyword: '$recursiveRef', - keywordLocation, - error: 'A subschema had errors.', - }, - ...result.errors - ) - } - } - - if ($ref !== undefined) { - const uri = __absolute_ref__ || $ref - const refSchema = lookup[uri] - if (refSchema === undefined) { - let message = `Unresolved $ref "${$ref}".` - if (__absolute_ref__ && __absolute_ref__ !== $ref) { - message += ` Absolute URI "${__absolute_ref__}".` - } - message += `\nKnown schemas:\n- ${Object.keys(lookup).join('\n- ')}` - throw new Error(message) - } - const keywordLocation = `${schemaLocation}/$ref` - const result = validate( - instance, - refSchema, - draft, - lookup, - shortCircuit, - 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 { valid: errors.length === 0, errors } - } - } - - if (Array.isArray($type)) { - let length = $type.length - let valid = false - for (let i = 0; i < length; i++) { - if ( - instanceType === $type[i] || - ($type[i] === 'integer' && - instanceType === 'number' && - instance % 1 === 0 && - instance === instance) - ) { - valid = true - break - } - } - if (!valid) { - 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) { - errors.push({ - instanceLocation, - keyword: 'type', - keywordLocation: `${schemaLocation}/type`, - error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`, - }) - } - } else if ($type !== undefined && instanceType !== $type) { - errors.push({ - instanceLocation, - keyword: 'type', - keywordLocation: `${schemaLocation}/type`, - error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`, - }) - } - - if ($const !== undefined) { - if (instanceType === 'object' || instanceType === 'array') { - if (!deepCompareStrict(instance, $const)) { - errors.push({ - instanceLocation, - keyword: 'const', - keywordLocation: `${schemaLocation}/const`, - error: `Instance does not match ${JSON.stringify($const)}.`, - }) - } - } else if (instance !== $const) { - errors.push({ - instanceLocation, - keyword: 'const', - keywordLocation: `${schemaLocation}/const`, - error: `Instance does not match ${JSON.stringify($const)}.`, - }) - } - } - - if ($enum !== undefined) { - if (instanceType === 'object' || instanceType === 'array') { - if (!$enum.some((value) => deepCompareStrict(instance, value))) { - 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)) { - errors.push({ - instanceLocation, - keyword: 'enum', - keywordLocation: `${schemaLocation}/enum`, - error: `Instance does not match any of ${JSON.stringify($enum)}.`, - }) - } - } - - if ($not !== undefined) { - const keywordLocation = `${schemaLocation}/not` - const result = validate( - instance, - $not, - draft, - lookup, - shortCircuit, - recursiveAnchor, - instanceLocation, - keywordLocation /*, - evaluated*/ - ) - if (result.valid) { - errors.push({ - instanceLocation, - keyword: 'not', - keywordLocation, - error: 'Instance matched "not" schema.', - }) - } - } - - let subEvaluateds: Array = [] - - 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 subEvaluated: Evaluated = Object.create(evaluated) - const result = validate( - instance, - subSchema, - draft, - lookup, - shortCircuit, - $recursiveAnchor === true ? recursiveAnchor : null, - instanceLocation, - `${keywordLocation}/${i}`, - subEvaluated - ) - errors.push(...result.errors) - anyValid = anyValid || result.valid - if (result.valid) { - subEvaluateds.push(subEvaluated) - } - } - if (anyValid) { - errors.length = errorsLength - } else { - errors.splice(errorsLength, 0, { - instanceLocation, - keyword: 'anyOf', - keywordLocation, - error: 'Instance does not match any subschemas.', - }) - } - } - - 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 subEvaluated: Evaluated = Object.create(evaluated) - const result = validate( - instance, - subSchema, - draft, - lookup, - shortCircuit, - $recursiveAnchor === true ? recursiveAnchor : null, - instanceLocation, - `${keywordLocation}/${i}`, - subEvaluated - ) - errors.push(...result.errors) - allValid = allValid && result.valid - if (result.valid) { - subEvaluateds.push(subEvaluated) - } - } - if (allValid) { - errors.length = errorsLength - } else { - errors.splice(errorsLength, 0, { - instanceLocation, - keyword: 'allOf', - keywordLocation, - error: `Instance does not match every subschema.`, - }) - } - } - - if ($oneOf !== undefined) { - const keywordLocation = `${schemaLocation}/oneOf` - const errorsLength = errors.length - const matches = $oneOf.filter((subSchema, i) => { - const subEvaluated: Evaluated = Object.create(evaluated) - const result = validate( - instance, - subSchema, - draft, - lookup, - shortCircuit, - $recursiveAnchor === true ? recursiveAnchor : null, - instanceLocation, - `${keywordLocation}/${i}`, - subEvaluated - ) - errors.push(...result.errors) - if (result.valid) { - subEvaluateds.push(subEvaluated) - } - 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 (instanceType === 'object' || instanceType === 'array') { - Object.assign(evaluated, ...subEvaluateds) - } - - if ($if !== undefined) { - const keywordLocation = `${schemaLocation}/if` - const conditionResult = validate( - instance, - $if, - draft, - lookup, - shortCircuit, - recursiveAnchor, - instanceLocation, - keywordLocation, - evaluated - ).valid - if (conditionResult) { - if ($then !== undefined) { - const thenResult = validate( - instance, - $then, - draft, - lookup, - shortCircuit, - 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) { - const elseResult = validate( - instance, - $else, - draft, - lookup, - shortCircuit, - recursiveAnchor, - instanceLocation, - `${schemaLocation}/else`, - evaluated - ) - if (!elseResult.valid) { - errors.push( - { - instanceLocation, - keyword: 'if', - keywordLocation, - error: `Instance does not match "else" schema.`, - }, - ...elseResult.errors - ) - } - } - } - - if (instanceType === 'object') { - if ($required !== undefined) { - for (const key of $required) { - if (!(key in instance)) { - errors.push({ - instanceLocation, - keyword: 'required', - keywordLocation: `${schemaLocation}/required`, - error: `Instance does not have required property "${key}".`, - }) - } - } - } - - const keys = Object.keys(instance) - - if ($minProperties !== undefined && keys.length < $minProperties) { - errors.push({ - instanceLocation, - keyword: 'minProperties', - keywordLocation: `${schemaLocation}/minProperties`, - error: `Instance does not have at least ${$minProperties} properties.`, - }) - } - - if ($maxProperties !== undefined && keys.length > $maxProperties) { - 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) { - const subInstancePointer = `${instanceLocation}/${encodePointer(key)}` - const result = validate( - key, - $propertyNames, - draft, - lookup, - shortCircuit, - recursiveAnchor, - subInstancePointer, - keywordLocation - ) - if (!result.valid) { - errors.push( - { - instanceLocation, - keyword: 'propertyNames', - keywordLocation, - error: `Property name "${key}" does not match schema.`, - }, - ...result.errors - ) - } - } - } - - if ($dependentRequired !== undefined) { - const keywordLocation = `${schemaLocation}/dependantRequired` - for (const key in $dependentRequired) { - if (key in instance) { - const required = $dependentRequired[key] as string[] - for (const dependantKey of required) { - if (!(dependantKey in instance)) { - errors.push({ - instanceLocation, - keyword: 'dependentRequired', - keywordLocation, - error: `Instance has "${key}" but does not have "${dependantKey}".`, - }) - } - } - } - } - } - - if ($dependentSchemas !== undefined) { - for (const key in $dependentSchemas) { - const keywordLocation = `${schemaLocation}/dependentSchemas` - if (key in instance) { - const result = validate( - instance, - $dependentSchemas[key], - draft, - lookup, - shortCircuit, - 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 - ) - } - } - } - } - - if ($dependencies !== undefined) { - const keywordLocation = `${schemaLocation}/dependencies` - for (const key in $dependencies) { - if (key in instance) { - const propsOrSchema = $dependencies[key] as Schema | string[] - if (Array.isArray(propsOrSchema)) { - 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 { - const result = validate( - instance, - propsOrSchema, - draft, - lookup, - shortCircuit, - 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 - ) - } - } - } - } - } - - const thisEvaluated = Object.create(null) - - let stop = false - - if ($properties !== undefined) { - const keywordLocation = `${schemaLocation}/properties` - for (const key in $properties) { - if (!(key in instance)) { - continue - } - const subInstancePointer = `${instanceLocation}/${encodePointer(key)}` - const result = validate( - instance[key], - $properties[key], - draft, - lookup, - shortCircuit, - recursiveAnchor, - subInstancePointer, - `${keywordLocation}/${encodePointer(key)}` - ) - if (result.valid) { - evaluated[key] = thisEvaluated[key] = true - } else { - stop = shortCircuit - errors.push( - { - instanceLocation, - keyword: 'properties', - keywordLocation, - error: `Property "${key}" does not match schema.`, - }, - ...result.errors - ) - if (stop) break - } - } - } - - if (!stop && $patternProperties !== undefined) { - const keywordLocation = `${schemaLocation}/patternProperties` - for (const pattern in $patternProperties) { - const regex = new RegExp(pattern) - const subSchema = $patternProperties[pattern] - for (const key in instance) { - if (!regex.test(key)) { - continue - } - const subInstancePointer = `${instanceLocation}/${encodePointer(key)}` - const result = validate( - instance[key], - subSchema, - draft, - lookup, - shortCircuit, - recursiveAnchor, - subInstancePointer, - `${keywordLocation}/${encodePointer(pattern)}` - ) - if (result.valid) { - evaluated[key] = thisEvaluated[key] = true - } else { - stop = shortCircuit - errors.push( - { - instanceLocation, - keyword: 'patternProperties', - keywordLocation, - error: `Property "${key}" matches pattern "${pattern}" but does not match associated schema.`, - }, - ...result.errors - ) - } - } - } - } - - if (!stop && $additionalProperties !== undefined) { - const keywordLocation = `${schemaLocation}/additionalProperties` - for (const key in instance) { - if (thisEvaluated[key]) { - continue - } - const subInstancePointer = `${instanceLocation}/${encodePointer(key)}` - const result = validate( - instance[key], - $additionalProperties, - draft, - lookup, - shortCircuit, - recursiveAnchor, - subInstancePointer, - keywordLocation - ) - if (result.valid) { - evaluated[key] = true - } else { - stop = shortCircuit - errors.push( - { - instanceLocation, - keyword: 'additionalProperties', - keywordLocation, - error: `Property "${key}" does not match additional properties schema.`, - }, - ...result.errors - ) - } - } - } else if (!stop && $unevaluatedProperties !== undefined) { - const keywordLocation = `${schemaLocation}/unevaluatedProperties` - for (const key in instance) { - if (!evaluated[key]) { - const subInstancePointer = `${instanceLocation}/${encodePointer(key)}` - const result = validate( - instance[key], - $unevaluatedProperties, - draft, - lookup, - shortCircuit, - recursiveAnchor, - subInstancePointer, - keywordLocation - ) - if (result.valid) { - evaluated[key] = true - } else { - errors.push( - { - instanceLocation, - keyword: 'unevaluatedProperties', - keywordLocation, - error: `Property "${key}" does not match unevaluated properties schema.`, - }, - ...result.errors - ) - } - } - } - } - } else if (instanceType === 'array') { - if ($maxItems !== undefined && instance.length > $maxItems) { - errors.push({ - instanceLocation, - keyword: 'maxItems', - keywordLocation: `${schemaLocation}/maxItems`, - error: `Array has too many items (${instance.length} > ${$maxItems}).`, - }) - } - - if ($minItems !== undefined && instance.length < $minItems) { - errors.push({ - instanceLocation, - keyword: 'minItems', - keywordLocation: `${schemaLocation}/minItems`, - error: `Array has too few items (${instance.length} < ${$minItems}).`, - }) - } - - const length: number = instance.length - let i = 0 - let stop = false - - if ($prefixItems !== undefined) { - const keywordLocation = `${schemaLocation}/prefixItems` - const length2 = Math.min($prefixItems.length, length) - for (; i < length2; i++) { - const result = validate( - instance[i], - $prefixItems[i], - draft, - lookup, - shortCircuit, - recursiveAnchor, - `${instanceLocation}/${i}`, - `${keywordLocation}/${i}` - ) - evaluated[i] = true - if (!result.valid) { - stop = shortCircuit - errors.push( - { - instanceLocation, - keyword: 'prefixItems', - keywordLocation, - error: `Items did not match schema.`, - }, - ...result.errors - ) - if (stop) break - } - } - } - - if ($items !== undefined) { - const keywordLocation = `${schemaLocation}/items` - if (Array.isArray($items)) { - const length2 = Math.min($items.length, length) - for (; i < length2; i++) { - const result = validate( - instance[i], - $items[i], - draft, - lookup, - shortCircuit, - recursiveAnchor, - `${instanceLocation}/${i}`, - `${keywordLocation}/${i}` - ) - evaluated[i] = true - if (!result.valid) { - stop = shortCircuit - errors.push( - { - instanceLocation, - keyword: 'items', - keywordLocation, - error: `Items did not match schema.`, - }, - ...result.errors - ) - if (stop) break - } - } - } else { - for (; i < length; i++) { - const result = validate( - instance[i], - $items, - draft, - lookup, - shortCircuit, - recursiveAnchor, - `${instanceLocation}/${i}`, - keywordLocation - ) - evaluated[i] = true - if (!result.valid) { - stop = shortCircuit - errors.push( - { - instanceLocation, - keyword: 'items', - keywordLocation, - error: `Items did not match schema.`, - }, - ...result.errors - ) - if (stop) break - } - } - } - - if (!stop && $additionalItems !== undefined) { - const keywordLocation = `${schemaLocation}/additionalItems` - for (; i < length; i++) { - const result = validate( - instance[i], - $additionalItems, - draft, - lookup, - shortCircuit, - recursiveAnchor, - `${instanceLocation}/${i}`, - keywordLocation - ) - evaluated[i] = true - if (!result.valid) { - stop = shortCircuit - errors.push( - { - instanceLocation, - keyword: 'additionalItems', - keywordLocation, - error: `Items did not match additional items schema.`, - }, - ...result.errors - ) - } - } - } - } - - if ($contains !== undefined) { - 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 j = 0; j < length; j++) { - const result = validate( - instance[j], - $contains, - draft, - lookup, - shortCircuit, - recursiveAnchor, - `${instanceLocation}/${j}`, - keywordLocation - ) - if (result.valid) { - evaluated[j] = true - contained++ - } 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 (!stop && $unevaluatedItems !== undefined) { - const keywordLocation = `${schemaLocation}/unevaluatedItems` - for (i; i < length; i++) { - if (evaluated[i]) { - continue - } - const result = validate( - instance[i], - $unevaluatedItems, - draft, - lookup, - shortCircuit, - recursiveAnchor, - `${instanceLocation}/${i}`, - keywordLocation - ) - evaluated[i] = true - if (!result.valid) { - errors.push( - { - instanceLocation, - keyword: 'unevaluatedItems', - keywordLocation, - error: `Items did not match unevaluated items schema.`, - }, - ...result.errors - ) - } - } - } - - if ($uniqueItems) { - for (let j = 0; j < length; j++) { - const a = instance[j] - const ao = typeof a === 'object' && a !== null - for (let k = 0; k < length; k++) { - if (j === k) { - continue - } - const b = instance[k] - const bo = typeof b === 'object' && b !== null - if (a === b || (ao && bo && deepCompareStrict(a, b))) { - 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 - } - } - } - } - } else if (instanceType === 'number') { - if (draft === '4') { - if ( - $minimum !== undefined && - (($exclusiveMinimum === true && instance <= $minimum) || - instance < $minimum) - ) { - errors.push({ - instanceLocation, - keyword: 'minimum', - keywordLocation: `${schemaLocation}/minimum`, - error: `${instance} is less than ${ - $exclusiveMinimum ? 'or equal to ' : '' - } ${$minimum}.`, - }) - } - if ( - $maximum !== undefined && - (($exclusiveMaximum === true && instance >= $maximum) || - instance > $maximum) - ) { - errors.push({ - instanceLocation, - keyword: 'maximum', - keywordLocation: `${schemaLocation}/maximum`, - error: `${instance} is greater than ${ - $exclusiveMaximum ? 'or equal to ' : '' - } ${$maximum}.`, - }) - } - } else { - if ($minimum !== undefined && instance < $minimum) { - errors.push({ - instanceLocation, - keyword: 'minimum', - keywordLocation: `${schemaLocation}/minimum`, - error: `${instance} is less than ${$minimum}.`, - }) - } - if ($maximum !== undefined && instance > $maximum) { - errors.push({ - instanceLocation, - keyword: 'maximum', - keywordLocation: `${schemaLocation}/maximum`, - error: `${instance} is greater than ${$maximum}.`, - }) - } - if ($exclusiveMinimum !== undefined && instance <= $exclusiveMinimum) { - errors.push({ - instanceLocation, - keyword: 'exclusiveMinimum', - keywordLocation: `${schemaLocation}/exclusiveMinimum`, - error: `${instance} is less than ${$exclusiveMinimum}.`, - }) - } - if ($exclusiveMaximum !== undefined && instance >= $exclusiveMaximum) { - errors.push({ - instanceLocation, - keyword: 'exclusiveMaximum', - keywordLocation: `${schemaLocation}/exclusiveMaximum`, - error: `${instance} is greater than or equal to ${$exclusiveMaximum}.`, - }) - } - } - if ($multipleOf !== undefined) { - const remainder = instance % $multipleOf - if ( - Math.abs(0 - remainder) >= 1.1920929e-7 && - Math.abs($multipleOf - remainder) >= 1.1920929e-7 - ) { - errors.push({ - instanceLocation, - keyword: 'multipleOf', - keywordLocation: `${schemaLocation}/multipleOf`, - error: `${instance} is not a multiple of ${$multipleOf}.`, - }) - } - } - } else if (instanceType === 'string') { - 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 && length > $maxLength) { - errors.push({ - instanceLocation, - keyword: 'maxLength', - keywordLocation: `${schemaLocation}/maxLength`, - error: `String is too long (${length} > ${$maxLength}).`, - }) - } - if ($pattern !== undefined && !new RegExp($pattern).test(instance)) { - errors.push({ - instanceLocation, - keyword: 'pattern', - keywordLocation: `${schemaLocation}/pattern`, - error: `String does not match pattern.`, - }) - } - if ( - $format !== undefined && - fastFormat[$format] && - !fastFormat[$format](instance) - ) { - errors.push({ - instanceLocation, - keyword: 'format', - keywordLocation: `${schemaLocation}/format`, - error: `String does not match format "${$format}".`, - }) - } - } - - return { valid: errors.length === 0, errors } -} diff --git a/packages/utils/src/json-schema/validator.ts b/packages/utils/src/json-schema/validator.ts deleted file mode 100644 index 55bd338c2..000000000 --- a/packages/utils/src/json-schema/validator.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable */ -import { dereference } from './dereference.js' -import { Schema, SchemaDraft } from './types.js' -import { validate } from './validate.js' - -export class Validator { - private readonly lookup: ReturnType - - constructor( - private readonly schema: Schema | boolean, - private readonly draft: SchemaDraft = '2019-09', - private readonly shortCircuit = true - ) { - this.lookup = dereference(schema) - } - - public validate(instance: any) { - return validate( - instance, - this.schema, - this.draft, - this.lookup, - this.shortCircuit - ) - } - - public addSchema(schema: Schema, id?: string) { - if (id) { - schema = { ...schema, $id: id } - } - dereference(schema, this.lookup) - } -} diff --git a/yarn.lock b/yarn.lock index 5db0209e0..1e27a04c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1325,6 +1325,13 @@ __metadata: languageName: node linkType: hard +"@cfworker/json-schema@npm:^4.0.2": + version: 4.0.2 + resolution: "@cfworker/json-schema@npm:4.0.2" + checksum: 10c0/672755c0eee6e9cff22dc2c20e85374c2b516a7c387f3acfbbdc1805046a4b30e260ce979b1da06c24309f8a87ed3548229268881c3f5347a3df8c8577833974 + languageName: node + linkType: hard + "@commitlint/cli@npm:^9.0.1": version: 9.1.2 resolution: "@commitlint/cli@npm:9.1.2" @@ -2306,6 +2313,7 @@ __metadata: version: 0.0.0-use.local resolution: "@kiltprotocol/utils@workspace:packages/utils" dependencies: + "@cfworker/json-schema": "npm:^4.0.2" "@kiltprotocol/eddsa-jcs-2022": "npm:^0.1.0" "@kiltprotocol/es256k-jcs-2023": "npm:^0.1.0" "@kiltprotocol/jcs-data-integrity-proofs-common": "npm:^0.1.0"