diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-02-14 11:19:54 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-14 11:19:54 -0500 |
commit | e419a418f6f03ef0a24330b67e7b76c5e3a7c22d (patch) | |
tree | a4c27bdfabc9280d9f6262d93d5152a58de8bc15 /ext/bg/js/json-schema.js | |
parent | 43d1457ebfe23196348649c245dfb942a0f00a1a (diff) |
Move bg/js (#1387)
* Move bg/js/anki.js to js/comm/anki.js
* Move bg/js/mecab.js to js/comm/mecab.js
* Move bg/js/search-main.js to js/display/search-main.js
* Move bg/js/template-patcher.js to js/templates/template-patcher.js
* Move bg/js/template-renderer-frame-api.js to js/templates/template-renderer-frame-api.js
* Move bg/js/template-renderer-frame-main.js to js/templates/template-renderer-frame-main.js
* Move bg/js/template-renderer-proxy.js to js/templates/template-renderer-proxy.js
* Move bg/js/template-renderer.js to js/templates/template-renderer.js
* Move bg/js/media-utility.js to js/media/media-utility.js
* Move bg/js/native-simple-dom-parser.js to js/dom/native-simple-dom-parser.js
* Move bg/js/simple-dom-parser.js to js/dom/simple-dom-parser.js
* Move bg/js/audio-downloader.js to js/media/audio-downloader.js
* Move bg/js/deinflector.js to js/language/deinflector.js
* Move bg/js/backend.js to js/background/backend.js
* Move bg/js/translator.js to js/language/translator.js
* Move bg/js/search-display-controller.js to js/display/search-display-controller.js
* Move bg/js/request-builder.js to js/background/request-builder.js
* Move bg/js/text-source-map.js to js/general/text-source-map.js
* Move bg/js/clipboard-reader.js to js/comm/clipboard-reader.js
* Move bg/js/clipboard-monitor.js to js/comm/clipboard-monitor.js
* Move bg/js/query-parser.js to js/display/query-parser.js
* Move bg/js/profile-conditions.js to js/background/profile-conditions.js
* Move bg/js/dictionary-database.js to js/language/dictionary-database.js
* Move bg/js/dictionary-importer.js to js/language/dictionary-importer.js
* Move bg/js/anki-note-builder.js to js/data/anki-note-builder.js
* Move bg/js/anki-note-data.js to js/data/anki-note-data.js
* Move bg/js/database.js to js/data/database.js
* Move bg/js/json-schema.js to js/data/json-schema.js
* Move bg/js/options.js to js/data/options-util.js
* Move bg/js/background-main.js to js/background/background-main.js
* Move bg/js/permissions-util.js to js/data/permissions-util.js
* Move bg/js/context-main.js to js/pages/action-popup-main.js
* Move bg/js/generic-page-main.js to js/pages/generic-page-main.js
* Move bg/js/info-main.js to js/pages/info-main.js
* Move bg/js/permissions-main.js to js/pages/permissions-main.js
* Move bg/js/welcome-main.js to js/pages/welcome-main.js
Diffstat (limited to 'ext/bg/js/json-schema.js')
-rw-r--r-- | ext/bg/js/json-schema.js | 757 |
1 files changed, 0 insertions, 757 deletions
diff --git a/ext/bg/js/json-schema.js b/ext/bg/js/json-schema.js deleted file mode 100644 index 7b6b9c53..00000000 --- a/ext/bg/js/json-schema.js +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Copyright (C) 2019-2021 Yomichan Authors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -/* global - * CacheMap - */ - -class JsonSchemaProxyHandler { - constructor(schema, jsonSchemaValidator) { - this._schema = schema; - this._jsonSchemaValidator = jsonSchemaValidator; - } - - getPrototypeOf(target) { - return Object.getPrototypeOf(target); - } - - setPrototypeOf() { - throw new Error('setPrototypeOf not supported'); - } - - isExtensible(target) { - return Object.isExtensible(target); - } - - preventExtensions(target) { - Object.preventExtensions(target); - return true; - } - - getOwnPropertyDescriptor(target, property) { - return Object.getOwnPropertyDescriptor(target, property); - } - - defineProperty() { - throw new Error('defineProperty not supported'); - } - - has(target, property) { - return property in target; - } - - get(target, property) { - if (typeof property === 'symbol') { - return target[property]; - } - - if (Array.isArray(target)) { - if (typeof property === 'string' && /^\d+$/.test(property)) { - property = parseInt(property, 10); - } else if (typeof property === 'string') { - return target[property]; - } - } - - const propertySchema = this._jsonSchemaValidator.getPropertySchema(this._schema, property, target); - if (propertySchema === null) { - return; - } - - const value = target[property]; - return value !== null && typeof value === 'object' ? this._jsonSchemaValidator.createProxy(value, propertySchema) : value; - } - - set(target, property, value) { - if (Array.isArray(target)) { - if (typeof property === 'string' && /^\d+$/.test(property)) { - property = parseInt(property, 10); - if (property > target.length) { - throw new Error('Array index out of range'); - } - } else if (typeof property === 'string') { - target[property] = value; - return true; - } - } - - const propertySchema = this._jsonSchemaValidator.getPropertySchema(this._schema, property, target); - if (propertySchema === null) { - throw new Error(`Property ${property} not supported`); - } - - value = clone(value); - - this._jsonSchemaValidator.validate(value, propertySchema); - - target[property] = value; - return true; - } - - deleteProperty(target, property) { - const required = this._schema.required; - if (Array.isArray(required) && required.includes(property)) { - throw new Error(`${property} cannot be deleted`); - } - return Reflect.deleteProperty(target, property); - } - - ownKeys(target) { - return Reflect.ownKeys(target); - } - - apply() { - throw new Error('apply not supported'); - } - - construct() { - throw new Error('construct not supported'); - } -} - -class JsonSchemaValidator { - constructor() { - this._regexCache = new CacheMap(100); - } - - createProxy(target, schema) { - return new Proxy(target, new JsonSchemaProxyHandler(schema, this)); - } - - isValid(value, schema) { - try { - this.validate(value, schema); - return true; - } catch (e) { - return false; - } - } - - validate(value, schema) { - const info = new JsonSchemaTraversalInfo(value, schema); - this._validate(value, schema, info); - } - - getValidValueOrDefault(schema, value) { - const info = new JsonSchemaTraversalInfo(value, schema); - return this._getValidValueOrDefault(schema, value, info); - } - - getPropertySchema(schema, property, value) { - return this._getPropertySchema(schema, property, value, null); - } - - clearCache() { - this._regexCache.clear(); - } - - // Private - - _getPropertySchema(schema, property, value, path) { - const type = this._getSchemaOrValueType(schema, value); - switch (type) { - case 'object': - { - const properties = schema.properties; - if (this._isObject(properties)) { - const propertySchema = properties[property]; - if (this._isObject(propertySchema)) { - if (path !== null) { path.push(['properties', properties], [property, propertySchema]); } - return propertySchema; - } - } - - const additionalProperties = schema.additionalProperties; - if (additionalProperties === false) { - return null; - } else if (this._isObject(additionalProperties)) { - if (path !== null) { path.push(['additionalProperties', additionalProperties]); } - return additionalProperties; - } else { - const result = JsonSchemaValidator.unconstrainedSchema; - if (path !== null) { path.push([null, result]); } - return result; - } - } - case 'array': - { - const items = schema.items; - if (this._isObject(items)) { - return items; - } - if (Array.isArray(items)) { - if (property >= 0 && property < items.length) { - const propertySchema = items[property]; - if (this._isObject(propertySchema)) { - if (path !== null) { path.push(['items', items], [property, propertySchema]); } - return propertySchema; - } - } - } - - const additionalItems = schema.additionalItems; - if (additionalItems === false) { - return null; - } else if (this._isObject(additionalItems)) { - if (path !== null) { path.push(['additionalItems', additionalItems]); } - return additionalItems; - } else { - const result = JsonSchemaValidator.unconstrainedSchema; - if (path !== null) { path.push([null, result]); } - return result; - } - } - default: - return null; - } - } - - _getSchemaOrValueType(schema, value) { - const type = schema.type; - - if (Array.isArray(type)) { - if (typeof value !== 'undefined') { - const valueType = this._getValueType(value); - if (type.indexOf(valueType) >= 0) { - return valueType; - } - } - return null; - } - - if (typeof type === 'undefined') { - if (typeof value !== 'undefined') { - return this._getValueType(value); - } - return null; - } - - return type; - } - - _validate(value, schema, info) { - this._validateSingleSchema(value, schema, info); - this._validateConditional(value, schema, info); - this._validateAllOf(value, schema, info); - this._validateAnyOf(value, schema, info); - this._validateOneOf(value, schema, info); - this._validateNoneOf(value, schema, info); - } - - _validateConditional(value, schema, info) { - const ifSchema = schema.if; - if (!this._isObject(ifSchema)) { return; } - - let okay = true; - info.schemaPush('if', ifSchema); - try { - this._validate(value, ifSchema, info); - } catch (e) { - okay = false; - } - info.schemaPop(); - - const nextSchema = okay ? schema.then : schema.else; - if (this._isObject(nextSchema)) { - info.schemaPush(okay ? 'then' : 'else', nextSchema); - this._validate(value, nextSchema, info); - info.schemaPop(); - } - } - - _validateAllOf(value, schema, info) { - const subSchemas = schema.allOf; - if (!Array.isArray(subSchemas)) { return; } - - info.schemaPush('allOf', subSchemas); - for (let i = 0; i < subSchemas.length; ++i) { - const subSchema = subSchemas[i]; - info.schemaPush(i, subSchema); - this._validate(value, subSchema, info); - info.schemaPop(); - } - info.schemaPop(); - } - - _validateAnyOf(value, schema, info) { - const subSchemas = schema.anyOf; - if (!Array.isArray(subSchemas)) { return; } - - info.schemaPush('anyOf', subSchemas); - for (let i = 0; i < subSchemas.length; ++i) { - const subSchema = subSchemas[i]; - info.schemaPush(i, subSchema); - try { - this._validate(value, subSchema, info); - return; - } catch (e) { - // NOP - } - info.schemaPop(); - } - - throw new JsonSchemaValidationError('0 anyOf schemas matched', value, schema, info); - // info.schemaPop(); // Unreachable - } - - _validateOneOf(value, schema, info) { - const subSchemas = schema.oneOf; - if (!Array.isArray(subSchemas)) { return; } - - info.schemaPush('oneOf', subSchemas); - let count = 0; - for (let i = 0; i < subSchemas.length; ++i) { - const subSchema = subSchemas[i]; - info.schemaPush(i, subSchema); - try { - this._validate(value, subSchema, info); - ++count; - } catch (e) { - // NOP - } - info.schemaPop(); - } - - if (count !== 1) { - throw new JsonSchemaValidationError(`${count} oneOf schemas matched`, value, schema, info); - } - - info.schemaPop(); - } - - _validateNoneOf(value, schema, info) { - const subSchemas = schema.not; - if (!Array.isArray(subSchemas)) { return; } - - info.schemaPush('not', subSchemas); - for (let i = 0; i < subSchemas.length; ++i) { - const subSchema = subSchemas[i]; - info.schemaPush(i, subSchema); - try { - this._validate(value, subSchema, info); - } catch (e) { - info.schemaPop(); - continue; - } - throw new JsonSchemaValidationError(`not[${i}] schema matched`, value, schema, info); - } - info.schemaPop(); - } - - _validateSingleSchema(value, schema, info) { - const type = this._getValueType(value); - const schemaType = schema.type; - if (!this._isValueTypeAny(value, type, schemaType)) { - throw new JsonSchemaValidationError(`Value type ${type} does not match schema type ${schemaType}`, value, schema, info); - } - - const schemaConst = schema.const; - if (typeof schemaConst !== 'undefined' && !this._valuesAreEqual(value, schemaConst)) { - throw new JsonSchemaValidationError('Invalid constant value', value, schema, info); - } - - const schemaEnum = schema.enum; - if (Array.isArray(schemaEnum) && !this._valuesAreEqualAny(value, schemaEnum)) { - throw new JsonSchemaValidationError('Invalid enum value', value, schema, info); - } - - switch (type) { - case 'number': - this._validateNumber(value, schema, info); - break; - case 'string': - this._validateString(value, schema, info); - break; - case 'array': - this._validateArray(value, schema, info); - break; - case 'object': - this._validateObject(value, schema, info); - break; - } - } - - _validateNumber(value, schema, info) { - const multipleOf = schema.multipleOf; - if (typeof multipleOf === 'number' && Math.floor(value / multipleOf) * multipleOf !== value) { - throw new JsonSchemaValidationError(`Number is not a multiple of ${multipleOf}`, value, schema, info); - } - - const minimum = schema.minimum; - if (typeof minimum === 'number' && value < minimum) { - throw new JsonSchemaValidationError(`Number is less than ${minimum}`, value, schema, info); - } - - const exclusiveMinimum = schema.exclusiveMinimum; - if (typeof exclusiveMinimum === 'number' && value <= exclusiveMinimum) { - throw new JsonSchemaValidationError(`Number is less than or equal to ${exclusiveMinimum}`, value, schema, info); - } - - const maximum = schema.maximum; - if (typeof maximum === 'number' && value > maximum) { - throw new JsonSchemaValidationError(`Number is greater than ${maximum}`, value, schema, info); - } - - const exclusiveMaximum = schema.exclusiveMaximum; - if (typeof exclusiveMaximum === 'number' && value >= exclusiveMaximum) { - throw new JsonSchemaValidationError(`Number is greater than or equal to ${exclusiveMaximum}`, value, schema, info); - } - } - - _validateString(value, schema, info) { - const minLength = schema.minLength; - if (typeof minLength === 'number' && value.length < minLength) { - throw new JsonSchemaValidationError('String length too short', value, schema, info); - } - - const maxLength = schema.maxLength; - if (typeof maxLength === 'number' && value.length > maxLength) { - throw new JsonSchemaValidationError('String length too long', value, schema, info); - } - - const pattern = schema.pattern; - if (typeof pattern === 'string') { - let patternFlags = schema.patternFlags; - if (typeof patternFlags !== 'string') { patternFlags = ''; } - - let regex; - try { - regex = this._getRegex(pattern, patternFlags); - } catch (e) { - throw new JsonSchemaValidationError(`Pattern is invalid (${e.message})`, value, schema, info); - } - - if (!regex.test(value)) { - throw new JsonSchemaValidationError('Pattern match failed', value, schema, info); - } - } - } - - _validateArray(value, schema, info) { - const minItems = schema.minItems; - if (typeof minItems === 'number' && value.length < minItems) { - throw new JsonSchemaValidationError('Array length too short', value, schema, info); - } - - const maxItems = schema.maxItems; - if (typeof maxItems === 'number' && value.length > maxItems) { - throw new JsonSchemaValidationError('Array length too long', value, schema, info); - } - - this._validateArrayContains(value, schema, info); - - for (let i = 0, ii = value.length; i < ii; ++i) { - const schemaPath = []; - const propertySchema = this._getPropertySchema(schema, i, value, schemaPath); - if (propertySchema === null) { - throw new JsonSchemaValidationError(`No schema found for array[${i}]`, value, schema, info); - } - - const propertyValue = value[i]; - - for (const [p, s] of schemaPath) { info.schemaPush(p, s); } - info.valuePush(i, propertyValue); - this._validate(propertyValue, propertySchema, info); - info.valuePop(); - for (let j = 0, jj = schemaPath.length; j < jj; ++j) { info.schemaPop(); } - } - } - - _validateArrayContains(value, schema, info) { - const containsSchema = schema.contains; - if (!this._isObject(containsSchema)) { return; } - - info.schemaPush('contains', containsSchema); - for (let i = 0, ii = value.length; i < ii; ++i) { - const propertyValue = value[i]; - info.valuePush(i, propertyValue); - try { - this._validate(propertyValue, containsSchema, info); - info.schemaPop(); - return; - } catch (e) { - // NOP - } - info.valuePop(); - } - throw new JsonSchemaValidationError('contains schema didn\'t match', value, schema, info); - } - - _validateObject(value, schema, info) { - const properties = new Set(Object.getOwnPropertyNames(value)); - - const required = schema.required; - if (Array.isArray(required)) { - for (const property of required) { - if (!properties.has(property)) { - throw new JsonSchemaValidationError(`Missing property ${property}`, value, schema, info); - } - } - } - - const minProperties = schema.minProperties; - if (typeof minProperties === 'number' && properties.length < minProperties) { - throw new JsonSchemaValidationError('Not enough object properties', value, schema, info); - } - - const maxProperties = schema.maxProperties; - if (typeof maxProperties === 'number' && properties.length > maxProperties) { - throw new JsonSchemaValidationError('Too many object properties', value, schema, info); - } - - for (const property of properties) { - const schemaPath = []; - const propertySchema = this._getPropertySchema(schema, property, value, schemaPath); - if (propertySchema === null) { - throw new JsonSchemaValidationError(`No schema found for ${property}`, value, schema, info); - } - - const propertyValue = value[property]; - - for (const [p, s] of schemaPath) { info.schemaPush(p, s); } - info.valuePush(property, propertyValue); - this._validate(propertyValue, propertySchema, info); - info.valuePop(); - for (let i = 0; i < schemaPath.length; ++i) { info.schemaPop(); } - } - } - - _isValueTypeAny(value, type, schemaTypes) { - if (typeof schemaTypes === 'string') { - return this._isValueType(value, type, schemaTypes); - } else if (Array.isArray(schemaTypes)) { - for (const schemaType of schemaTypes) { - if (this._isValueType(value, type, schemaType)) { - return true; - } - } - return false; - } - return true; - } - - _isValueType(value, type, schemaType) { - return ( - type === schemaType || - (schemaType === 'integer' && Math.floor(value) === value) - ); - } - - _getValueType(value) { - const type = typeof value; - if (type === 'object') { - if (value === null) { return 'null'; } - if (Array.isArray(value)) { return 'array'; } - } - return type; - } - - _valuesAreEqualAny(value1, valueList) { - for (const value2 of valueList) { - if (this._valuesAreEqual(value1, value2)) { - return true; - } - } - return false; - } - - _valuesAreEqual(value1, value2) { - return value1 === value2; - } - - _getDefaultTypeValue(type) { - if (typeof type === 'string') { - switch (type) { - case 'null': - return null; - case 'boolean': - return false; - case 'number': - case 'integer': - return 0; - case 'string': - return ''; - case 'array': - return []; - case 'object': - return {}; - } - } - return null; - } - - _getDefaultSchemaValue(schema) { - const schemaType = schema.type; - const schemaDefault = schema.default; - return ( - typeof schemaDefault !== 'undefined' && - this._isValueTypeAny(schemaDefault, this._getValueType(schemaDefault), schemaType) ? - clone(schemaDefault) : - this._getDefaultTypeValue(schemaType) - ); - } - - _getValidValueOrDefault(schema, value, info) { - let type = this._getValueType(value); - if (typeof value === 'undefined' || !this._isValueTypeAny(value, type, schema.type)) { - value = this._getDefaultSchemaValue(schema); - type = this._getValueType(value); - } - - switch (type) { - case 'object': - value = this._populateObjectDefaults(value, schema, info); - break; - case 'array': - value = this._populateArrayDefaults(value, schema, info); - break; - default: - if (!this.isValid(value, schema)) { - const schemaDefault = this._getDefaultSchemaValue(schema); - if (this.isValid(schemaDefault, schema)) { - value = schemaDefault; - } - } - break; - } - - return value; - } - - _populateObjectDefaults(value, schema, info) { - const properties = new Set(Object.getOwnPropertyNames(value)); - - const required = schema.required; - if (Array.isArray(required)) { - for (const property of required) { - properties.delete(property); - - const propertySchema = this._getPropertySchema(schema, property, value, null); - if (propertySchema === null) { continue; } - info.valuePush(property, value); - info.schemaPush(property, propertySchema); - const hasValue = Object.prototype.hasOwnProperty.call(value, property); - value[property] = this._getValidValueOrDefault(propertySchema, hasValue ? value[property] : void 0, info); - info.schemaPop(); - info.valuePop(); - } - } - - for (const property of properties) { - const propertySchema = this._getPropertySchema(schema, property, value, null); - if (propertySchema === null) { - Reflect.deleteProperty(value, property); - } else { - info.valuePush(property, value); - info.schemaPush(property, propertySchema); - value[property] = this._getValidValueOrDefault(propertySchema, value[property], info); - info.schemaPop(); - info.valuePop(); - } - } - - return value; - } - - _populateArrayDefaults(value, schema, info) { - for (let i = 0, ii = value.length; i < ii; ++i) { - const propertySchema = this._getPropertySchema(schema, i, value, null); - if (propertySchema === null) { continue; } - info.valuePush(i, value); - info.schemaPush(i, propertySchema); - value[i] = this._getValidValueOrDefault(propertySchema, value[i], info); - info.schemaPop(); - info.valuePop(); - } - - const minItems = schema.minItems; - if (typeof minItems === 'number' && value.length < minItems) { - for (let i = value.length; i < minItems; ++i) { - const propertySchema = this._getPropertySchema(schema, i, value, null); - if (propertySchema === null) { break; } - info.valuePush(i, value); - info.schemaPush(i, propertySchema); - const item = this._getValidValueOrDefault(propertySchema, void 0, info); - info.schemaPop(); - info.valuePop(); - value.push(item); - } - } - - const maxItems = schema.maxItems; - if (typeof maxItems === 'number' && value.length > maxItems) { - value.splice(maxItems, value.length - maxItems); - } - - return value; - } - - _isObject(value) { - return typeof value === 'object' && value !== null && !Array.isArray(value); - } - - _getRegex(pattern, flags) { - const key = `${flags}:${pattern}`; - let regex = this._regexCache.get(key); - if (typeof regex === 'undefined') { - regex = new RegExp(pattern, flags); - this._regexCache.set(key, regex); - } - return regex; - } -} - -Object.defineProperty(JsonSchemaValidator, 'unconstrainedSchema', { - value: Object.freeze({}), - configurable: false, - enumerable: true, - writable: false -}); - -class JsonSchemaTraversalInfo { - constructor(value, schema) { - this.valuePath = []; - this.schemaPath = []; - this.valuePush(null, value); - this.schemaPush(null, schema); - } - - valuePush(path, value) { - this.valuePath.push([path, value]); - } - - valuePop() { - this.valuePath.pop(); - } - - schemaPush(path, schema) { - this.schemaPath.push([path, schema]); - } - - schemaPop() { - this.schemaPath.pop(); - } -} - -class JsonSchemaValidationError extends Error { - constructor(message, value, schema, info) { - super(message); - this.value = value; - this.schema = schema; - this.info = info; - } -} |