diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4e99e4d6..9d14d38f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,9 @@ ### 2.20.1 + +- `Fix` — Fix sanitisation problem with Inline Tools [#1631](https://github.com/codex-team/editor.js/issues/1631) +- `Refactoring` - The Sanitizer module is util now. - `Refactoring` - Tooltip module is util now. ### 2.20.0 @@ -15,7 +18,7 @@ ### 2.19.2 -- `New` - `toolbar.toggleBlockSettings()` API method added [#1442](https://github.com/codex-team/editor.js/issues/1421). +- `New` - `toolbar.toggleBlockSettings()` API method added [#1442](https://github.com/codex-team/editor.js/issues/1421). - `Improvements` - A generic type for Tool config added [#1516](https://github.com/codex-team/editor.js/issues/1516) - `Improvements` - Remove unused `force` option in `Caret.navigateNext()` and `Caret.navigatePrevious()` [#857](https://github.com/codex-team/editor.js/issues/857#issuecomment-770363438). - `Improvements` - Remove bundles from the repo [#1541](https://github.com/codex-team/editor.js/pull/1541). @@ -33,6 +36,7 @@ - `Refactoring` - Shortcuts module is util now. - `Fix` - Fix bubbling on BlockManagers' listener [#1433](https://github.com/codex-team/editor.js/issues/1433). + ### 2.19.1 - `Improvements` - The [Cypress](https://www.cypress.io) was integrated as the end-to-end testing framework diff --git a/package.json b/package.json index 7d52a57c..484e0b05 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "css-loader": "^3.5.3", "cssnano": "^4.1.10", "cypress": "^6.8.0", + "cypress-intellij-reporter": "^0.0.6", "eslint": "^6.8.0", "eslint-config-codex": "^1.3.3", "eslint-loader": "^4.0.2", diff --git a/src/components/block/index.ts b/src/components/block/index.ts index 62c32489..15a95b61 100644 --- a/src/components/block/index.ts +++ b/src/components/block/index.ts @@ -45,11 +45,6 @@ interface BlockConstructorOptions { */ readOnly: boolean; - /** - * Tunes for current Block - */ - tunes: ToolsCollection; - /** * Tunes data for current Block */ @@ -224,7 +219,6 @@ export default class Block { tool, api, readOnly, - tunes, tunesData, }: BlockConstructorOptions) { this.name = tool.name; @@ -241,7 +235,7 @@ export default class Block { /** * @type {BlockTune[]} */ - this.tunes = tunes; + this.tunes = tool.tunes; this.composeTunes(tunesData); diff --git a/src/components/modules/api/sanitizer.ts b/src/components/modules/api/sanitizer.ts index 01ab2be2..b0ce0ef0 100644 --- a/src/components/modules/api/sanitizer.ts +++ b/src/components/modules/api/sanitizer.ts @@ -1,6 +1,7 @@ -import { Sanitizer } from '../../../../types/api'; +import { Sanitizer as ISanitizer } from '../../../../types/api'; import { SanitizerConfig } from '../../../../types/configs'; import Module from '../../__module'; +import { clean } from '../../utils/sanitizer'; /** * @class SanitizerAPI @@ -12,7 +13,7 @@ export default class SanitizerAPI extends Module { * * @returns {Sanitizer} */ - public get methods(): Sanitizer { + public get methods(): ISanitizer { return { clean: (taintString, config): string => this.clean(taintString, config), }; @@ -27,6 +28,6 @@ export default class SanitizerAPI extends Module { * @returns {string} */ public clean(taintString: string, config: SanitizerConfig): string { - return this.Editor.Sanitizer.clean(taintString, config); + return clean(taintString, config); } } diff --git a/src/components/modules/blockManager.ts b/src/components/modules/blockManager.ts index d672d60c..5614aa83 100644 --- a/src/components/modules/blockManager.ts +++ b/src/components/modules/blockManager.ts @@ -227,13 +227,11 @@ export default class BlockManager extends Module { }: {tool: string; data?: BlockToolData; tunes?: {[name: string]: BlockTuneData}}): Block { const readOnly = this.Editor.ReadOnly.isEnabled; const tool = this.Editor.Tools.blockTools.get(name); - const tunes = this.Editor.Tools.getTunesForTool(tool); const block = new Block({ data, tool, api: this.Editor.API, readOnly, - tunes, tunesData, }); diff --git a/src/components/modules/blockSelection.ts b/src/components/modules/blockSelection.ts index 1c05d239..96ada11b 100644 --- a/src/components/modules/blockSelection.ts +++ b/src/components/modules/blockSelection.ts @@ -13,6 +13,7 @@ import Shortcuts from '../utils/shortcuts'; import SelectionUtils from '../selection'; import { SanitizerConfig } from '../../../types/configs'; +import { clean } from '../utils/sanitizer'; /** * @@ -297,7 +298,7 @@ export default class BlockSelection extends Module { /** * Make

tag that holds clean HTML */ - const cleanHTML = this.Editor.Sanitizer.clean(block.holder.innerHTML, this.sanitizerConfig); + const cleanHTML = clean(block.holder.innerHTML, this.sanitizerConfig); const fragment = $.make('p'); fragment.innerHTML = cleanHTML; diff --git a/src/components/modules/paste.ts b/src/components/modules/paste.ts index fea1bd33..a9280bd0 100644 --- a/src/components/modules/paste.ts +++ b/src/components/modules/paste.ts @@ -8,6 +8,7 @@ import { } from '../../../types'; import Block from '../block'; import { SavedData } from '../../../types/data-formats'; +import { clean, sanitizeBlocks } from '../utils/sanitizer'; import BlockTool from '../tools/block'; /** @@ -158,8 +159,7 @@ export default class Paste extends Module { * @param {boolean} isDragNDrop - true if data transfer comes from drag'n'drop events */ public async processDataTransfer(dataTransfer: DataTransfer, isDragNDrop = false): Promise { - const { Sanitizer } = this.Editor; - + const { Tools } = this.Editor; const types = dataTransfer.types; /** @@ -203,9 +203,8 @@ export default class Paste extends Module { return result; }, {}); - const customConfig = Object.assign({}, toolsTags, Sanitizer.getAllInlineToolsConfig(), { br: {} }); - - const cleanData = Sanitizer.clean(htmlData, customConfig); + const customConfig = Object.assign({}, toolsTags, Tools.getAllInlineToolsSanitizeConfig(), { br: {} }); + const cleanData = clean(htmlData, customConfig); /** If there is no HTML or HTML string is equal to plain one, process it as plain text */ if (!cleanData.trim() || cleanData.trim() === plainData || !$.isHTMLString(cleanData)) { @@ -515,7 +514,7 @@ export default class Paste extends Module { * @returns {PasteData[]} */ private processHTML(innerHTML: string): PasteData[] { - const { Tools, Sanitizer } = this.Editor; + const { Tools } = this.Editor; const wrapper = $.make('DIV'); wrapper.innerHTML = innerHTML; @@ -551,9 +550,9 @@ export default class Paste extends Module { return result; }, {}); - const customConfig = Object.assign({}, toolTags, Sanitizer.getInlineToolsConfig(tool)); + const customConfig = Object.assign({}, toolTags, tool.baseSanitizeConfig); - content.innerHTML = Sanitizer.clean(content.innerHTML, customConfig); + content.innerHTML = clean(content.innerHTML, customConfig); const event = this.composePasteEvent('tag', { data: content, @@ -640,7 +639,7 @@ export default class Paste extends Module { * @param {PasteData} dataToInsert - data of Block to insert */ private async processInlinePaste(dataToInsert: PasteData): Promise { - const { BlockManager, Caret, Sanitizer } = this.Editor; + const { BlockManager, Caret, Tools } = this.Editor; const { content } = dataToInsert; const currentBlockIsDefault = BlockManager.currentBlock && BlockManager.currentBlock.tool.isDefault; @@ -663,12 +662,12 @@ export default class Paste extends Module { /** If there is no pattern substitute - insert string as it is */ if (BlockManager.currentBlock && BlockManager.currentBlock.currentInput) { - const currentToolSanitizeConfig = Sanitizer.getInlineToolsConfig(BlockManager.currentBlock.tool); + const currentToolSanitizeConfig = BlockManager.currentBlock.tool.sanitizeConfig; document.execCommand( 'insertHTML', false, - Sanitizer.clean(content.innerHTML, currentToolSanitizeConfig) + clean(content.innerHTML, currentToolSanitizeConfig) ); } else { this.insertBlock(dataToInsert); @@ -741,8 +740,10 @@ export default class Paste extends Module { * @returns {void} */ private insertEditorJSData(blocks: Pick[]): void { - const { BlockManager, Caret, Sanitizer } = this.Editor; - const sanitizedBlocks = Sanitizer.sanitizeBlocks(blocks); + const { BlockManager, Caret, Tools } = this.Editor; + const sanitizedBlocks = sanitizeBlocks(blocks, (name) => + Tools.blockTools.get(name).sanitizeConfig + ); sanitizedBlocks.forEach(({ tool, data }, i) => { let needToReplaceCurrentBlock = false; diff --git a/src/components/modules/sanitizer.ts b/src/components/modules/sanitizer.ts deleted file mode 100644 index 0892fb98..00000000 --- a/src/components/modules/sanitizer.ts +++ /dev/null @@ -1,333 +0,0 @@ -/** - * CodeX Sanitizer - * - * @module Sanitizer - * Clears HTML from taint tags - * - * @version 2.0.0 - * - * @example - * Module can be used within two ways: - * 1) When you have an instance - * - this.Editor.Sanitizer.clean(yourTaintString); - * 2) As static method - * - EditorJS.Sanitizer.clean(yourTaintString, yourCustomConfiguration); - * - * {@link SanitizerConfig} - */ - -import Module from '../__module'; -import * as _ from '../utils'; - -/** - * @typedef {object} SanitizerConfig - * @property {object} tags - define tags restrictions - * - * @example - * - * tags : { - * p: true, - * a: { - * href: true, - * rel: "nofollow", - * target: "_blank" - * } - * } - */ - -import HTMLJanitor from 'html-janitor'; -import { BlockToolData, SanitizerConfig } from '../../../types'; -import { SavedData } from '../../../types/data-formats'; -import InlineTool from '../tools/inline'; -import BlockTool from '../tools/block'; - -/** - * - */ -export default class Sanitizer extends Module { - /** - * Memoize tools config - */ - private configCache: {[toolName: string]: SanitizerConfig} = {}; - - /** - * Cached inline tools config - */ - private inlineToolsConfigCache: SanitizerConfig | null = null; - - /** - * Sanitize Blocks - * - * Enumerate blocks and clean data - * - * @param {Array<{tool, data: BlockToolData}>} blocksData - blocks' data to sanitize - */ - public sanitizeBlocks( - blocksData: Pick[] - ): Pick[] { - return blocksData.map((block) => { - const toolConfig = this.composeToolConfig(block.tool); - - if (_.isEmpty(toolConfig)) { - return block; - } - - block.data = this.deepSanitize(block.data, toolConfig) as BlockToolData; - - return block; - }); - } - - /** - * Method recursively reduces Block's data and cleans with passed rules - * - * @param {BlockToolData|object|*} dataToSanitize - taint string or object/array that contains taint string - * @param {SanitizerConfig} rules - object with sanitizer rules - */ - public deepSanitize(dataToSanitize: object | string, rules: SanitizerConfig): object | string { - /** - * BlockData It may contain 3 types: - * - Array - * - Object - * - Primitive - */ - if (Array.isArray(dataToSanitize)) { - /** - * Array: call sanitize for each item - */ - return this.cleanArray(dataToSanitize, rules); - } else if (_.isObject(dataToSanitize)) { - /** - * Objects: just clean object deeper. - */ - return this.cleanObject(dataToSanitize, rules); - } else { - /** - * Primitives (number|string|boolean): clean this item - * - * Clean only strings - */ - if (_.isString(dataToSanitize)) { - return this.cleanOneItem(dataToSanitize, rules); - } - - return dataToSanitize; - } - } - - /** - * Cleans string from unwanted tags - * Method allows to use default config - * - * @param {string} taintString - taint string - * @param {SanitizerConfig} customConfig - allowed tags - * - * @returns {string} clean HTML - */ - public clean(taintString: string, customConfig: SanitizerConfig = {} as SanitizerConfig): string { - const sanitizerConfig = { - tags: customConfig, - }; - - /** - * API client can use custom config to manage sanitize process - */ - const sanitizerInstance = this.createHTMLJanitorInstance(sanitizerConfig); - - return sanitizerInstance.clean(taintString); - } - - /** - * Merge with inline tool config - * - * @param {string} toolName - tool name - * - * @returns {SanitizerConfig} - */ - public composeToolConfig(toolName: string): SanitizerConfig { - /** - * If cache is empty, then compose tool config and put it to the cache object - */ - if (this.configCache[toolName]) { - return this.configCache[toolName]; - } - - const tool = this.Editor.Tools.available.get(toolName); - const baseConfig = this.getInlineToolsConfig(tool as BlockTool); - - /** - * If Tools doesn't provide sanitizer config or it is empty - */ - if (!tool.sanitizeConfig || (tool.sanitizeConfig && _.isEmpty(tool.sanitizeConfig))) { - return baseConfig; - } - - const toolRules = tool.sanitizeConfig; - - const toolConfig = {} as SanitizerConfig; - - for (const fieldName in toolRules) { - if (Object.prototype.hasOwnProperty.call(toolRules, fieldName)) { - const rule = toolRules[fieldName]; - - if (_.isObject(rule)) { - toolConfig[fieldName] = Object.assign({}, baseConfig, rule); - } else { - toolConfig[fieldName] = rule; - } - } - } - this.configCache[toolName] = toolConfig; - - return toolConfig; - } - - /** - * Returns Sanitizer config - * When Tool's "inlineToolbar" value is True, get all sanitizer rules from all tools, - * otherwise get only enabled - * - * @param tool - BlockTool object - */ - public getInlineToolsConfig(tool: BlockTool): SanitizerConfig { - const { Tools } = this.Editor; - const enableInlineTools = tool.enabledInlineTools || []; - - let config = {} as SanitizerConfig; - - if (_.isBoolean(enableInlineTools) && enableInlineTools) { - /** - * getting all tools sanitizer rule - */ - config = this.getAllInlineToolsConfig(); - } else { - /** - * getting only enabled - */ - (enableInlineTools as string[]).map((inlineToolName) => { - config = Object.assign( - config, - Tools.inlineTools.get(inlineToolName).sanitizeConfig - ) as SanitizerConfig; - }); - } - - /** - * Allow linebreaks - */ - config['br'] = true; - config['wbr'] = true; - - return config; - } - - /** - * Return general config for all inline tools - */ - public getAllInlineToolsConfig(): SanitizerConfig { - const { Tools } = this.Editor; - - if (this.inlineToolsConfigCache) { - return this.inlineToolsConfigCache; - } - - const config: SanitizerConfig = {} as SanitizerConfig; - - Object.entries(Tools.inlineTools) - .forEach(([, inlineTool]: [string, InlineTool]) => { - Object.assign(config, inlineTool.sanitizeConfig); - }); - - this.inlineToolsConfigCache = config; - - return this.inlineToolsConfigCache; - } - - /** - * Clean array - * - * @param {Array} array - [1, 2, {}, []] - * @param {SanitizerConfig} ruleForItem - sanitizer config for array - */ - private cleanArray(array: (object | string)[], ruleForItem: SanitizerConfig): (object | string)[] { - return array.map((arrayItem) => this.deepSanitize(arrayItem, ruleForItem)); - } - - /** - * Clean object - * - * @param {object} object - {level: 0, text: 'adada', items: [1,2,3]}} - * @param {object} rules - { b: true } or true|false - * @returns {object} - */ - private cleanObject(object: object, rules: SanitizerConfig|{[field: string]: SanitizerConfig}): object { - const cleanData = {}; - - for (const fieldName in object) { - if (!Object.prototype.hasOwnProperty.call(object, fieldName)) { - continue; - } - - const currentIterationItem = object[fieldName]; - - /** - * Get object from config by field name - * - if it is a HTML Janitor rule, call with this rule - * - otherwise, call with parent's config - */ - const ruleForItem = this.isRule(rules[fieldName] as SanitizerConfig) ? rules[fieldName] : rules; - - cleanData[fieldName] = this.deepSanitize(currentIterationItem, ruleForItem as SanitizerConfig); - } - - return cleanData; - } - - /** - * Clean primitive value - * - * @param {string} taintString - string to clean - * @param {SanitizerConfig|boolean} rule - sanitizer rule - * - * @returns {string} - */ - private cleanOneItem(taintString: string, rule: SanitizerConfig|boolean): string { - if (_.isObject(rule)) { - return this.clean(taintString, rule); - } else if (rule === false) { - return this.clean(taintString, {} as SanitizerConfig); - } else { - return taintString; - } - } - - /** - * Check if passed item is a HTML Janitor rule: - * { a : true }, {}, false, true, function(){} — correct rules - * undefined, null, 0, 1, 2 — not a rules - * - * @param {SanitizerConfig} config - config to check - */ - private isRule(config: SanitizerConfig): boolean { - return _.isObject(config) || _.isBoolean(config) || _.isFunction(config); - } - - /** - * If developer uses editor's API, then he can customize sanitize restrictions. - * Or, sanitizing config can be defined globally in editors initialization. That config will be used everywhere - * At least, if there is no config overrides, that API uses Default configuration - * - * @see {@link https://www.npmjs.com/package/html-janitor} - * @license Apache-2.0 - * @see {@link https://github.com/guardian/html-janitor/blob/master/LICENSE} - * - * @param {SanitizerConfig} config - sanitizer extension - */ - private createHTMLJanitorInstance(config: {tags: SanitizerConfig}): HTMLJanitor|null { - if (config) { - return new HTMLJanitor(config); - } - - return null; - } -} diff --git a/src/components/modules/saver.ts b/src/components/modules/saver.ts index d32ff1d3..1263562b 100644 --- a/src/components/modules/saver.ts +++ b/src/components/modules/saver.ts @@ -10,6 +10,7 @@ import { OutputData } from '../../../types'; import { ValidatedData } from '../../../types/data-formats'; import Block from '../block'; import * as _ from '../utils'; +import { sanitizeBlocks } from '../utils/sanitizer'; declare const VERSION: string; @@ -27,7 +28,7 @@ export default class Saver extends Module { * @returns {OutputData} */ public async save(): Promise { - const { BlockManager, Sanitizer, ModificationsObserver } = this.Editor; + const { BlockManager, ModificationsObserver, Tools } = this.Editor; const blocks = BlockManager.blocks, chainData = []; @@ -42,7 +43,9 @@ export default class Saver extends Module { }); const extractedData = await Promise.all(chainData); - const sanitizedData = await Sanitizer.sanitizeBlocks(extractedData); + const sanitizedData = await sanitizeBlocks(extractedData, (name) => { + return Tools.blockTools.get(name).sanitizeConfig; + }); return this.makeOutput(sanitizedData); } finally { diff --git a/src/components/modules/toolbar/conversion.ts b/src/components/modules/toolbar/conversion.ts index f49d86c1..44743fa5 100644 --- a/src/components/modules/toolbar/conversion.ts +++ b/src/components/modules/toolbar/conversion.ts @@ -6,6 +6,7 @@ import { SavedData } from '../../../../types/data-formats'; import Flipper from '../../flipper'; import I18n from '../../i18n'; import { I18nInternalNS } from '../../i18n/namespace-internal'; +import { clean } from '../../utils/sanitizer'; /** * HTML Elements used for ConversionToolbar @@ -226,7 +227,7 @@ export default class ConversionToolbar extends Module { /** * Clean exported data with replacing sanitizer config */ - const cleaned: string = this.Editor.Sanitizer.clean( + const cleaned: string = clean( exportData, replacingTool.sanitizeConfig ); diff --git a/src/components/modules/toolbar/inline.ts b/src/components/modules/toolbar/inline.ts index 8bf0731e..a100f15c 100644 --- a/src/components/modules/toolbar/inline.ts +++ b/src/components/modules/toolbar/inline.ts @@ -300,70 +300,6 @@ export default class InlineToolbar extends Module { this.removeAllNodes(); } - /** - * Returns inline toolbar settings for a particular tool - * - * @param tool - BlockTool object - * @returns {string[] | boolean} array of ordered tool names or false - */ - private getInlineToolbarSettings(tool: BlockTool): string[] | boolean { - /** - * InlineToolbar property of a particular tool - */ - const settingsForTool = tool.enabledInlineTools; - - /** - * Whether to enable IT for a particular tool is the decision of the editor user. - * He can enable it by the inlineToolbar settings for this tool. To enable, he should pass true or strings[] - */ - const enabledForTool = settingsForTool === true || Array.isArray(settingsForTool); - - /** - * Disabled by user - */ - if (!enabledForTool) { - return false; - } - - /** - * 1st priority. - * - * If user pass the list of inline tools for the particular tool, return it. - */ - if (Array.isArray(settingsForTool)) { - return settingsForTool; - } - - /** - * 2nd priority. - * - * If user pass just 'true' for tool, get common inlineToolbar settings - * - if common settings is an array, use it - * - if common settings is 'true' or not specified, get default order - */ - - /** - * Common inlineToolbar settings got from the root of EditorConfig - */ - const commonInlineToolbarSettings = this.config.inlineToolbar; - - /** - * If common settings is an array, use it - */ - if (Array.isArray(commonInlineToolbarSettings)) { - return commonInlineToolbarSettings; - } - - /** - * If common settings is 'true' or not specified (will be set as true at core.ts), get the default order - */ - if (commonInlineToolbarSettings === true) { - return Array.from(this.Editor.Tools.inlineTools.keys()); - } - - return false; - } - /** * Making DOM */ @@ -472,12 +408,7 @@ export default class InlineToolbar extends Module { return false; } - /** - * getInlineToolbarSettings could return an string[] (order of tools) or false (Inline Toolbar disabled). - */ - const inlineToolbarSettings = this.getInlineToolbarSettings(currentBlock.tool); - - return inlineToolbarSettings !== false; + return currentBlock.tool.inlineTools.size !== 0; } /** @@ -583,18 +514,7 @@ export default class InlineToolbar extends Module { this.nodes.actions.innerHTML = ''; this.toolsInstances = new Map(); - /** - * Filter buttons if Block Tool pass config like inlineToolbar=['link'] - * Else filter them according to the default inlineToolbar property. - * - * For this moment, inlineToolbarOrder could not be 'false' - * because this method will be called only if the Inline Toolbar is enabled - */ - const inlineToolbarOrder = this.getInlineToolbarSettings(currentBlock.tool) as string[]; - - inlineToolbarOrder.forEach((toolName) => { - const tool = this.Editor.Tools.inlineTools.get(toolName); - + Array.from(currentBlock.tool.inlineTools.values()).forEach(tool => { this.addTool(tool); }); diff --git a/src/components/modules/tools.ts b/src/components/modules/tools.ts index 856b5a22..c1aa30ca 100644 --- a/src/components/modules/tools.ts +++ b/src/components/modules/tools.ts @@ -3,6 +3,7 @@ import Module from '../__module'; import * as _ from '../utils'; import { EditorConfig, + SanitizerConfig, Tool, ToolConstructable, ToolSettings @@ -124,7 +125,7 @@ export default class Tools extends Module { * * @returns {Promise} */ - public prepare(): Promise { + public async prepare(): Promise { this.validateTools(); /** @@ -155,46 +156,28 @@ export default class Tools extends Module { /** * to see how it works {@link '../utils.ts#sequence'} */ - return _.sequence(sequenceData, (data: { toolName: string }) => { + await _.sequence(sequenceData, (data: { toolName: string }) => { this.toolPrepareMethodSuccess(data); }, (data: { toolName: string }) => { this.toolPrepareMethodFallback(data); }); + + this.prepareBlockTools(); } /** - * Returns Block Tunes for passed Tool - * - * @param tool - Tool object + * Return general Sanitizer config for all inline tools */ - public getTunesForTool(tool: BlockTool): ToolsCollection { - const names = tool.enabledBlockTunes; + @_.cacheable + public getAllInlineToolsSanitizeConfig(): SanitizerConfig { + const config: SanitizerConfig = {} as SanitizerConfig; - if (names === false) { - return new ToolsCollection(); - } + Array.from(this.inlineTools.values()) + .forEach(inlineTool => { + Object.assign(config, inlineTool.sanitizeConfig); + }); - if (Array.isArray(names)) { - return new ToolsCollection( - Array - .from(this.blockTunes.entries()) - .filter(([, tune]) => names.includes(tune.name)) - .concat([ ...this.blockTunes.internalTools.entries() ]) - ); - } - - const defaultTuneNames = this.config.tunes; - - if (Array.isArray(defaultTuneNames)) { - return new ToolsCollection( - Array - .from(this.blockTunes.entries()) - .filter(([, tune]) => defaultTuneNames.includes(tune.name)) - .concat([ ...this.blockTunes.internalTools.entries() ]) - ); - } - - return this.blockTunes.internalTools; + return config; } /** @@ -320,6 +303,86 @@ export default class Tools extends Module { return toolPreparationList; } + /** + * Assign enabled Inline Tools and Block Tunes for Block Tool + */ + private prepareBlockTools(): void { + Array.from(this.blockTools.values()).forEach(tool => { + this.assignInlineToolsToBlockTool(tool); + this.assignBlockTunesToBlockTool(tool); + }); + } + + /** + * Assign enabled Inline Tools for Block Tool + * + * @param tool - Block Tool + */ + private assignInlineToolsToBlockTool(tool: BlockTool): void { + /** + * If common inlineToolbar property is false no Inline Tools should be assigned + */ + if (this.config.inlineToolbar === false) { + return; + } + + /** + * If user pass just 'true' for tool, get common inlineToolbar settings + * - if common settings is an array, use it + * - if common settings is 'true' or not specified, get default order + */ + if (tool.enabledInlineTools === true) { + tool.inlineTools = new ToolsCollection( + Array.isArray(this.config.inlineToolbar) + ? this.config.inlineToolbar.map(name => [name, this.inlineTools.get(name)]) + /** + * If common settings is 'true' or not specified (will be set as true at core.ts), get the default order + */ + : Array.from(this.inlineTools.entries()) + ); + + return; + } + + /** + * If user pass the list of inline tools for the particular tool, return it. + */ + if (Array.isArray(tool.enabledInlineTools)) { + tool.inlineTools = new ToolsCollection( + tool.enabledInlineTools.map(name => [name, this.inlineTools.get(name)]) + ); + } + } + + /** + * Assign enabled Block Tunes for Block Tool + * + * @param tool — Block Tool + */ + private assignBlockTunesToBlockTool(tool: BlockTool): void { + if (tool.enabledInlineTools === false) { + return; + } + + if (Array.isArray(tool.enabledBlockTunes)) { + tool.tunes = new ToolsCollection( + tool.enabledBlockTunes.map(name => [name, this.blockTunes.get(name)]) + ); + + return; + } + + if (Array.isArray(this.config.tunes)) { + tool.tunes = new ToolsCollection( + this.config.tunes.map(name => [name, this.blockTunes.get(name)]) + ); + + return; + } + + tool.tunes = this.blockTunes.internalTools; + } + /** * Validate Tools configuration objects and throw Error for user if it is invalid */ diff --git a/src/components/tools/base.ts b/src/components/tools/base.ts index 862a2afb..1f36ff37 100644 --- a/src/components/tools/base.ts +++ b/src/components/tools/base.ts @@ -249,7 +249,7 @@ export default abstract class BaseTool { * Returns Tool's sanitizer configuration */ public get sanitizeConfig(): SanitizerConfig { - return this.constructable[CommonInternalSettings.SanitizeConfig]; + return this.constructable[CommonInternalSettings.SanitizeConfig] || {}; } /** diff --git a/src/components/tools/block.ts b/src/components/tools/block.ts index 610e6748..7da7315c 100644 --- a/src/components/tools/block.ts +++ b/src/components/tools/block.ts @@ -5,10 +5,13 @@ import { BlockToolConstructable, BlockToolData, ConversionConfig, - PasteConfig, + PasteConfig, SanitizerConfig, ToolboxConfig } from '../../../types'; import * as _ from '../utils'; +import InlineTool from './inline'; +import BlockTune from './tune'; +import ToolsCollection from './collection'; /** * Class to work with Block tools constructables @@ -19,6 +22,16 @@ export default class BlockTool extends BaseTool { */ public type = ToolType.Block; + /** + * InlineTool collection for current Block Tool + */ + public inlineTools: ToolsCollection = new ToolsCollection(); + + /** + * BlockTune collection for current Block Tool + */ + public tunes: ToolsCollection = new ToolsCollection(); + /** * Tool's constructable blueprint */ @@ -85,7 +98,7 @@ export default class BlockTool extends BaseTool { * Returns enabled inline tools for Tool */ public get enabledInlineTools(): boolean | string[] { - return this.config[UserSettings.EnabledInlineTools]; + return this.config[UserSettings.EnabledInlineTools] || false; } /** @@ -101,4 +114,52 @@ export default class BlockTool extends BaseTool { public get pasteConfig(): PasteConfig { return this.constructable[InternalBlockToolSettings.PasteConfig] || {}; } + + /** + * Returns sanitize configuration for Block Tool including conifgs from Inline Tools + */ + @_.cacheable + public get sanitizeConfig(): SanitizerConfig { + const toolRules = super.sanitizeConfig; + const baseConfig = this.baseSanitizeConfig; + + if (_.isEmpty(toolRules)) { + return baseConfig; + } + + const toolConfig = {} as SanitizerConfig; + + for (const fieldName in toolRules) { + if (Object.prototype.hasOwnProperty.call(toolRules, fieldName)) { + const rule = toolRules[fieldName]; + + /** + * If rule is object, merge it with Inline Tools configuration + * + * Otherwise pass as it is + */ + if (_.isObject(rule)) { + toolConfig[fieldName] = Object.assign({}, baseConfig, rule); + } else { + toolConfig[fieldName] = rule; + } + } + } + + return toolConfig; + } + + /** + * Returns sanitizer configuration composed from sanitize config of Inline Tools enabled for Tool + */ + @_.cacheable + public get baseSanitizeConfig(): SanitizerConfig { + const baseConfig = {}; + + Array + .from(this.inlineTools.values()) + .forEach(tool => Object.assign(baseConfig, tool.sanitizeConfig)); + + return baseConfig; + } } diff --git a/src/components/utils.ts b/src/components/utils.ts index 4634e590..8e17379d 100644 --- a/src/components/utils.ts +++ b/src/components/utils.ts @@ -642,3 +642,49 @@ export function deprecationAssert(condition: boolean, oldProperty: string, newPr logLabeled(message, 'warn'); } } + +/** + * Decorator which provides ability to cache method or accessor result + * + * @param target - target instance or constructor function + * @param propertyKey - method or accessor name + * @param descriptor - property descriptor + */ +export function cacheable( + target: Target, + propertyKey: string, + descriptor: PropertyDescriptor +): PropertyDescriptor { + const propertyToOverride = descriptor.value ? 'value' : 'get'; + const originalMethod = descriptor[propertyToOverride]; + const cacheKey = `#${propertyKey}Cache`; + + /** + * Override get or value descriptor property to cache return value + */ + descriptor[propertyToOverride] = function (...args: Arguments): Value { + /** + * If there is no cache, create it + */ + if (this[cacheKey] === undefined) { + this[cacheKey] = originalMethod.apply(this, ...args); + } + + return this[cacheKey]; + }; + + /** + * If get accessor has been overridden, we need to override set accessor to clear cache + */ + if (propertyToOverride === 'get' && descriptor.set) { + const originalSet = descriptor.set; + + descriptor.set = function (value: any): void { + delete target[cacheKey]; + + originalSet.apply(this, value); + }; + } + + return descriptor; +}; diff --git a/src/components/utils/sanitizer.ts b/src/components/utils/sanitizer.ts new file mode 100644 index 00000000..1eb42b58 --- /dev/null +++ b/src/components/utils/sanitizer.ts @@ -0,0 +1,191 @@ +/** + * CodeX Sanitizer + * + * Clears HTML from taint tags + * + * @version 2.0.0 + * + * @example + * + * clean(yourTaintString, yourConfig); + * + * {@link SanitizerConfig} + */ + +import * as _ from '../utils'; + +/** + * @typedef {object} SanitizerConfig + * @property {object} tags - define tags restrictions + * + * @example + * + * tags : { + * p: true, + * a: { + * href: true, + * rel: "nofollow", + * target: "_blank" + * } + * } + */ + +import HTMLJanitor from 'html-janitor'; +import { BlockToolData, SanitizerConfig } from '../../../types'; +import { SavedData } from '../../../types/data-formats'; + +/** + * Sanitize Blocks + * + * Enumerate blocks and clean data + * + * @param blocksData - blocks' data to sanitize + * @param sanitizeConfig — sanitize config to use or function to get config for Tool + */ +export function sanitizeBlocks( + blocksData: Array>, + sanitizeConfig: SanitizerConfig | ((toolName: string) => SanitizerConfig) +): Array> { + return blocksData.map((block) => { + const toolConfig = _.isFunction(sanitizeConfig) ? sanitizeConfig(block.tool) : sanitizeConfig; + + if (_.isEmpty(toolConfig)) { + return block; + } + + block.data = deepSanitize(block.data, toolConfig) as BlockToolData; + + return block; + }); +} +/** + * Cleans string from unwanted tags + * Method allows to use default config + * + * @param {string} taintString - taint string + * @param {SanitizerConfig} customConfig - allowed tags + * + * @returns {string} clean HTML + */ +export function clean(taintString: string, customConfig: SanitizerConfig = {} as SanitizerConfig): string { + const sanitizerConfig = { + tags: customConfig, + }; + + /** + * API client can use custom config to manage sanitize process + */ + const sanitizerInstance = new HTMLJanitor(sanitizerConfig); + + return sanitizerInstance.clean(taintString); +} + +/** + * Method recursively reduces Block's data and cleans with passed rules + * + * @param {BlockToolData|object|*} dataToSanitize - taint string or object/array that contains taint string + * @param {SanitizerConfig} rules - object with sanitizer rules + */ +function deepSanitize(dataToSanitize: object | string, rules: SanitizerConfig): object | string { + /** + * BlockData It may contain 3 types: + * - Array + * - Object + * - Primitive + */ + if (Array.isArray(dataToSanitize)) { + /** + * Array: call sanitize for each item + */ + return cleanArray(dataToSanitize, rules); + } else if (_.isObject(dataToSanitize)) { + /** + * Objects: just clean object deeper. + */ + return cleanObject(dataToSanitize, rules); + } else { + /** + * Primitives (number|string|boolean): clean this item + * + * Clean only strings + */ + if (_.isString(dataToSanitize)) { + return cleanOneItem(dataToSanitize, rules); + } + + return dataToSanitize; + } +} + + +/** + * Clean array + * + * @param {Array} array - [1, 2, {}, []] + * @param {SanitizerConfig} ruleForItem - sanitizer config for array + */ +function cleanArray(array: Array, ruleForItem: SanitizerConfig): Array { + return array.map((arrayItem) => deepSanitize(arrayItem, ruleForItem)); +} + +/** + * Clean object + * + * @param {object} object - {level: 0, text: 'adada', items: [1,2,3]}} + * @param {object} rules - { b: true } or true|false + * @returns {object} + */ +function cleanObject(object: object, rules: SanitizerConfig|{[field: string]: SanitizerConfig}): object { + const cleanData = {}; + + for (const fieldName in object) { + if (!Object.prototype.hasOwnProperty.call(object, fieldName)) { + continue; + } + + const currentIterationItem = object[fieldName]; + + /** + * Get object from config by field name + * - if it is a HTML Janitor rule, call with this rule + * - otherwise, call with parent's config + */ + const ruleForItem = isRule(rules[fieldName] as SanitizerConfig) ? rules[fieldName] : rules; + + cleanData[fieldName] = deepSanitize(currentIterationItem, ruleForItem as SanitizerConfig); + } + + return cleanData; +} + +/** + * Clean primitive value + * + * @param {string} taintString - string to clean + * @param {SanitizerConfig|boolean} rule - sanitizer rule + * + * @returns {string} + */ +function cleanOneItem(taintString: string, rule: SanitizerConfig|boolean): string { + if (_.isObject(rule)) { + return clean(taintString, rule); + } else if (rule === false) { + return clean(taintString, {} as SanitizerConfig); + } else { + return taintString; + } +} + +/** + * Check if passed item is a HTML Janitor rule: + * { a : true }, {}, false, true, function(){} — correct rules + * undefined, null, 0, 1, 2 — not a rules + * + * @param {SanitizerConfig} config - config to check + */ +function isRule(config: SanitizerConfig): boolean { + return _.isObject(config) || _.isBoolean(config) || _.isFunction(config); +} + + + diff --git a/src/types-internal/editor-modules.d.ts b/src/types-internal/editor-modules.d.ts index 64751505..3449adea 100644 --- a/src/types-internal/editor-modules.d.ts +++ b/src/types-internal/editor-modules.d.ts @@ -9,7 +9,6 @@ import Notifier from '../components/modules/notifier'; import DragNDrop from '../components/modules/dragNDrop'; import ModificationsObserver from '../components/modules/modificationsObserver'; import Renderer from '../components/modules/renderer'; -import Sanitizer from '../components/modules/sanitizer'; import Tools from '../components/modules/tools'; import API from '../components/modules/api/index'; import Caret from '../components/modules/caret'; @@ -49,7 +48,6 @@ export interface EditorModules { DragNDrop: DragNDrop; ModificationsObserver: ModificationsObserver; Renderer: Renderer; - Sanitizer: Sanitizer; Tools: Tools; API: API; Caret: Caret; diff --git a/test/cypress/support/commands.ts b/test/cypress/support/commands.ts index e23e8431..7b8f8da0 100644 --- a/test/cypress/support/commands.ts +++ b/test/cypress/support/commands.ts @@ -36,3 +36,29 @@ Cypress.Commands.add('createEditor', (editorConfig: EditorConfig = {}): Chainabl }); }); }); + +/** + * Paste command to dispatch paste event + * + * @usage + * cy.get('div').paste({'text/plain': 'Text', 'text/html': 'Text'}) + * + * @param data - map with MIME type as a key and data as value + */ +Cypress.Commands.add('paste', { + prevSubject: true, +}, (subject, data: {[type: string]: string}) => { + const pasteEvent = Object.assign(new Event('paste', { + bubbles: true, + cancelable: true, + }), { + clipboardData: { + getData: (type): string => data[type], + types: Object.keys(data), + }, + }); + + subject[0].dispatchEvent(pasteEvent); + + return subject; +}); diff --git a/test/cypress/support/index.d.ts b/test/cypress/support/index.d.ts index 5a9e8631..f675962a 100644 --- a/test/cypress/support/index.d.ts +++ b/test/cypress/support/index.d.ts @@ -14,6 +14,16 @@ declare global { * @example cy.createEditor({}) */ createEditor(editorConfig: EditorConfig): Chainable + + /** + * Paste command to dispatch paste event + * + * @usage + * cy.get('div').paste({'text/plain': 'Text', 'text/html': 'Text'}) + * + * @param data - map with MIME type as a key and data as value + */ + paste(data: {[type: string]: string}): Chainable } interface ApplicationWindow { diff --git a/test/cypress/tests/modules/Tools.spec.ts b/test/cypress/tests/modules/Tools.spec.ts index 4361fb33..24a884f8 100644 --- a/test/cypress/tests/modules/Tools.spec.ts +++ b/test/cypress/tests/modules/Tools.spec.ts @@ -81,17 +81,28 @@ describe('Tools module', () => { module = constructModule({ defaultBlock: 'withoutPrepare', tools: { - withSuccessfulPrepare: class { - // eslint-disable-next-line @typescript-eslint/no-empty-function - public static prepare(): void {} - } as any, + withSuccessfulPrepare: { + class: class { + // eslint-disable-next-line @typescript-eslint/no-empty-function + public static prepare(): void {} + } as any, + inlineToolbar: ['inlineTool2'], + tunes: ['blockTune2'] + }, withFailedPrepare: class { public static prepare(): void { throw new Error(); } } as any, - withoutPrepare: class { - } as any, + withoutPrepare: { + class: class {} as any, + inlineToolbar: false, + tunes: false, + }, + blockTool: { + class: class {} as any, + inlineToolbar: true, + }, inlineTool: class { public static isInline = true @@ -104,6 +115,18 @@ describe('Tools module', () => { // eslint-disable-next-line @typescript-eslint/no-empty-function public checkState(): void {} } as any, + inlineTool2: class { + public static isInline = true + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public render(): void {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public surround(): void {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public checkState(): void {} + } as any, /** * This tool will be unavailable as it doesn't have required methods */ @@ -113,6 +136,9 @@ describe('Tools module', () => { blockTune: class { public static isTune = true; } as any, + blockTune2: class { + public static isTune = true; + } as any, unavailableBlockTune: class { public static isTune = true; @@ -121,6 +147,8 @@ describe('Tools module', () => { } } as any, }, + inlineToolbar: ['inlineTool2', 'inlineTool'], + tunes: ['blockTune2', 'blockTune'], }); await module.prepare(); @@ -175,6 +203,42 @@ describe('Tools module', () => { expect(module.blockTools.has('withFailedPrepare')).to.be.false; expect(Array.from(module.blockTools.values()).every(tool => tool.isBlock())).to.be.true; }); + + it('Block Tools should contain tunes in correct order', () => { + let tool = module.blockTools.get('blockTool'); + + expect(tool.tunes.has('blockTune')).to.be.true; + expect(tool.tunes.has('blockTune2')).to.be.true; + expect(Array.from(tool.tunes.keys())).to.be.deep.eq(['blockTune2', 'blockTune']); + + tool = module.blockTools.get('withSuccessfulPrepare'); + + expect(tool.tunes.has('blockTune')).to.be.false; + expect(tool.tunes.has('blockTune2')).to.be.true; + + tool = module.blockTools.get('withoutPrepare'); + + expect(tool.tunes.has('blockTune')).to.be.false; + expect(tool.tunes.has('blockTune2')).to.be.false; + }); + + it('Block Tools should contain inline tools in correct order', () => { + let tool = module.blockTools.get('blockTool'); + + expect(tool.inlineTools.has('inlineTool')).to.be.true; + expect(tool.inlineTools.has('inlineTool2')).to.be.true; + expect(Array.from(tool.inlineTools.keys())).to.be.deep.eq(['inlineTool2', 'inlineTool']); + + tool = module.blockTools.get('withSuccessfulPrepare'); + + expect(tool.inlineTools.has('inlineTool')).to.be.false; + expect(tool.inlineTools.has('inlineTool2')).to.be.true; + + tool = module.blockTools.get('withoutPrepare'); + + expect(tool.inlineTools.has('inlineTool')).to.be.false; + expect(tool.inlineTools.has('inlineTool2')).to.be.false; + }); }); context('.blockTunes', () => { diff --git a/test/cypress/tests/sanitisation.spec.ts b/test/cypress/tests/sanitisation.spec.ts new file mode 100644 index 00000000..00808964 --- /dev/null +++ b/test/cypress/tests/sanitisation.spec.ts @@ -0,0 +1,65 @@ +describe('Output sanitisation', () => { + beforeEach(() => { + if (this && this.editorInstance) { + this.editorInstance.destroy(); + } else { + cy.createEditor({}).as('editorInstance'); + } + }); + + context('Output should save inline formatting', () => { + it('should save initial formatting for paragraph', () => { + cy.createEditor({ + data: { + blocks: [ { + type: 'paragraph', + data: { text: 'Bold text' }, + } ], + }, + }).then(async editor => { + const output = await (editor as any).save(); + + const boldText = output.blocks[0].data.text; + + expect(boldText).to.eq('Bold text'); + }); + }); + + it('should save formatting for paragraph', () => { + cy.get('[data-cy=editorjs]') + .get('div.ce-block') + .click() + .type('This text should be bold.{selectall}'); + + cy.get('[data-cy=editorjs]') + .get('button.ce-inline-tool--bold') + .click(); + + cy.get('[data-cy=editorjs]') + .get('div.ce-block') + .click(); + + cy.get('@editorInstance').then(async editorInstance => { + const output = await (editorInstance as any).save(); + + const text = output.blocks[0].data.text; + + expect(text).to.eq('This text should be bold.'); + }); + }); + + it('should save formatting for paragraph on paste', () => { + cy.get('[data-cy=editorjs]') + .get('div.ce-block') + .paste({ 'text/html': '

Text

Bold text

' }); + + cy.get('@editorInstance').then(async editorInstance => { + const output = await (editorInstance as any).save(); + + const boldText = output.blocks[1].data.text; + + expect(boldText).to.eq('Bold text'); + }); + }); + }); +}); diff --git a/test/cypress/tests/tools/BlockTool.spec.ts b/test/cypress/tests/tools/BlockTool.spec.ts index 89853ff7..da0b33bf 100644 --- a/test/cypress/tests/tools/BlockTool.spec.ts +++ b/test/cypress/tests/tools/BlockTool.spec.ts @@ -2,8 +2,10 @@ import { BlockToolData, ToolSettings } from '../../../../types'; import { ToolType } from '../../../../src/components/tools/base'; import BlockTool from '../../../../src/components/tools/block'; +import InlineTool from '../../../../src/components/tools/inline'; +import ToolsCollection from '../../../../src/components/tools/collection'; -describe('BlockTool', () => { +describe.only('BlockTool', () => { /** * Mock for BlockTool constructor options */ @@ -11,7 +13,9 @@ describe('BlockTool', () => { name: 'blockTool', constructable: class { public static sanitize = { - rule1: 'rule1', + rule1: { + div: true, + }, } public static toolbox = { @@ -131,10 +135,87 @@ describe('BlockTool', () => { }); }); - it('.sanitizeConfig should return correct value', () => { - const tool = new BlockTool(options as any); + context('.sanitizeConfig', () => { + it('should return correct value', () => { + const tool = new BlockTool(options as any); - expect(tool.sanitizeConfig).to.be.deep.eq(options.constructable.sanitize); + expect(tool.sanitizeConfig).to.be.deep.eq(options.constructable.sanitize); + }); + + it('should return composed config if there are enabled inline tools', () => { + const tool = new BlockTool(options as any); + + const inlineTool = new InlineTool({ + name: 'inlineTool', + constructable: class { + public static sanitize = { + b: true, + } + }, + api: {}, + config: {}, + } as any); + + tool.inlineTools = new ToolsCollection([ ['inlineTool', inlineTool] ]); + + const expected = options.constructable.sanitize; + + // tslint:disable-next-line:forin + for (const key in expected) { + expected[key] = { + ...expected[key], + b: true, + }; + } + + expect(tool.sanitizeConfig).to.be.deep.eq(expected); + }); + + it('should return inline tools config if block one is not set', () => { + const tool = new BlockTool({ + ...options, + constructable: class {}, + } as any); + + const inlineTool1 = new InlineTool({ + name: 'inlineTool', + constructable: class { + public static sanitize = { + b: true, + } + }, + api: {}, + config: {}, + } as any); + + const inlineTool2 = new InlineTool({ + name: 'inlineTool', + constructable: class { + public static sanitize = { + a: true, + } + }, + api: {}, + config: {}, + } as any); + + tool.inlineTools = new ToolsCollection([ ['inlineTool', inlineTool1], ['inlineTool2', inlineTool2] ]); + + expect(tool.sanitizeConfig).to.be.deep.eq(Object.assign( + {}, + inlineTool1.sanitizeConfig, + inlineTool2.sanitizeConfig + )); + }); + + it('should return empty object by default', () => { + const tool = new BlockTool({ + ...options, + constructable: class {}, + } as any); + + expect(tool.sanitizeConfig).to.be.deep.eq({}); + }); }); it('.isBlock() should return true', () => { @@ -179,10 +260,24 @@ describe('BlockTool', () => { expect(tool.pasteConfig).to.be.deep.eq(options.constructable.pasteConfig); }); - it('.enabledInlineTools should return correct value', () => { - const tool = new BlockTool(options as any); + context('.enabledInlineTools', () => { + it('should return correct value', () => { + const tool = new BlockTool(options as any); - expect(tool.enabledInlineTools).to.be.deep.eq(options.config.inlineToolbar); + expect(tool.enabledInlineTools).to.be.deep.eq(options.config.inlineToolbar); + }); + + it('should return false by default', () => { + const tool = new BlockTool({ + ...options, + config: { + ...options.config, + inlineToolbar: undefined, + }, + } as any); + + expect(tool.enabledInlineTools).to.be.false; + }); }); it('.enabledBlockTunes should return correct value', () => { diff --git a/test/cypress/tsconfig.json b/test/cypress/tsconfig.json index 1a73982a..a996c048 100644 --- a/test/cypress/tsconfig.json +++ b/test/cypress/tsconfig.json @@ -4,7 +4,8 @@ "lib": ["dom", "es2017", "es2018"], "moduleResolution": "node", "resolveJsonModule": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true }, "include": [ "../../**/*.ts" diff --git a/tsconfig.json b/tsconfig.json index fd0aa5e1..c95c063b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "resolveJsonModule": true, // allows to omit export default in .json files - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true } } diff --git a/yarn.lock b/yarn.lock index efbfc224..7fb92268 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1700,6 +1700,11 @@ semver "^6.3.0" tsutils "^3.17.1" +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -1921,6 +1926,11 @@ alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -1987,6 +1997,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + append-transform@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" @@ -2014,6 +2032,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -2229,6 +2252,11 @@ binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -2280,7 +2308,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" dependencies: @@ -2317,6 +2345,11 @@ browser-resolve@^2.0.0: dependencies: resolve "^1.17.0" +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -2652,6 +2685,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -2755,6 +2793,21 @@ cheerio@^0.19.0: htmlparser2 "~3.8.1" lodash "^3.2.0" +chokidar@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chokidar@^2.0.4, chokidar@^2.1.1, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -2867,6 +2920,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone-regexp@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" @@ -3346,6 +3408,13 @@ cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" +cypress-intellij-reporter@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/cypress-intellij-reporter/-/cypress-intellij-reporter-0.0.6.tgz#5c396b6fe0a6fcef3b380ec6e62b9c229d62781c" + integrity sha512-KDxeWKKAAGekhg1xmGToSsHDWgogM1hUYakAL4yjKQr9gSI2iyRxcrKlq1/jG4omCbUEY+AZGiiwyKOscY9+Gg== + dependencies: + mocha latest + cypress@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.8.0.tgz#8338f39212a8f71e91ff8c017a1b6e22d823d8c1" @@ -3458,6 +3527,11 @@ decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -3538,6 +3612,11 @@ detective@^5.2.0: defined "^1.0.0" minimist "^1.1.1" +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -3784,6 +3863,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -4282,6 +4366,14 @@ find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -4318,6 +4410,11 @@ flat-cache@^2.0.1: rimraf "2.6.3" write "1.0.3" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" @@ -4431,6 +4528,11 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4453,7 +4555,7 @@ get-assigned-identifiers@^1.2.0: resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -4510,7 +4612,14 @@ glob-parent@^5.0.0, glob-parent@^5.1.0: dependencies: is-glob "^4.0.1" -glob@^7.1.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob-parent@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.6, glob@^7.1.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" dependencies: @@ -4607,6 +4716,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -4699,6 +4813,11 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + hex-color-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" @@ -5007,6 +5126,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.0, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -5119,7 +5245,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" dependencies: @@ -5167,7 +5293,7 @@ is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" -is-plain-obj@^2.0.0: +is-plain-obj@^2.0.0, is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -5352,6 +5478,13 @@ js-yaml@3.14.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" + integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== + dependencies: + argparse "^2.0.1" + js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" @@ -5635,6 +5768,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -5698,6 +5838,13 @@ lodash@^4.17.20: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@4.0.0, log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -5717,13 +5864,6 @@ log-symbols@^3.0.0: dependencies: chalk "^2.4.2" -log-symbols@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" - log-update@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" @@ -5935,7 +6075,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -6014,6 +6154,37 @@ mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mocha@latest: + version "8.3.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.2.tgz#53406f195fa86fbdebe71f8b1c6fb23221d69fcc" + integrity sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.1" + debug "4.3.1" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "4.0.0" + log-symbols "4.0.0" + minimatch "3.0.4" + ms "2.1.3" + nanoid "3.1.20" + serialize-javascript "5.0.1" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.1.0" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + module-deps@^6.0.0, module-deps@^6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.3.tgz#15490bc02af4b56cf62299c7c17cba32d71a96ee" @@ -6059,6 +6230,11 @@ ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -6067,6 +6243,11 @@ nan@^2.12.1: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" +nanoid@3.1.20: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6158,7 +6339,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -6388,6 +6569,13 @@ p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.3.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -6406,6 +6594,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -6580,7 +6775,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.5, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" @@ -7383,7 +7578,7 @@ ramda@~0.27.1: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" dependencies: @@ -7485,6 +7680,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -7852,6 +8054,13 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" +serialize-javascript@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" @@ -8172,7 +8381,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -8302,6 +8511,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" @@ -8390,6 +8604,13 @@ supports-color@6.1.0, supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -9057,18 +9278,25 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" +which@2.0.2, which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: isexe "^2.0.0" -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: - isexe "^2.0.0" + string-width "^1.0.2 || 2" word-wrap@~1.2.3: version "1.2.3" @@ -9080,6 +9308,11 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" +workerpool@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" + integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== + wrap-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" @@ -9105,6 +9338,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -9132,6 +9374,11 @@ y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" +y18n@^5.0.5: + version "5.0.6" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.6.tgz#8236b05cfc5af6a409f41326a4847c68989bb04f" + integrity sha512-PlVX4Y0lDTN6E2V4ES2tEdyvXkeKzxa8c/vo0pxPr/TqbztddTP0yn7zZylIyiAuxerqj0Q5GhpJ1YJCP8LaZQ== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -9146,6 +9393,11 @@ yaml@^1.7.2: dependencies: "@babel/runtime" "^7.9.2" +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + yargs-parser@^13.1.0: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -9160,6 +9412,21 @@ yargs-parser@^18.1.1, yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.2.2: + version "20.2.7" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" + integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + yargs@13.2.4: version "13.2.4" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" @@ -9176,6 +9443,19 @@ yargs@13.2.4: y18n "^4.0.0" yargs-parser "^13.1.0" +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^15.0.2: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -9200,3 +9480,8 @@ yauzl@^2.10.0: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==